From 78ddc567f9fb371c16c81ba271ac880f96791e7d Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Fri, 5 Jun 2015 12:41:23 +0300 Subject: [PATCH 001/133] Python generator: moved base class handling to the generate step --- modules/python/src2/gen2.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index 1648e53abe..20c5c812cc 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -757,19 +757,6 @@ class PythonWrapperGenerator(object): sys.exit(-1) self.classes[classinfo.name] = classinfo - if classinfo.base: - chunks = classinfo.base.split('_') - base = '_'.join(chunks) - while base not in self.classes and len(chunks)>1: - del chunks[-2] - base = '_'.join(chunks) - if base not in self.classes: - print("Generator error: unable to resolve base %s for %s" - % (classinfo.base, classinfo.name)) - sys.exit(-1) - classinfo.base = base - classinfo.isalgorithm |= self.classes[base].isalgorithm - def split_decl_name(self, name): chunks = name.split('.') namespace = chunks[:-1] @@ -881,6 +868,22 @@ class PythonWrapperGenerator(object): # function self.add_func(decl) + # step 1.5 check if all base classes exist + for name, classinfo in self.classes.items(): + if classinfo.base: + chunks = classinfo.base.split('_') + base = '_'.join(chunks) + while base not in self.classes and len(chunks)>1: + del chunks[-2] + base = '_'.join(chunks) + if base not in self.classes: + print("Generator error: unable to resolve base %s for %s" + % (classinfo.base, classinfo.name)) + sys.exit(-1) + classinfo.base = base + classinfo.isalgorithm |= self.classes[base].isalgorithm + self.classes[name] = classinfo + # step 2: generate code for the classes and their methods classlist = list(self.classes.items()) classlist.sort() From 8184e57dd6490107211b37492a05d574bcb3d985 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 5 Jun 2015 16:24:38 +0300 Subject: [PATCH 002/133] fix tests build (win,shared,world) --- cmake/OpenCVModule.cmake | 8 ++------ modules/core/CMakeLists.txt | 6 ++++-- modules/hal/CMakeLists.txt | 1 - modules/world/CMakeLists.txt | 6 ++++-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index a1a1b90202..7b597909f1 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -176,15 +176,11 @@ macro(ocv_add_module _name) endif() endif() - # add HAL as dependency - if(NOT "${the_module}" STREQUAL "opencv_hal") - ocv_add_dependencies(${the_module} opencv_hal) - endif() - # add self to the world dependencies if((NOT DEFINED OPENCV_MODULE_IS_PART_OF_WORLD AND NOT OPENCV_MODULE_${the_module}_CLASS STREQUAL "BINDINGS" - AND NOT OPENCV_PROCESSING_EXTRA_MODULES) + AND NOT OPENCV_PROCESSING_EXTRA_MODULES + AND (NOT BUILD_SHARED_LIBS OR NOT "x${OPENCV_MODULE_TYPE}" STREQUAL "xSTATIC")) OR OPENCV_MODULE_IS_PART_OF_WORLD ) set(OPENCV_MODULE_${the_module}_IS_PART_OF_WORLD ON CACHE INTERNAL "") diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt index c45760c654..dbedc5b065 100644 --- a/modules/core/CMakeLists.txt +++ b/modules/core/CMakeLists.txt @@ -1,7 +1,9 @@ set(the_description "The Core Functionality") -ocv_add_module(core PRIVATE_REQUIRED ${ZLIB_LIBRARIES} "${OPENCL_LIBRARIES}" +ocv_add_module(core + opencv_hal + PRIVATE_REQUIRED ${ZLIB_LIBRARIES} "${OPENCL_LIBRARIES}" OPTIONAL opencv_cudev - WRAP java python) + WRAP java python) set(extra_libs "") diff --git a/modules/hal/CMakeLists.txt b/modules/hal/CMakeLists.txt index b5b2abb81e..b04e96b9e7 100644 --- a/modules/hal/CMakeLists.txt +++ b/modules/hal/CMakeLists.txt @@ -1,7 +1,6 @@ set(the_description "The Hardware Acceleration Layer (HAL) module") set(OPENCV_MODULE_TYPE STATIC) -# set(OPENCV_MODULE_IS_PART_OF_WORLD FALSE) if(UNIX) if(CMAKE_COMPILER_IS_GNUCXX OR CV_ICC) diff --git a/modules/world/CMakeLists.txt b/modules/world/CMakeLists.txt index 8a4170a79b..6377853e2f 100644 --- a/modules/world/CMakeLists.txt +++ b/modules/world/CMakeLists.txt @@ -35,8 +35,10 @@ set(headers_list "HEADERS") set(sources_list "SOURCES") set(link_deps "") foreach(m ${OPENCV_MODULE_${the_module}_DEPS}) - set(headers_list "${headers_list};${OPENCV_MODULE_${m}_HEADERS}") - set(sources_list "${sources_list};${OPENCV_MODULE_${m}_SOURCES}") + if(OPENCV_MODULE_${m}_IS_PART_OF_WORLD) + set(headers_list "${headers_list};${OPENCV_MODULE_${m}_HEADERS}") + set(sources_list "${sources_list};${OPENCV_MODULE_${m}_SOURCES}") + endif() set(link_deps "${link_deps};${OPENCV_MODULE_${m}_LINK_DEPS}") endforeach() From 0d821f04fd7520aeb951d84a6817064dd2348ef9 Mon Sep 17 00:00:00 2001 From: "alexander barakin (aka sash-kan)" Date: Sat, 6 Jun 2015 14:41:00 +0300 Subject: [PATCH 003/133] fix icvCloseCAM_V4L wrong order of code blocks this patch fixes bug http://code.opencv.org/issues/2822 . based on disussion (in russian): http://ru.stackoverflow.com/a/428398/178576 Signed-off-by: alexander barakin (aka sash-kan) --- modules/videoio/src/cap_v4l.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/videoio/src/cap_v4l.cpp b/modules/videoio/src/cap_v4l.cpp index 791f0a2347..a879de7d58 100644 --- a/modules/videoio/src/cap_v4l.cpp +++ b/modules/videoio/src/cap_v4l.cpp @@ -2824,21 +2824,8 @@ static void icvCloseCAM_V4L( CvCaptureCAM_V4L* capture ){ { #ifdef HAVE_CAMV4L2 - if (V4L2_SUPPORT == 0) + if (V4L2_SUPPORT == 1) #endif /* HAVE_CAMV4L2 */ -#ifdef HAVE_CAMV4L - { - - if (capture->mmaps) - free(capture->mmaps); - if (capture->memoryMap) - munmap(capture->memoryMap, capture->memoryBuffer.size); - - } -#endif /* HAVE_CAMV4L */ -#if defined(HAVE_CAMV4L) && defined(HAVE_CAMV4L2) - else -#endif /* HAVE_CAMV4L && HAVE_CAMV4L2 */ #ifdef HAVE_CAMV4L2 { capture->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -2860,6 +2847,19 @@ static void icvCloseCAM_V4L( CvCaptureCAM_V4L* capture ){ } } #endif /* HAVE_CAMV4L2 */ +#if defined(HAVE_CAMV4L) && defined(HAVE_CAMV4L2) + else +#endif /* HAVE_CAMV4L && HAVE_CAMV4L2 */ +#ifdef HAVE_CAMV4L + { + + if (capture->mmaps) + free(capture->mmaps); + if (capture->memoryMap) + munmap(capture->memoryMap, capture->memoryBuffer.size); + + } +#endif /* HAVE_CAMV4L */ if (capture->deviceHandle != -1) close(capture->deviceHandle); From a345d16cc13f1a3c54b4f10bbecec9df46e5d1e8 Mon Sep 17 00:00:00 2001 From: jayceelock Date: Tue, 9 Jun 2015 11:35:48 +0200 Subject: [PATCH 004/133] Changed to allow for ISO page size selection, checkerboard pattern now centres itself properly in middle of the page --- doc/pattern_tools/gen_pattern.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) mode change 100755 => 100644 doc/pattern_tools/gen_pattern.py diff --git a/doc/pattern_tools/gen_pattern.py b/doc/pattern_tools/gen_pattern.py old mode 100755 new mode 100644 index fc1e74bbc3..ebeeb123fe --- a/doc/pattern_tools/gen_pattern.py +++ b/doc/pattern_tools/gen_pattern.py @@ -3,7 +3,6 @@ """gen_pattern.py Usage example: python gen_pattern.py -o out.svg -r 11 -c 8 -T circles -s 20.0 -R 5.0 -u mm -w 216 -h 279 - -o, --output - output file (default out.svg) -r, --rows - pattern rows (default 11) -c, --columns - pattern columns (default 8) @@ -13,6 +12,7 @@ python gen_pattern.py -o out.svg -r 11 -c 8 -T circles -s 20.0 -R 5.0 -u mm -w 2 -u, --units - mm, inches, px, m (default mm) -w, --page_width - page width in units (default 216) -h, --page_height - page height in units (default 279) +-a, --page_size - page size (default A4), supercedes -h -w arguments -H, --help - show help """ @@ -51,11 +51,13 @@ class PatternMaker: def makeCheckerboardPattern(self): spacing = self.square_size - for x in range(1,self.cols+1): - for y in range(1,self.rows+1): + xspacing = (self.width - self.cols * self.square_size) / 2.0 + yspacing = (self.height - self.rows * self.square_size) / 2.0 + for x in range(0,self.cols): + for y in range(0,self.rows): if x%2 == y%2: - dot = SVG("rect", x=x * spacing, y=y * spacing, width=spacing, height=spacing, stroke_width="0", fill="black") - self.g.append(dot) + square = SVG("rect", x=x * spacing + xspacing, y=y * spacing + yspacing, width=spacing, height=spacing, fill="black") + self.g.append(square) def save(self): c = canvas(self.g,width="%d%s"%(self.width,self.units),height="%d%s"%(self.height,self.units),viewBox="0 0 %d %d"%(self.width,self.height)) @@ -65,9 +67,9 @@ class PatternMaker: def main(): # parse command line options, TODO use argparse for better doc try: - opts, args = getopt.getopt(sys.argv[1:], "Ho:c:r:T:u:s:R:w:h:", ["help","output=","columns=","rows=", + opts, args = getopt.getopt(sys.argv[1:], "Ho:c:r:T:u:s:R:w:h:a:", ["help","output=","columns=","rows=", "type=","units=","square_size=","radius_rate=", - "page_width=","page_height="]) + "page_width=","page_height=", "page_size="]) except getopt.error, msg: print msg print "for help use --help" @@ -79,8 +81,11 @@ def main(): units = "mm" square_size = 20.0 radius_rate = 5.0 - page_width = 216 #8.5 inches - page_height = 279 #11 inches + page_size = "A4" + # page size dict (ISO standard, mm) for easy lookup. format - size: [width, height] + page_sizes = {"A0": [840, 1188], "A1": [594, 840], "A2": [420, 594], "A3": [297, 420], "A4": [210, 297], "A5": [148, 210]} + page_width = page_sizes[page_size.upper()][0] + page_height = page_sizes[page_size.upper()][1] # process options for o, a in opts: if o in ("-H", "--help"): @@ -104,6 +109,11 @@ def main(): page_width = float(a) elif o in ("-h", "--page_height"): page_height = float(a) + elif o in ("-a", "--page_size"): + units = "mm" + page_size = a.upper() + page_width = page_sizes[page_size][0] + page_height = page_sizes[page_size][1] pm = PatternMaker(columns,rows,output,units,square_size,radius_rate,page_width,page_height) #dict for easy lookup of pattern type mp = {"circles":pm.makeCirclesPattern,"acircles":pm.makeACirclesPattern,"checkerboard":pm.makeCheckerboardPattern} From 1f983ec39c97298b0c8ce409a1cc229ecf14e55c Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Tue, 9 Jun 2015 13:59:48 +0300 Subject: [PATCH 005/133] Fixed compilation of pthread-based parallel_for with gcc 4.4.3 --- modules/core/src/parallel.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/core/src/parallel.cpp b/modules/core/src/parallel.cpp index b1e7567818..0b593ee070 100644 --- a/modules/core/src/parallel.cpp +++ b/modules/core/src/parallel.cpp @@ -132,8 +132,14 @@ namespace cv { ParallelLoopBody::~ParallelLoopBody() {} +#if defined HAVE_PTHREADS && HAVE_PTHREADS + void parallel_for_pthreads(const cv::Range& range, const cv::ParallelLoopBody& body, double nstripes); + size_t parallel_pthreads_get_threads_num(); + void parallel_pthreads_set_threads_num(int num); +#endif } + namespace { #ifdef CV_PARALLEL_FRAMEWORK @@ -301,7 +307,7 @@ void cv::parallel_for_(const cv::Range& range, const cv::ParallelLoopBody& body, } #elif defined HAVE_PTHREADS - void parallel_for_pthreads(const Range& range, const ParallelLoopBody& body, double nstripes); + parallel_for_pthreads(range, body, nstripes); #else @@ -361,8 +367,6 @@ int cv::getNumThreads(void) #elif defined HAVE_PTHREADS - size_t parallel_pthreads_get_threads_num(); - return parallel_pthreads_get_threads_num(); #else @@ -424,8 +428,6 @@ void cv::setNumThreads( int threads ) #elif defined HAVE_PTHREADS - void parallel_pthreads_set_threads_num(int num); - parallel_pthreads_set_threads_num(threads); #endif From 959d5752926d1183f933c2583308b6d63bbbee41 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 8 Jun 2015 12:58:02 +0300 Subject: [PATCH 006/133] videoio: update ffmpeg backend - fix compilation for old libraries - update codec/tag selection logic - add documentation note about MP4 container tags --- modules/videoio/include/opencv2/videoio.hpp | 4 +++- modules/videoio/src/cap_ffmpeg_impl.hpp | 25 ++++++++++----------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/modules/videoio/include/opencv2/videoio.hpp b/modules/videoio/include/opencv2/videoio.hpp index 2547e4c511..7be181275f 100644 --- a/modules/videoio/include/opencv2/videoio.hpp +++ b/modules/videoio/include/opencv2/videoio.hpp @@ -564,7 +564,9 @@ public: @param fourcc 4-character code of codec used to compress the frames. For example, VideoWriter::fourcc('P','I','M','1') is a MPEG-1 codec, VideoWriter::fourcc('M','J','P','G') is a motion-jpeg codec etc. List of codes can be obtained at [Video Codecs by - FOURCC](http://www.fourcc.org/codecs.php) page. + FOURCC](http://www.fourcc.org/codecs.php) page. FFMPEG backend with MP4 container natively uses + other values as fourcc code: see [ObjectType](http://www.mp4ra.org/codecs.html), + so you may receive a warning message from OpenCV about fourcc code conversion. @param fps Framerate of the created video stream. @param frameSize Size of the video frames. @param isColor If it is not zero, the encoder will expect and encode color frames, otherwise it diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index 96b8b6890e..072d4cdd9d 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -1543,7 +1543,7 @@ void CvVideoWriter_FFMPEG::close() #define CV_PRINTABLE_CHAR(ch) ((ch) < 32 ? '?' : (ch)) #define CV_TAG_TO_PRINTABLE_CHAR4(tag) CV_PRINTABLE_CHAR((tag) & 255), CV_PRINTABLE_CHAR(((tag) >> 8) & 255), CV_PRINTABLE_CHAR(((tag) >> 16) & 255), CV_PRINTABLE_CHAR(((tag) >> 24) & 255) -static inline bool cv_ff_codec_tag_match(const AVCodecTag *tags, enum AVCodecID id, unsigned int tag) +static inline bool cv_ff_codec_tag_match(const AVCodecTag *tags, CV_CODEC_ID id, unsigned int tag) { while (tags->id != AV_CODEC_ID_NONE) { @@ -1553,7 +1553,7 @@ static inline bool cv_ff_codec_tag_match(const AVCodecTag *tags, enum AVCodecID } return false; } -static inline bool cv_ff_codec_tag_list_match(const AVCodecTag *const *tags, enum AVCodecID id, unsigned int tag) +static inline bool cv_ff_codec_tag_list_match(const AVCodecTag *const *tags, CV_CODEC_ID id, unsigned int tag) { int i; for (i = 0; tags && tags[i]; i++) { @@ -1611,21 +1611,24 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc, #if LIBAVCODEC_VERSION_INT<((51<<16)+(49<<8)+0) if( (codec_id = codec_get_bmp_id( fourcc )) == CV_CODEC(CODEC_ID_NONE) ) return false; -#elif LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(54, 1, 0) -// APIchnages: -// 2012-01-31 - dd6d3b0 - lavf 54.01.0 -// Add avformat_get_riff_video_tags() and avformat_get_riff_audio_tags(). +#else if( (codec_id = av_codec_get_id(fmt->codec_tag, fourcc)) == CV_CODEC(CODEC_ID_NONE) ) { const struct AVCodecTag * fallback_tags[] = { +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(54, 1, 0) +// APIchanges: +// 2012-01-31 - dd6d3b0 - lavf 54.01.0 +// Add avformat_get_riff_video_tags() and avformat_get_riff_audio_tags(). avformat_get_riff_video_tags(), -#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(55, 25, 100) +#endif +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(55, 25, 100) && defined LIBAVFORMAT_VERSION_MICRO && LIBAVFORMAT_VERSION_MICRO >= 100 // APIchanges: ffmpeg only // 2014-01-19 - 1a193c4 - lavf 55.25.100 - avformat.h // Add avformat_get_mov_video_tags() and avformat_get_mov_audio_tags(). - // TODO ffmpeg only, need to skip libav: avformat_get_mov_video_tags(), + avformat_get_mov_video_tags(), #endif - codec_bmp_tags, NULL }; + codec_bmp_tags, // fallback for avformat < 54.1 + NULL }; if( (codec_id = av_codec_get_id(fallback_tags, fourcc)) == CV_CODEC(CODEC_ID_NONE) ) { fflush(stdout); @@ -1650,10 +1653,6 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc, fourcc = supported_tag; } } -#else - const struct AVCodecTag * tags[] = { codec_bmp_tags, NULL}; - if( (codec_id = av_codec_get_id(tags, fourcc)) == CV_CODEC(CODEC_ID_NONE) ) - return false; #endif // alloc memory for context From 3f2666778aecaf8a44487fd88cf386a38f14747f Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 8 Jun 2015 13:06:29 +0300 Subject: [PATCH 007/133] videoio: fix some issues in ffmpeg error processing --- modules/videoio/src/cap_ffmpeg_impl.hpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index 072d4cdd9d..7cc5483d53 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -1739,7 +1739,7 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc, /* find the video encoder */ codec = avcodec_find_encoder(c->codec_id); if (!codec) { - fprintf(stderr, "Could not find encoder for codec id %d: %s", c->codec_id, icvFFMPEGErrStr( + fprintf(stderr, "Could not find encoder for codec id %d: %s\n", c->codec_id, icvFFMPEGErrStr( #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 2, 0) AVERROR_ENCODER_NOT_FOUND #else @@ -1763,7 +1763,7 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc, avcodec_open(c, codec) #endif ) < 0) { - fprintf(stderr, "Could not open codec '%s': %s", codec->name, icvFFMPEGErrStr(err)); + fprintf(stderr, "Could not open codec '%s': %s\n", codec->name, icvFFMPEGErrStr(err)); return false; } @@ -1967,6 +1967,13 @@ void OutputMediaStream_FFMPEG::close() AVStream* OutputMediaStream_FFMPEG::addVideoStream(AVFormatContext *oc, CV_CODEC_ID codec_id, int w, int h, int bitrate, double fps, PixelFormat pixel_format) { + AVCodec* codec = avcodec_find_encoder(codec_id); + if (!codec) + { + fprintf(stderr, "Could not find encoder for codec id %d\n", codec_id); + return NULL; + } + #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 10, 0) AVStream* st = avformat_new_stream(oc, 0); #else @@ -1998,8 +2005,6 @@ AVStream* OutputMediaStream_FFMPEG::addVideoStream(AVFormatContext *oc, CV_CODEC c->width = w; c->height = h; - AVCodec* codec = avcodec_find_encoder(c->codec_id); - // time base: this is the fundamental unit of time (in seconds) in terms // of which frame timestamps are represented. for fixed-fps content, // timebase should be 1/framerate and timestamp increments should be From 3e2515d7358adcdc0e689c93e54dd4885c92ef80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9?= Date: Tue, 9 Jun 2015 17:23:22 +0200 Subject: [PATCH 008/133] Fix bug in distanceATS_L1_8u and typos. The inner loop of the backward scan got the wrong initial "a". --- modules/imgproc/src/distransform.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/imgproc/src/distransform.cpp b/modules/imgproc/src/distransform.cpp index e6aac214c9..a6491086eb 100644 --- a/modules/imgproc/src/distransform.cpp +++ b/modules/imgproc/src/distransform.cpp @@ -438,7 +438,7 @@ static void getDistanceTransformMask( int maskType, float *metrics ) metrics[2] = 2.1969f; break; default: - CV_Error(CV_StsBadArg, "Uknown metric type"); + CV_Error(CV_StsBadArg, "Unknown metric type"); } } @@ -662,7 +662,7 @@ distanceATS_L1_8u( const Mat& src, Mat& dst ) // do right edge a = lut[dbase[width-1+dststep]]; - dbase[width-1] = (uchar)(MIN(a, dbase[width-1])); + a = dbase[width-1] = (uchar)(MIN(a, dbase[width-1])); for( x = width - 2; x >= 0; x-- ) { @@ -730,7 +730,7 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe float _mask[5] = {0}; if( maskSize != CV_DIST_MASK_3 && maskSize != CV_DIST_MASK_5 && maskSize != CV_DIST_MASK_PRECISE ) - CV_Error( CV_StsBadSize, "Mask size should be 3 or 5 or 0 (presize)" ); + CV_Error( CV_StsBadSize, "Mask size should be 3 or 5 or 0 (precise)" ); if( distType == CV_DIST_C || distType == CV_DIST_L1 ) maskSize = !need_labels ? CV_DIST_MASK_3 : CV_DIST_MASK_5; From caaf60ba008bb7b61e2a791aab31dafe5267ab33 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 9 Jun 2015 16:58:58 +0300 Subject: [PATCH 009/133] remove bindings generation for DetectionBasedTracker --- .../include/opencv2/objdetect/detection_based_tracker.hpp | 1 + modules/python/common.cmake | 1 + 2 files changed, 2 insertions(+) diff --git a/modules/objdetect/include/opencv2/objdetect/detection_based_tracker.hpp b/modules/objdetect/include/opencv2/objdetect/detection_based_tracker.hpp index 54117fdb91..1f5f1d3676 100644 --- a/modules/objdetect/include/opencv2/objdetect/detection_based_tracker.hpp +++ b/modules/objdetect/include/opencv2/objdetect/detection_based_tracker.hpp @@ -44,6 +44,7 @@ #ifndef __OPENCV_OBJDETECT_DBT_HPP__ #define __OPENCV_OBJDETECT_DBT_HPP__ +// After this condition removal update blacklist for bindings: modules/python/common.cmake #if defined(__linux__) || defined(LINUX) || defined(__APPLE__) || defined(__ANDROID__) || \ (defined(__cplusplus) && __cplusplus > 201103L) || (defined(_MSC_VER) && _MSC_VER >= 1700) diff --git a/modules/python/common.cmake b/modules/python/common.cmake index 57439809b7..2444b77a98 100644 --- a/modules/python/common.cmake +++ b/modules/python/common.cmake @@ -28,6 +28,7 @@ endforeach(m) ocv_list_filterout(opencv_hdrs ".h$") ocv_list_filterout(opencv_hdrs "cuda") ocv_list_filterout(opencv_hdrs "cudev") +ocv_list_filterout(opencv_hdrs "detection_based_tracker.hpp") # Conditional compilation set(cv2_generated_hdrs "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_include.h" From 26d9a7cd400921b4c2b2bc6ecafd30873d848084 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Wed, 10 Jun 2015 13:00:36 +0300 Subject: [PATCH 010/133] Trying to fix flann python wrapper issue --- modules/features2d/src/matchers.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/modules/features2d/src/matchers.cpp b/modules/features2d/src/matchers.cpp index 8d2f69ec83..a37ef0693f 100644 --- a/modules/features2d/src/matchers.cpp +++ b/modules/features2d/src/matchers.cpp @@ -1022,12 +1022,27 @@ FlannBasedMatcher::FlannBasedMatcher( const Ptr& _indexParam void FlannBasedMatcher::add( InputArrayOfArrays _descriptors ) { DescriptorMatcher::add( _descriptors ); - std::vector descriptors; - _descriptors.getUMatVector(descriptors); - - for( size_t i = 0; i < descriptors.size(); i++ ) + if(_descriptors.isUMatVector() || _descriptors.isUMat()) { - addedDescCount += descriptors[i].rows; + std::vector descriptors; + _descriptors.getUMatVector(descriptors); + for( size_t i = 0; i < descriptors.size(); i++ ) + { + addedDescCount += descriptors[i].rows; + } + } + else if(_descriptors.isMatVector() || _descriptors.isMat()) + { + std::vector descriptors; + _descriptors.getMatVector(descriptors); + for( size_t i = 0; i < descriptors.size(); i++ ) + { + addedDescCount += descriptors[i].rows; + } + } + else + { + CV_Assert( _descriptors.isUMat() || _descriptors.isUMatVector() || _descriptors.isMat() || _descriptors.isMatVector()); } } From 061131a5ee5f009a0123022e7549e4d92cc18047 Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Wed, 10 Jun 2015 13:18:40 +0300 Subject: [PATCH 011/133] Update imgproc.hpp --- modules/imgproc/include/opencv2/imgproc.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 5c18545bce..371fd9466b 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1785,7 +1785,7 @@ Example: : GaussianBlur( gray, gray, Size(9, 9), 2, 2 ); vector circles; HoughCircles(gray, circles, HOUGH_GRADIENT, - 2, gray->rows/4, 200, 100 ); + 2, gray.rows/4, 200, 100 ); for( size_t i = 0; i < circles.size(); i++ ) { Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); From dac715ad1f641cdd60a4b3a6718de3133f87c1df Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Thu, 11 Jun 2015 11:03:14 +0300 Subject: [PATCH 012/133] Update imgproc.hpp --- modules/imgproc/include/opencv2/imgproc.hpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 371fd9466b..d0b549b686 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1689,7 +1689,8 @@ See the line detection example below: #include using namespace cv; - + using namespace std; + int main(int argc, char** argv) { Mat src, dst, color_dst; @@ -1774,11 +1775,12 @@ Example: : #include using namespace cv; - + using namespace std; + int main(int argc, char** argv) { Mat img, gray; - if( argc != 2 && !(img=imread(argv[1], 1)).data) + if( argc != 2 || !(img=imread(argv[1], 1)).data) return -1; cvtColor(img, gray, COLOR_BGR2GRAY); // smooth it, otherwise a lot of false circles may be detected @@ -1797,6 +1799,8 @@ Example: : } namedWindow( "circles", 1 ); imshow( "circles", img ); + + waitKey(0); return 0; } @endcode From caa0058d5b95012a75029714cb2ea14c1d50fd82 Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Thu, 11 Jun 2015 11:46:42 +0300 Subject: [PATCH 013/133] Update imgproc.hpp --- modules/imgproc/include/opencv2/imgproc.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index d0b549b686..a9fa7ae21e 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1690,7 +1690,7 @@ See the line detection example below: using namespace cv; using namespace std; - + int main(int argc, char** argv) { Mat src, dst, color_dst; @@ -1776,7 +1776,7 @@ Example: : using namespace cv; using namespace std; - + int main(int argc, char** argv) { Mat img, gray; @@ -1799,7 +1799,7 @@ Example: : } namedWindow( "circles", 1 ); imshow( "circles", img ); - + waitKey(0); return 0; } From b7cfd5a7dad40cc36f729953acf540c9aca6079b Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 11 Jun 2015 15:36:29 +0300 Subject: [PATCH 014/133] ocl: added errors processing in OpenCL code generator, resolve space issue --- cmake/OpenCVModule.cmake | 2 +- cmake/cl2cpp.cmake | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index a1a1b90202..62714d92d8 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -662,7 +662,7 @@ macro(ocv_glob_module_sources) ocv_include_directories(${OPENCL_INCLUDE_DIRS}) add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${OCL_NAME}.cpp" "${CMAKE_CURRENT_BINARY_DIR}/${OCL_NAME}.hpp" - COMMAND ${CMAKE_COMMAND} -DMODULE_NAME="${name}" -DCL_DIR="${CMAKE_CURRENT_LIST_DIR}/src/opencl" -DOUTPUT="${CMAKE_CURRENT_BINARY_DIR}/${OCL_NAME}.cpp" -P "${OpenCV_SOURCE_DIR}/cmake/cl2cpp.cmake" + COMMAND ${CMAKE_COMMAND} "-DMODULE_NAME=${name}" "-DCL_DIR=${CMAKE_CURRENT_LIST_DIR}/src/opencl" "-DOUTPUT=${CMAKE_CURRENT_BINARY_DIR}/${OCL_NAME}.cpp" -P "${OpenCV_SOURCE_DIR}/cmake/cl2cpp.cmake" DEPENDS ${cl_kernels} "${OpenCV_SOURCE_DIR}/cmake/cl2cpp.cmake") ocv_source_group("Src\\opencl\\kernels" FILES ${cl_kernels}) ocv_source_group("Src\\opencl\\kernels\\autogenerated" FILES "${CMAKE_CURRENT_BINARY_DIR}/${OCL_NAME}.cpp" "${CMAKE_CURRENT_BINARY_DIR}/${OCL_NAME}.hpp") diff --git a/cmake/cl2cpp.cmake b/cmake/cl2cpp.cmake index 700f12fb5c..c0e211b900 100644 --- a/cmake/cl2cpp.cmake +++ b/cmake/cl2cpp.cmake @@ -1,6 +1,14 @@ +if (NOT EXISTS "${CL_DIR}") + message(FATAL_ERROR "Specified wrong OpenCL kernels directory: ${CL_DIR}") +endif() + file(GLOB cl_list "${CL_DIR}/*.cl" ) list(SORT cl_list) +if (NOT cl_list) + message(FATAL_ERROR "Can't find OpenCL kernels in directory: ${CL_DIR}") +endif() + string(REPLACE ".cpp" ".hpp" OUTPUT_HPP "${OUTPUT}") get_filename_component(OUTPUT_HPP_NAME "${OUTPUT_HPP}" NAME) From d647261de22179edd8a734e6a6f293c61f870e1d Mon Sep 17 00:00:00 2001 From: themightyoarfish Date: Thu, 11 Jun 2015 14:59:57 +0200 Subject: [PATCH 015/133] Fixed alpha preservation when converting 4-channel Mat to UIImage --- modules/imgcodecs/src/ios_conversions.mm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/imgcodecs/src/ios_conversions.mm b/modules/imgcodecs/src/ios_conversions.mm index 43268734c9..0b771f1cbc 100644 --- a/modules/imgcodecs/src/ios_conversions.mm +++ b/modules/imgcodecs/src/ios_conversions.mm @@ -66,6 +66,10 @@ UIImage* MatToUIImage(const cv::Mat& image) { CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); + // Preserve alpha transparency, if exists + bool alpha = cvMat.channels() == 4; + CGBitmapInfo bitMapInfo = (alpha ? kCGImageAlphaLast : kCGImageAlphaNone) | kCGBitmapByteOrderDefault; + // Creating CGImage from cv::Mat CGImageRef imageRef = CGImageCreate(image.cols, image.rows, @@ -73,8 +77,7 @@ UIImage* MatToUIImage(const cv::Mat& image) { 8 * image.elemSize(), image.step.p[0], colorSpace, - kCGImageAlphaNone| - kCGBitmapByteOrderDefault, + bitmapInfo, provider, NULL, false, From a482dcce464acbd5368fb93c6c3d52ba8401776a Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 11 Jun 2015 16:53:07 +0300 Subject: [PATCH 016/133] fix support for pthreads parallel_for --- CMakeLists.txt | 29 +++++++++++++++++++------- cmake/OpenCVFindLibsPerf.cmake | 16 ++++++++------ cmake/templates/cvconfig.h.in | 6 ++++++ modules/core/src/parallel.cpp | 13 +++++++----- modules/core/src/parallel_pthreads.cpp | 2 +- modules/core/src/precomp.hpp | 6 ------ 6 files changed, 47 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d9a17b3820..27d84703f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,7 +188,7 @@ OCV_OPTION(WITH_QUICKTIME "Use QuickTime for Video I/O insted of QTKit" OFF OCV_OPTION(WITH_TBB "Include Intel TBB support" OFF IF (NOT IOS AND NOT WINRT) ) OCV_OPTION(WITH_OPENMP "Include OpenMP support" OFF) OCV_OPTION(WITH_CSTRIPES "Include C= support" OFF IF (WIN32 AND NOT WINRT) ) -OCV_OPTION(WITH_PTHREADS_PF "Use pthreads-based parallel_for" OFF IF (NOT WIN32) ) +OCV_OPTION(WITH_PTHREADS_PF "Use pthreads-based parallel_for" ON IF (NOT WIN32) ) OCV_OPTION(WITH_TIFF "Include TIFF support" ON IF (NOT IOS) ) OCV_OPTION(WITH_UNICAP "Include Unicap support (GPL)" OFF IF (UNIX AND NOT APPLE AND NOT ANDROID) ) OCV_OPTION(WITH_V4L "Include Video 4 Linux support" ON IF (UNIX AND NOT ANDROID) ) @@ -1026,6 +1026,27 @@ if(DEFINED WITH_GPHOTO2) endif(DEFINED WITH_GPHOTO2) +# Order is similar to CV_PARALLEL_FRAMEWORK in core/src/parallel.cpp +ocv_clear_vars(CV_PARALLEL_FRAMEWORK) +if(HAVE_TBB) + set(CV_PARALLEL_FRAMEWORK "TBB (ver ${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR} interface ${TBB_INTERFACE_VERSION})") +elseif(HAVE_CSTRIPES) + set(CV_PARALLEL_FRAMEWORK "C=") +elseif(HAVE_OPENMP) + set(CV_PARALLEL_FRAMEWORK "OpenMP") +elseif(HAVE_GCD) + set(CV_PARALLEL_FRAMEWORK "GCD") +elseif(WINRT OR HAVE_CONCURRENCY) + set(CV_PARALLEL_FRAMEWORK "Concurrency") +elseif(HAVE_PTHREADS_PF) + set(CV_PARALLEL_FRAMEWORK "pthreads") +else() + set(CV_PARALLEL_FRAMEWORK "none") +endif() +status("") +status(" Parallel framework:" TRUE THEN "${CV_PARALLEL_FRAMEWORK}" ELSE NO) + + # ========================== Other third-party libraries ========================== status("") status(" Other third-party libraries:") @@ -1045,12 +1066,6 @@ status(" Use IPP Async:" HAVE_IPP_A THEN "YES" ELSE NO) endif(DEFINED WITH_IPP_A) status(" Use Eigen:" HAVE_EIGEN THEN "YES (ver ${EIGEN_WORLD_VERSION}.${EIGEN_MAJOR_VERSION}.${EIGEN_MINOR_VERSION})" ELSE NO) -status(" Use TBB:" HAVE_TBB THEN "YES (ver ${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR} interface ${TBB_INTERFACE_VERSION})" ELSE NO) -status(" Use OpenMP:" HAVE_OPENMP THEN YES ELSE NO) -status(" Use GCD" HAVE_GCD THEN YES ELSE NO) -status(" Use Concurrency" HAVE_CONCURRENCY THEN YES ELSE NO) -status(" Use C=:" HAVE_CSTRIPES THEN YES ELSE NO) -status(" Use pthreads for parallel for:" HAVE_PTHREADS_PF THEN YES ELSE NO) status(" Use Cuda:" HAVE_CUDA THEN "YES (ver ${CUDA_VERSION_STRING})" ELSE NO) status(" Use OpenCL:" HAVE_OPENCL THEN YES ELSE NO) diff --git a/cmake/OpenCVFindLibsPerf.cmake b/cmake/OpenCVFindLibsPerf.cmake index bda5d792a3..d1bc5419ab 100644 --- a/cmake/OpenCVFindLibsPerf.cmake +++ b/cmake/OpenCVFindLibsPerf.cmake @@ -120,12 +120,16 @@ if(WITH_OPENMP) set(HAVE_OPENMP "${OPENMP_FOUND}") endif() -if(UNIX OR ANDROID) -if(NOT APPLE AND NOT HAVE_TBB AND NOT HAVE_OPENMP) - set(HAVE_PTHREADS_PF 1) -else() - set(HAVE_PTHREADS_PF 0) -endif() +if(NOT MSVC AND NOT DEFINED HAVE_PTHREADS) + set(_fname "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/pthread_test.cpp") + file(WRITE "${_fname}" "#include \nint main() { (void)pthread_self(); return 0; }\n") + try_compile(HAVE_PTHREADS "${CMAKE_BINARY_DIR}" "${_fname}") + file(REMOVE "${_fname}") +endif() + +ocv_clear_vars(HAVE_PTHREADS_PF) +if(WITH_PTHREADS_PF) + set(HAVE_PTHREADS_PF ${HAVE_PTHREADS}) else() set(HAVE_PTHREADS_PF 0) endif() diff --git a/cmake/templates/cvconfig.h.in b/cmake/templates/cvconfig.h.in index 4a1d1c632c..3330774c14 100644 --- a/cmake/templates/cvconfig.h.in +++ b/cmake/templates/cvconfig.h.in @@ -139,6 +139,12 @@ /* PNG codec */ #cmakedefine HAVE_PNG +/* Posix threads (pthreads) */ +#cmakedefine HAVE_PTHREADS + +/* parallel_for with pthreads */ +#cmakedefine HAVE_PTHREADS_PF + /* Qt support */ #cmakedefine HAVE_QT diff --git a/modules/core/src/parallel.cpp b/modules/core/src/parallel.cpp index 0b593ee070..caa81299d7 100644 --- a/modules/core/src/parallel.cpp +++ b/modules/core/src/parallel.cpp @@ -80,6 +80,7 @@ 4. HAVE_GCD - system wide, used automatically (APPLE only) 5. WINRT - system wide, used automatically (Windows RT only) 6. HAVE_CONCURRENCY - part of runtime, used automatically (Windows only - MSVS 10, MSVS 11) + 7. HAVE_PTHREADS_PF - pthreads if available */ #if defined HAVE_TBB @@ -125,14 +126,14 @@ # define CV_PARALLEL_FRAMEWORK "winrt-concurrency" #elif defined HAVE_CONCURRENCY # define CV_PARALLEL_FRAMEWORK "ms-concurrency" -#elif defined HAVE_PTHREADS +#elif defined HAVE_PTHREADS_PF # define CV_PARALLEL_FRAMEWORK "pthreads" #endif namespace cv { ParallelLoopBody::~ParallelLoopBody() {} -#if defined HAVE_PTHREADS && HAVE_PTHREADS +#ifdef HAVE_PTHREADS_PF void parallel_for_pthreads(const cv::Range& range, const cv::ParallelLoopBody& body, double nstripes); size_t parallel_pthreads_get_threads_num(); void parallel_pthreads_set_threads_num(int num); @@ -306,7 +307,7 @@ void cv::parallel_for_(const cv::Range& range, const cv::ParallelLoopBody& body, Concurrency::CurrentScheduler::Detach(); } -#elif defined HAVE_PTHREADS +#elif defined HAVE_PTHREADS_PF parallel_for_pthreads(range, body, nstripes); @@ -365,7 +366,7 @@ int cv::getNumThreads(void) ? Concurrency::CurrentScheduler::Get()->GetNumberOfVirtualProcessors() : pplScheduler->GetNumberOfVirtualProcessors()); -#elif defined HAVE_PTHREADS +#elif defined HAVE_PTHREADS_PF return parallel_pthreads_get_threads_num(); @@ -426,7 +427,7 @@ void cv::setNumThreads( int threads ) Concurrency::MaxConcurrency, threads-1)); } -#elif defined HAVE_PTHREADS +#elif defined HAVE_PTHREADS_PF parallel_pthreads_set_threads_num(threads); @@ -452,6 +453,8 @@ int cv::getThreadNum(void) return 0; #elif defined HAVE_CONCURRENCY return std::max(0, (int)Concurrency::Context::VirtualProcessorId()); // zero for master thread, unique number for others but not necessary 1,2,3,... +#elif defined HAVE_PTHREADS_PF + return (int)(size_t)(void*)pthread_self(); // no zero-based indexing #else return 0; #endif diff --git a/modules/core/src/parallel_pthreads.cpp b/modules/core/src/parallel_pthreads.cpp index 8c34959783..091ea2db84 100644 --- a/modules/core/src/parallel_pthreads.cpp +++ b/modules/core/src/parallel_pthreads.cpp @@ -42,7 +42,7 @@ #include "precomp.hpp" -#if defined HAVE_PTHREADS && HAVE_PTHREADS +#ifdef HAVE_PTHREADS_PF #include #include diff --git a/modules/core/src/precomp.hpp b/modules/core/src/precomp.hpp index d463126368..88b60e4713 100644 --- a/modules/core/src/precomp.hpp +++ b/modules/core/src/precomp.hpp @@ -292,12 +292,6 @@ TLSData& getCoreTlsData(); #define CL_RUNTIME_EXPORT #endif -#ifndef HAVE_PTHREADS -#if !(defined WIN32 || defined _WIN32 || defined WINCE || defined HAVE_WINRT) -#define HAVE_PTHREADS 1 -#endif -#endif - extern bool __termination; // skip some cleanups, because process is terminating // (for example, if ExitProcess() was already called) From c613ee2da43bc43425acd81ee08fa4f18fa30e7f Mon Sep 17 00:00:00 2001 From: Jaime Fernandez Date: Thu, 11 Jun 2015 09:41:12 -0700 Subject: [PATCH 017/133] BUG: ndarray to Mat conversion with NPY_RELAXED_STRIDES set Add logic to avoid nonsense strides when dimension size is 1 and NumPy is built with NPY_RELAXED_STRIDES from tripping OpenCV. --- modules/python/src2/cv2.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 974545994b..fbe4c7f01e 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -315,8 +315,9 @@ static bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo info) // a) multi-dimensional (ndims > 2) arrays, as well as simpler 1- and 2-dimensional cases // b) transposed arrays, where _strides[] elements go in non-descending order // c) flipped arrays, where some of _strides[] elements are negative - if( (i == ndims-1 && (size_t)_strides[i] != elemsize) || - (i < ndims-1 && _strides[i] < _strides[i+1]) ) + // the _sizes[i] > 1 is needed to avoid spurious copies when NPY_RELAXED_STRIDES is set + if( (i == ndims-1 && _sizes[i] > 1 && (size_t)_strides[i] != elemsize) || + (i < ndims-1 && _sizes[i] > 1 && _strides[i] < _strides[i+1]) ) needcopy = true; } @@ -343,10 +344,21 @@ static bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo info) _strides = PyArray_STRIDES(oarr); } - for(int i = 0; i < ndims; i++) + // Normalize strides in case NPY_RELAXED_STRIDES is set + size_t default_step = elemsize; + for ( int i = ndims - 1; i >= 0; --i ) { size[i] = (int)_sizes[i]; - step[i] = (size_t)_strides[i]; + if ( size[i] > 1 ) + { + step[i] = (size_t)_strides[i]; + default_step = step[i] * size[i]; + } + else + { + step[i] = default_step; + default_step *= size[i]; + } } // handle degenerate case From def2256609077475adb3760c38dab4af362f2311 Mon Sep 17 00:00:00 2001 From: Alexander Duda Date: Fri, 12 Jun 2015 13:50:43 +0200 Subject: [PATCH 018/133] cv::stereoRectify: fix segfault in case of empty distCoeffs cvStereoRectify assumes that NULL is provided in case of no distCoeffs --- modules/calib3d/src/calibration.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/calib3d/src/calibration.cpp b/modules/calib3d/src/calibration.cpp index 98deab6396..5dcd274e34 100644 --- a/modules/calib3d/src/calibration.cpp +++ b/modules/calib3d/src/calibration.cpp @@ -2194,7 +2194,7 @@ void cvStereoRectify( const CvMat* _cameraMatrix1, const CvMat* _cameraMatrix2, for( k = 0; k < 2; k++ ) { const CvMat* A = k == 0 ? _cameraMatrix1 : _cameraMatrix2; const CvMat* Dk = k == 0 ? _distCoeffs1 : _distCoeffs2; - double dk1 = Dk ? cvmGet(Dk, 0, 0) : 0; + double dk1 = Dk && Dk->data.ptr ? cvmGet(Dk, 0, 0) : 0; double fc = cvmGet(A,idx^1,idx^1); if( dk1 < 0 ) { fc *= 1 + dk1*(nx*nx + ny*ny)/(4*fc*fc); @@ -3372,7 +3372,9 @@ void cv::stereoRectify( InputArray _cameraMatrix1, InputArray _distCoeffs1, p_Q = &(c_Q = _Qmat.getMat()); } - cvStereoRectify( &c_cameraMatrix1, &c_cameraMatrix2, &c_distCoeffs1, &c_distCoeffs2, + CvMat *p_distCoeffs1 = distCoeffs1.empty() ? NULL : &c_distCoeffs1; + CvMat *p_distCoeffs2 = distCoeffs2.empty() ? NULL : &c_distCoeffs2; + cvStereoRectify( &c_cameraMatrix1, &c_cameraMatrix2, p_distCoeffs1, p_distCoeffs2, imageSize, &c_R, &c_T, &c_R1, &c_R2, &c_P1, &c_P2, p_Q, flags, alpha, newImageSize, (CvRect*)validPixROI1, (CvRect*)validPixROI2); } From 5123c3e52e0f5c48aa863d9b52babfcfb348afad Mon Sep 17 00:00:00 2001 From: sbokov Date: Sun, 14 Jun 2015 23:59:48 +0300 Subject: [PATCH 019/133] Added ground-truth disparity map for 'aloe' --- samples/data/aloeGT.png | Bin 0 -> 98827 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 samples/data/aloeGT.png diff --git a/samples/data/aloeGT.png b/samples/data/aloeGT.png new file mode 100644 index 0000000000000000000000000000000000000000..43e62ebfe910f6be8c440d57b6ad300743768934 GIT binary patch literal 98827 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 KMNUMnLSTZ{B)cpC literal 0 HcmV?d00001 From 6724a0b8cd40edee1d28a6574359e7d98bc41eee Mon Sep 17 00:00:00 2001 From: Maxim Kostin Date: Tue, 2 Jun 2015 18:33:21 +0300 Subject: [PATCH 020/133] Added dummy highgui WinRT implementation --- modules/highgui/CMakeLists.txt | 26 +- modules/highgui/src/agile_wrl.h | 568 -- modules/highgui/src/ppltasks_winrt.h | 9466 -------------------------- modules/highgui/src/window.cpp | 5 +- modules/highgui/src/window_winrt.cpp | 438 ++ 5 files changed, 461 insertions(+), 10042 deletions(-) delete mode 100644 modules/highgui/src/agile_wrl.h delete mode 100644 modules/highgui/src/ppltasks_winrt.h create mode 100644 modules/highgui/src/window_winrt.cpp diff --git a/modules/highgui/CMakeLists.txt b/modules/highgui/CMakeLists.txt index 0b7f8fe526..dba05ec05b 100644 --- a/modules/highgui/CMakeLists.txt +++ b/modules/highgui/CMakeLists.txt @@ -1,7 +1,3 @@ -if (WINRT) - ocv_module_disable(highgui) -endif() - set(the_description "High-level GUI and Media I/O") ocv_add_module(highgui opencv_imgproc opencv_imgcodecs opencv_videoio WRAP python) @@ -11,8 +7,7 @@ ocv_add_module(highgui opencv_imgproc opencv_imgcodecs opencv_videoio WRAP pytho # Jose Luis Blanco, 2008 # ---------------------------------------------------------------------------- -# Compilation with /ZW is not allowed for *.c files -if(HAVE_WINRT_CX AND NOT WINRT) +if(WINRT_8_1) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /ZW") endif() @@ -71,6 +66,25 @@ elseif(HAVE_QT) if(${_have_flag}) set_source_files_properties(${_RCC_OUTFILES} PROPERTIES COMPILE_FLAGS -Wno-missing-declarations) endif() +elseif(WINRT AND NOT WINRT_8_0) + # Dependencies used by the implementation referenced + # below are not available on WinRT 8.0. + # Enabling it for WiRT 8.1+ only. + status(" ${name}: WinRT detected") + list(APPEND highgui_srcs ${CMAKE_CURRENT_LIST_DIR}/src/window_winrt.cpp) + + # libraries below are neither available nor required + # on ARM devices and/or Windows Phone + if(WINRT_PHONE OR (OpenCV_ARCH STREQUAL "ARM")) + list(REMOVE_ITEM HIGHGUI_LIBRARIES "comctl32" "gdi32" "ole32" "setupapi") + if(WINRT_PHONE) + status(" ${name}: Windows Phone detected") + elseif(OpenCV_ARCH STREQUAL "ARM") + status(" ${name}: ARM detected") + endif() + status(" ${name}: Removing 'comctl32.lib, gdi32.lib, ole32.lib, setupapi.lib'") + status(" ${name}: Leaving '${HIGHGUI_LIBRARIES}'") + endif() elseif(HAVE_WIN32UI) list(APPEND highgui_srcs ${CMAKE_CURRENT_LIST_DIR}/src/window_w32.cpp) elseif(HAVE_GTK OR HAVE_GTK3) diff --git a/modules/highgui/src/agile_wrl.h b/modules/highgui/src/agile_wrl.h deleted file mode 100644 index 99fbf41856..0000000000 --- a/modules/highgui/src/agile_wrl.h +++ /dev/null @@ -1,568 +0,0 @@ -// -// Copyright (C) Microsoft Corporation -// All rights reserved. -// Modified for native C++ WRL support by Gregory Morse -// -// Code in Details namespace is for internal usage within the library code -// - -#ifndef _PLATFORM_AGILE_H_ -#define _PLATFORM_AGILE_H_ - -#ifdef _MSC_VER -#pragma once -#endif // _MSC_VER - -#include -#include - -template class Agile; - -template -struct UnwrapAgile -{ - static const bool _IsAgile = false; -}; -template -struct UnwrapAgile> -{ - static const bool _IsAgile = true; -}; -template -struct UnwrapAgile> -{ - static const bool _IsAgile = true; -}; - -#define IS_AGILE(T) UnwrapAgile::_IsAgile - -#define __is_winrt_agile(T) (std::is_same::value || std::is_base_of::value || std::is_base_of::value) //derived from Microsoft::WRL::FtmBase or IAgileObject - -#define __is_win_interface(T) (std::is_base_of::value || std::is_base_of::value) //derived from IUnknown or IInspectable - -#define __is_win_class(T) (std::is_same::value || std::is_base_of::value) //derived from Microsoft::WRL::RuntimeClass or HSTRING - - namespace Details - { - IUnknown* __stdcall GetObjectContext(); - HRESULT __stdcall GetProxyImpl(IUnknown*, REFIID, IUnknown*, IUnknown**); - HRESULT __stdcall ReleaseInContextImpl(IUnknown*, IUnknown*); - - template -#if _MSC_VER >= 1800 - __declspec(no_refcount) inline HRESULT GetProxy(T *ObjectIn, IUnknown *ContextCallBack, T **Proxy) -#else - inline HRESULT GetProxy(T *ObjectIn, IUnknown *ContextCallBack, T **Proxy) -#endif - { -#if _MSC_VER >= 1800 - return GetProxyImpl(*reinterpret_cast(&ObjectIn), __uuidof(T*), ContextCallBack, reinterpret_cast(Proxy)); -#else - return GetProxyImpl(*reinterpret_cast(&const_cast(ObjectIn)), __uuidof(T*), ContextCallBack, reinterpret_cast(Proxy)); -#endif - } - - template - inline HRESULT ReleaseInContext(T *ObjectIn, IUnknown *ContextCallBack) - { - return ReleaseInContextImpl(ObjectIn, ContextCallBack); - } - - template - class AgileHelper - { - __abi_IUnknown* _p; - bool _release; - public: - AgileHelper(__abi_IUnknown* p, bool release = true) : _p(p), _release(release) - { - } - AgileHelper(AgileHelper&& other) : _p(other._p), _release(other._release) - { - _other._p = nullptr; - _other._release = true; - } - AgileHelper operator=(AgileHelper&& other) - { - _p = other._p; - _release = other._release; - _other._p = nullptr; - _other._release = true; - return *this; - } - - ~AgileHelper() - { - if (_release && _p) - { - _p->__abi_Release(); - } - } - - __declspec(no_refcount) __declspec(no_release_return) - T* operator->() - { - return reinterpret_cast(_p); - } - - __declspec(no_refcount) __declspec(no_release_return) - operator T * () - { - return reinterpret_cast(_p); - } - private: - AgileHelper(const AgileHelper&); - AgileHelper operator=(const AgileHelper&); - }; - template - struct __remove_hat - { - typedef T type; - }; - template - struct __remove_hat - { - typedef T type; - }; - template - struct AgileTypeHelper - { - typename typedef __remove_hat::type type; - typename typedef __remove_hat::type* agileMemberType; - }; - } // namespace Details - -#pragma warning(push) -#pragma warning(disable: 4451) // Usage of ref class inside this context can lead to invalid marshaling of object across contexts - - template < - typename T, - bool TIsNotAgile = (__is_win_class(typename Details::AgileTypeHelper::type) && !__is_winrt_agile(typename Details::AgileTypeHelper::type)) || - __is_win_interface(typename Details::AgileTypeHelper::type) - > - class Agile - { - static_assert(__is_win_class(typename Details::AgileTypeHelper::type) || __is_win_interface(typename Details::AgileTypeHelper::type), "Agile can only be used with ref class or interface class types"); - typename typedef Details::AgileTypeHelper::agileMemberType TypeT; - TypeT _object; - ::Microsoft::WRL::ComPtr _contextCallback; - ULONG_PTR _contextToken; - -#if _MSC_VER >= 1800 - enum class AgileState - { - NonAgilePointer = 0, - AgilePointer = 1, - Unknown = 2 - }; - AgileState _agileState; -#endif - - void CaptureContext() - { - _contextCallback = Details::GetObjectContext(); - __abi_ThrowIfFailed(CoGetContextToken(&_contextToken)); - } - - void SetObject(TypeT object) - { - // Capture context before setting the pointer - // If context capture fails then nothing to cleanup - Release(); - if (object != nullptr) - { - ::Microsoft::WRL::ComPtr checkIfAgile; - HRESULT hr = reinterpret_cast(object)->QueryInterface(__uuidof(IAgileObject), &checkIfAgile); - // Don't Capture context if object is agile - if (hr != S_OK) - { -#if _MSC_VER >= 1800 - _agileState = AgileState::NonAgilePointer; -#endif - CaptureContext(); - } -#if _MSC_VER >= 1800 - else - { - _agileState = AgileState::AgilePointer; - } -#endif - } - _object = object; - } - - public: - Agile() throw() : _object(nullptr), _contextToken(0) -#if _MSC_VER >= 1800 - , _agileState(AgileState::Unknown) -#endif - { - } - - Agile(nullptr_t) throw() : _object(nullptr), _contextToken(0) -#if _MSC_VER >= 1800 - , _agileState(AgileState::Unknown) -#endif - { - } - - explicit Agile(TypeT object) throw() : _object(nullptr), _contextToken(0) -#if _MSC_VER >= 1800 - , _agileState(AgileState::Unknown) -#endif - { - // Assumes that the source object is from the current context - SetObject(object); - } - - Agile(const Agile& object) throw() : _object(nullptr), _contextToken(0) -#if _MSC_VER >= 1800 - , _agileState(AgileState::Unknown) -#endif - { - // Get returns pointer valid for current context - SetObject(object.Get()); - } - - Agile(Agile&& object) throw() : _object(nullptr), _contextToken(0) -#if _MSC_VER >= 1800 - , _agileState(AgileState::Unknown) -#endif - { - // Assumes that the source object is from the current context - Swap(object); - } - - ~Agile() throw() - { - Release(); - } - - TypeT Get() const - { - // Agile object, no proxy required -#if _MSC_VER >= 1800 - if (_agileState == AgileState::AgilePointer || _object == nullptr) -#else - if (_contextToken == 0 || _contextCallback == nullptr || _object == nullptr) -#endif - { - return _object; - } - - // Do the check for same context - ULONG_PTR currentContextToken; - __abi_ThrowIfFailed(CoGetContextToken(¤tContextToken)); - if (currentContextToken == _contextToken) - { - return _object; - } - -#if _MSC_VER >= 1800 - // Different context and holding on to a non agile object - // Do the costly work of getting a proxy - TypeT localObject; - __abi_ThrowIfFailed(Details::GetProxy(_object, _contextCallback.Get(), &localObject)); - - if (_agileState == AgileState::Unknown) -#else - // Object is agile if it implements IAgileObject - // GetAddressOf captures the context with out knowing the type of object that it will hold - if (_object != nullptr) -#endif - { -#if _MSC_VER >= 1800 - // Object is agile if it implements IAgileObject - // GetAddressOf captures the context with out knowing the type of object that it will hold - ::Microsoft::WRL::ComPtr checkIfAgile; - HRESULT hr = reinterpret_cast(localObject)->QueryInterface(__uuidof(IAgileObject), &checkIfAgile); -#else - ::Microsoft::WRL::ComPtr checkIfAgile; - HRESULT hr = reinterpret_cast(_object)->QueryInterface(__uuidof(IAgileObject), &checkIfAgile); -#endif - if (hr == S_OK) - { - auto pThis = const_cast(this); -#if _MSC_VER >= 1800 - pThis->_agileState = AgileState::AgilePointer; -#endif - pThis->_contextToken = 0; - pThis->_contextCallback = nullptr; - return _object; - } -#if _MSC_VER >= 1800 - else - { - auto pThis = const_cast(this); - pThis->_agileState = AgileState::NonAgilePointer; - } -#endif - } - -#if _MSC_VER < 1800 - // Different context and holding on to a non agile object - // Do the costly work of getting a proxy - TypeT localObject; - __abi_ThrowIfFailed(Details::GetProxy(_object, _contextCallback.Get(), &localObject)); -#endif - return localObject; - } - - TypeT* GetAddressOf() throw() - { - Release(); - CaptureContext(); - return &_object; - } - - TypeT* GetAddressOfForInOut() throw() - { - CaptureContext(); - return &_object; - } - - TypeT operator->() const throw() - { - return Get(); - } - - Agile& operator=(nullptr_t) throw() - { - Release(); - return *this; - } - - Agile& operator=(TypeT object) throw() - { - Agile(object).Swap(*this); - return *this; - } - - Agile& operator=(Agile object) throw() - { - // parameter is by copy which gets pointer valid for current context - object.Swap(*this); - return *this; - } - -#if _MSC_VER < 1800 - Agile& operator=(IUnknown* lp) throw() - { - // bump ref count - ::Microsoft::WRL::ComPtr spObject(lp); - - // put it into Platform Object - Platform::Object object; - *(IUnknown**)(&object) = spObject.Detach(); - - SetObject(object); - return *this; - } -#endif - - void Swap(Agile& object) - { - std::swap(_object, object._object); - std::swap(_contextCallback, object._contextCallback); - std::swap(_contextToken, object._contextToken); -#if _MSC_VER >= 1800 - std::swap(_agileState, object._agileState); -#endif - } - - // Release the interface and set to NULL - void Release() throw() - { - if (_object) - { - // Cast to IInspectable (no QI) - IUnknown* pObject = *(IUnknown**)(&_object); - // Set * to null without release - *(IUnknown**)(&_object) = nullptr; - - ULONG_PTR currentContextToken; - __abi_ThrowIfFailed(CoGetContextToken(¤tContextToken)); - if (_contextToken == 0 || _contextCallback == nullptr || _contextToken == currentContextToken) - { - pObject->Release(); - } - else - { - Details::ReleaseInContext(pObject, _contextCallback.Get()); - } - _contextCallback = nullptr; - _contextToken = 0; -#if _MSC_VER >= 1800 - _agileState = AgileState::Unknown; -#endif - } - } - - bool operator==(nullptr_t) const throw() - { - return _object == nullptr; - } - - bool operator==(const Agile& other) const throw() - { - return _object == other._object && _contextToken == other._contextToken; - } - - bool operator<(const Agile& other) const throw() - { - if (reinterpret_cast(_object) < reinterpret_cast(other._object)) - { - return true; - } - - return _object == other._object && _contextToken < other._contextToken; - } - }; - - template - class Agile - { - static_assert(__is_win_class(typename Details::AgileTypeHelper::type) || __is_win_interface(typename Details::AgileTypeHelper::type), "Agile can only be used with ref class or interface class types"); - typename typedef Details::AgileTypeHelper::agileMemberType TypeT; - TypeT _object; - - public: - Agile() throw() : _object(nullptr) - { - } - - Agile(nullptr_t) throw() : _object(nullptr) - { - } - - explicit Agile(TypeT object) throw() : _object(object) - { - } - - Agile(const Agile& object) throw() : _object(object._object) - { - } - - Agile(Agile&& object) throw() : _object(nullptr) - { - Swap(object); - } - - ~Agile() throw() - { - Release(); - } - - TypeT Get() const - { - return _object; - } - - TypeT* GetAddressOf() throw() - { - Release(); - return &_object; - } - - TypeT* GetAddressOfForInOut() throw() - { - return &_object; - } - - TypeT operator->() const throw() - { - return Get(); - } - - Agile& operator=(nullptr_t) throw() - { - Release(); - return *this; - } - - Agile& operator=(TypeT object) throw() - { - if (_object != object) - { - _object = object; - } - return *this; - } - - Agile& operator=(Agile object) throw() - { - object.Swap(*this); - return *this; - } - -#if _MSC_VER < 1800 - Agile& operator=(IUnknown* lp) throw() - { - Release(); - // bump ref count - ::Microsoft::WRL::ComPtr spObject(lp); - - // put it into Platform Object - Platform::Object object; - *(IUnknown**)(&object) = spObject.Detach(); - - _object = object; - return *this; - } -#endif - - // Release the interface and set to NULL - void Release() throw() - { - _object = nullptr; - } - - void Swap(Agile& object) - { - std::swap(_object, object._object); - } - - bool operator==(nullptr_t) const throw() - { - return _object == nullptr; - } - - bool operator==(const Agile& other) const throw() - { - return _object == other._object; - } - - bool operator<(const Agile& other) const throw() - { - return reinterpret_cast(_object) < reinterpret_cast(other._object); - } - }; - -#pragma warning(pop) - - template - bool operator==(nullptr_t, const Agile& a) throw() - { - return a == nullptr; - } - - template - bool operator!=(const Agile& a, nullptr_t) throw() - { - return !(a == nullptr); - } - - template - bool operator!=(nullptr_t, const Agile& a) throw() - { - return !(a == nullptr); - } - - template - bool operator!=(const Agile& a, const Agile& b) throw() - { - return !(a == b); - } - - -#endif // _PLATFORM_AGILE_H_ diff --git a/modules/highgui/src/ppltasks_winrt.h b/modules/highgui/src/ppltasks_winrt.h deleted file mode 100644 index c9867d8a56..0000000000 --- a/modules/highgui/src/ppltasks_winrt.h +++ /dev/null @@ -1,9466 +0,0 @@ -/*** -* ==++== -* -* Copyright (c) Microsoft Corporation. All rights reserved. -* -* Modified for native C++ WRL support by Gregory Morse -* -* ==--== -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* ppltasks_winrt.h -* -* Parallel Patterns Library - PPL Tasks -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ - -#pragma once - -#ifndef _PPLTASKS_WINRT_H -#define _PPLTASKS_WINRT_H - -#include -#include -#if _MSC_VER >= 1800 -#include - -// Cannot build using a compiler that is older than dev10 SP1 -#ifdef _MSC_VER -#if _MSC_FULL_VER < 160040219 /*IFSTRIP=IGN*/ -#error ERROR: Visual Studio 2010 SP1 or later is required to build ppltasks -#endif /*IFSTRIP=IGN*/ -#endif -#else -#include -#endif -#include -#include -#include -#include -#if _MSC_VER >= 1800 -#include -#endif - -#ifndef __cplusplus_winrt - -#include -#include -#if _MSC_VER >= 1800 -#include "agile_wrl.h" -#endif -#include -#include - -#ifndef _UITHREADCTXT_SUPPORT - -#ifdef WINAPI_FAMILY /*IFSTRIP=IGN*/ - -// It is safe to include winapifamily as WINAPI_FAMILY was defined by the user -#include - -#if WINAPI_FAMILY == WINAPI_FAMILY_APP /*IFSTRIP=IGN*/ - // UI thread context support is not required for desktop and Windows Store apps - #define _UITHREADCTXT_SUPPORT 0 -#elif WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP /*IFSTRIP=IGN*/ - // UI thread context support is not required for desktop and Windows Store apps - #define _UITHREADCTXT_SUPPORT 0 -#else /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ - #define _UITHREADCTXT_SUPPORT 1 -#endif /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ - -#else /* WINAPI_FAMILY */ - // Not supported without a WINAPI_FAMILY setting. - #define _UITHREADCTXT_SUPPORT 0 -#endif /* WINAPI_FAMILY */ - -#endif /* _UITHREADCTXT_SUPPORT */ - -#if _UITHREADCTXT_SUPPORT -#include -#endif /* _UITHREADCTXT_SUPPORT */ - -#pragma detect_mismatch("_PPLTASKS_WITH_WINRT", "0") - -#ifdef _DEBUG -#define _DBG_ONLY(X) X -#else -#define _DBG_ONLY(X) -#endif // #ifdef _DEBUG - -// std::copy_exception changed to std::make_exception_ptr from VS 2010 to VS 11. -#ifdef _MSC_VER -#if _MSC_VER < 1700 /*IFSTRIP=IGN*/ -namespace std -{ - template exception_ptr make_exception_ptr(_E _Except) - { - return copy_exception(_Except); - } -} -#endif -#ifndef _PPLTASK_ASYNC_LOGGING -#if _MSC_VER >= 1800 && defined(__cplusplus_winrt) -#define _PPLTASK_ASYNC_LOGGING 1 // Only enable async logging under dev12 winrt -#else -#define _PPLTASK_ASYNC_LOGGING 0 -#endif -#endif -#endif - -#pragma pack(push,_CRT_PACKING) - -#pragma warning(push) -#pragma warning(disable: 28197) -#pragma warning(disable: 4100) // Unreferenced formal parameter - needed for document generation -#if _MSC_VER >= 1800 -#pragma warning(disable: 4127) // constant express in if condition - we use it for meta programming -#else -#pragma warning(disable: 4702) // Unreachable code - it is caused by user lambda throw exceptions -#endif - -// All CRT public header files are required to be protected from the macro new -#pragma push_macro("new") -#undef new - -// stuff ported from Dev11 CRT -// NOTE: this doesn't actually match std::declval. it behaves differently for void! -// so don't blindly change it to std::declval. -namespace stdx -{ - template - _T&& declval(); -} - -/// -/// The Concurrency_winrt namespace provides classes and functions that give you access to the Concurrency Runtime, -/// a concurrent programming framework for C++. For more information, see . -/// -/**/ -namespace Concurrency_winrt -{ - // In debug builds, default to 10 frames, unless this is overridden prior to #includ'ing ppltasks.h. In retail builds, default to only one frame. -#ifndef PPL_TASK_SAVE_FRAME_COUNT -#ifdef _DEBUG -#define PPL_TASK_SAVE_FRAME_COUNT 10 -#else -#define PPL_TASK_SAVE_FRAME_COUNT 1 -#endif -#endif - - /// - /// Helper macro to determine how many stack frames need to be saved. When any number less or equal to 1 is specified, - /// only one frame is captured and no stackwalk will be involved. Otherwise, the number of callstack frames will be captured. - /// - /// - /// This needs to be defined as a macro rather than a function so that if we're only gathering one frame, _ReturnAddress() - /// will evaluate to client code, rather than a helper function inside of _TaskCreationCallstack, itself. - /// -#ifdef _CAPTURE_CALLSTACK -#undef _CAPTURE_CALLSTACK -#endif -#if PPL_TASK_SAVE_FRAME_COUNT > 1 -#if !defined(_DEBUG) -#pragma message ("WARNING: Redefinning PPL_TASK_SAVE_FRAME_COUNT under Release build for non-desktop applications is not supported; only one frame will be captured!") -#define _CAPTURE_CALLSTACK() ::Concurrency_winrt::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) -#else -#define _CAPTURE_CALLSTACK() ::Concurrency_winrt::details::_TaskCreationCallstack::_CaptureMultiFramesCallstack(PPL_TASK_SAVE_FRAME_COUNT) -#endif -#else -#define _CAPTURE_CALLSTACK() ::Concurrency_winrt::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) -#endif -/// - -/// A type that represents the terminal state of a task. Valid values are completed and canceled. -/// -/// -/**/ -typedef Concurrency::task_group_status task_status; - -template class task; -template <> class task; - -/// -/// Returns an indication of whether the task that is currently executing has received a request to cancel its -/// execution. Cancellation is requested on a task if the task was created with a cancellation token, and -/// the token source associated with that token is canceled. -/// -/// -/// true if the currently executing task has received a request for cancellation, false otherwise. -/// -/// -/// If you call this method in the body of a task and it returns true, you must respond with a call to -/// cancel_current_task to acknowledge the cancellation request, -/// after performing any cleanup you need. This will abort the execution of the task and cause it to enter into -/// the canceled state. If you do not respond and continue execution, or return instead of calling -/// cancel_current_task, the task will enter the completed state when it is done. -/// state. -/// A task is not cancellable if it was created without a cancellation token. -/// -/// -/// -/// -/// -/**/ -#if _MSC_VER >= 1800 -inline bool __cdecl is_task_cancellation_requested() -{ - return ::Concurrency::details::_TaskCollection_t::_Is_cancellation_requested(); -} -#else -inline bool __cdecl is_task_cancellation_requested() -{ - // ConcRT scheduler under the hood is using TaskCollection, which is same as task_group - return ::Concurrency::is_current_task_group_canceling(); -} -#endif - -/// -/// Cancels the currently executing task. This function can be called from within the body of a task to abort the -/// task's execution and cause it to enter the canceled state. While it may be used in response to -/// the is_task_cancellation_requested function, you may -/// also use it by itself, to initiate cancellation of the task that is currently executing. -/// It is not a supported scenario to call this function if you are not within the body of a task. -/// Doing so will result in undefined behavior such as a crash or a hang in your application. -/// -/// -/// -/**/ -//#if _MSC_VER >= 1800 -inline __declspec(noreturn) void __cdecl cancel_current_task() -{ - throw Concurrency::task_canceled(); -} -//#else -//_CRTIMP2 __declspec(noreturn) void __cdecl cancel_current_task(); -//#endif - -namespace details -{ -#if _MSC_VER >= 1800 - /// - /// Callstack container, which is used to capture and preserve callstacks in ppltasks. - /// Members of this class is examined by vc debugger, thus there will be no public access methods. - /// Please note that names of this class should be kept stable for debugger examining. - /// - class _TaskCreationCallstack - { - private: - // If _M_SingleFrame != nullptr, there will be only one frame of callstacks, which is stored in _M_SingleFrame; - // otherwise, _M_Frame will store all the callstack frames. - void* _M_SingleFrame; - std::vector _M_frames; - public: - _TaskCreationCallstack() - { - _M_SingleFrame = nullptr; - } - - // Store one frame of callstack. This function works for both Debug / Release CRT. - static _TaskCreationCallstack _CaptureSingleFrameCallstack(void *_SingleFrame) - { - _TaskCreationCallstack _csc; - _csc._M_SingleFrame = _SingleFrame; - return _csc; - } - - // Capture _CaptureFrames number of callstack frames. This function only work properly for Desktop or Debug CRT. - __declspec(noinline) - static _TaskCreationCallstack _CaptureMultiFramesCallstack(size_t _CaptureFrames) - { - _TaskCreationCallstack _csc; - _csc._M_frames.resize(_CaptureFrames); - // skip 2 frames to make sure callstack starts from user code - _csc._M_frames.resize(::Concurrency::details::platform::CaptureCallstack(&_csc._M_frames[0], 2, _CaptureFrames)); - return _csc; - } - }; -#endif - typedef UINT32 _Unit_type; - - struct _TypeSelectorNoAsync {}; - struct _TypeSelectorAsyncOperationOrTask {}; - struct _TypeSelectorAsyncOperation : public _TypeSelectorAsyncOperationOrTask { }; - struct _TypeSelectorAsyncTask : public _TypeSelectorAsyncOperationOrTask { }; - struct _TypeSelectorAsyncAction {}; - struct _TypeSelectorAsyncActionWithProgress {}; - struct _TypeSelectorAsyncOperationWithProgress {}; - - template - struct _NormalizeVoidToUnitType - { - typedef _Ty _Type; - }; - - template<> - struct _NormalizeVoidToUnitType - { - typedef _Unit_type _Type; - }; - - template - struct _IsUnwrappedAsyncSelector - { - static const bool _Value = true; - }; - - template<> - struct _IsUnwrappedAsyncSelector<_TypeSelectorNoAsync> - { - static const bool _Value = false; - }; - - template - struct _UnwrapTaskType - { - typedef _Ty _Type; - }; - - template - struct _UnwrapTaskType> - { - typedef _Ty _Type; - }; - - template - _TypeSelectorAsyncTask _AsyncOperationKindSelector(task<_T>); - - _TypeSelectorNoAsync _AsyncOperationKindSelector(...); - - template - struct _Unhat - { - typedef _Type _Value; - }; - - template - struct _Unhat<_Type*> - { - typedef _Type _Value; - }; - - //struct _NonUserType { public: int _Dummy; }; - - template - struct _ValueTypeOrRefType - { - typedef _Unit_type _Value; - }; - - template - struct _ValueTypeOrRefType<_Type, true> - { - typedef _Type _Value; - }; - - template - _Ty _UnwrapAsyncActionWithProgressSelector(ABI::Windows::Foundation::IAsyncActionWithProgress_impl<_Ty>*); - - template - _Ty _UnwrapAsyncActionWithProgressSelector(...); - - template - _Progress _UnwrapAsyncOperationWithProgressProgressSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress_impl<_Ty, _Progress>*); - - template - _Progress _UnwrapAsyncOperationWithProgressProgressSelector(...); - - template - _T2 _ProgressTypeSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>*); - - template - _T1 _ProgressTypeSelector(ABI::Windows::Foundation::IAsyncActionWithProgress<_T1>*); - - template - struct _GetProgressType - { - typedef decltype(_ProgressTypeSelector(stdx::declval<_Type>())) _Value; - }; - - template - _TypeSelectorAsyncOperation _AsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncOperation<_T>*); - - _TypeSelectorAsyncAction _AsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncAction*); - - template - _TypeSelectorAsyncOperationWithProgress _AsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>*); - - template - _TypeSelectorAsyncActionWithProgress _AsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncActionWithProgress<_T>*); - - template - struct _IsIAsyncInfo - { - static const bool _Value = std::is_base_of::_Value>::value || - std::is_same<_TypeSelectorAsyncAction, decltype(details::_AsyncOperationKindSelector(stdx::declval<_Type>()))>::value || - std::is_same<_TypeSelectorAsyncOperation, decltype(details::_AsyncOperationKindSelector(stdx::declval<_Type>()))>::value || - std::is_same<_TypeSelectorAsyncOperationWithProgress, decltype(details::_AsyncOperationKindSelector(stdx::declval<_Type>()))>::value || - std::is_same<_TypeSelectorAsyncActionWithProgress, decltype(details::_AsyncOperationKindSelector(stdx::declval<_Type>()))>::value; - }; - - template <> - struct _IsIAsyncInfo - { - static const bool _Value = false; - }; - - template - _Ty _UnwrapAsyncOperationSelector(ABI::Windows::Foundation::IAsyncOperation_impl<_Ty>*); - - template - _Ty _UnwrapAsyncOperationSelector(...); - - template - _Ty _UnwrapAsyncOperationWithProgressSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress_impl<_Ty, _Progress>*); - - template - _Ty _UnwrapAsyncOperationWithProgressSelector(...); - - // Unwrap functions for asyncOperations - template - auto _GetUnwrappedType(ABI::Windows::Foundation::IAsyncOperation<_Ty>*) -> typename ABI::Windows::Foundation::Internal::GetAbiType*>()))>::type; - - void _GetUnwrappedType(ABI::Windows::Foundation::IAsyncAction*); - - template - auto _GetUnwrappedType(ABI::Windows::Foundation::IAsyncOperationWithProgress<_Ty, _Progress>*) -> typename ABI::Windows::Foundation::Internal::GetAbiType*>()))>::type; - - template - void _GetUnwrappedType(ABI::Windows::Foundation::IAsyncActionWithProgress<_Progress>*); - - template - _T _ReturnAsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncOperation<_T>*); - - void _ReturnAsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncAction*); - - template - _T1 _ReturnAsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>*); - - template - void _ReturnAsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncActionWithProgress<_T>*); - - class _ProgressReporterCtorArgType{}; - - template ::_Value> - struct _TaskTypeTraits - { - typedef typename details::_UnwrapTaskType<_Type>::_Type _TaskRetType; - typedef _TaskRetType _TaskRetType_abi; - typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; - typedef typename details::_NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; - - static const bool _IsAsyncTask = _IsAsync; - static const bool _IsUnwrappedTaskOrAsync = details::_IsUnwrappedAsyncSelector<_AsyncKind>::_Value; - }; - - template - struct _TaskTypeTraits<_Type, true> - { - typedef decltype(_ReturnAsyncOperationKindSelector(stdx::declval<_Type>())) _TaskRetType; - typedef decltype(_GetUnwrappedType(stdx::declval<_Type>())) _TaskRetType_abi; - typedef _TaskRetType _NormalizedTaskRetType; - typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; - - static const bool _IsAsyncTask = true; - static const bool _IsUnwrappedTaskOrAsync = details::_IsUnwrappedAsyncSelector<_AsyncKind>::_Value; - }; - - template auto _IsCallable(_Function _Func, int, int, int) -> decltype(_Func(stdx::declval*>()), std::true_type()) { (void)_Func; return std::true_type(); } - template auto _IsCallable(_Function _Func, int, int, ...) -> decltype(_Func(stdx::declval<_ReturnType*>()), std::true_type()) { (void)_Func; return std::true_type(); } - template auto _IsCallable(_Function _Func, int, ...) -> decltype(_Func(), std::true_type()) { (void)_Func; return std::true_type(); } - template std::false_type _IsCallable(_Function, ...) { return std::false_type(); } - - template <> - struct _TaskTypeTraits - { - typedef void _TaskRetType; - typedef void _TaskRetType_abi; - typedef _TypeSelectorNoAsync _AsyncKind; - typedef _Unit_type _NormalizedTaskRetType; - - static const bool _IsAsyncTask = false; - static const bool _IsUnwrappedTaskOrAsync = false; - }; - - // *************************************************************************** - // Template type traits and helpers for async production APIs: - // - - struct _ZeroArgumentFunctor { }; - struct _OneArgumentFunctor { }; - struct _TwoArgumentFunctor { }; - struct _ThreeArgumentFunctor { }; - - // **************************************** - // CLASS TYPES: - - // mutable functions - // ******************** - // THREE ARGUMENTS: - - // non-void arg: - template - _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3)); - - // non-void arg: - template - _Arg2 _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3)); - - // non-void arg: - template - _Arg3 _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3)); - - template - _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3)); - - template - _ThreeArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3)); - - // ******************** - // TWO ARGUMENTS: - - // non-void arg: - template - _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2)); - - // non-void arg: - template - _Arg2 _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2)); - - // non-void arg: - template - void _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2)); - - template - _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2)); - - template - _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1, _Arg2)); - - // ******************** - // ONE ARGUMENT: - - // non-void arg: - template - _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1)); - - // non-void arg: - template - void _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1)); - - // non-void arg: - template - void _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1)); - - template - _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1)); - - template - _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1)); - - // ******************** - // ZERO ARGUMENT: - - // void arg: - template - void _Arg1ClassHelperThunk(_ReturnType(_Class::*)()); - - // void arg: - template - void _Arg2ClassHelperThunk(_ReturnType(_Class::*)()); - - // void arg: - template - void _Arg3ClassHelperThunk(_ReturnType(_Class::*)()); - - // void arg: - template - _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)()); - - template - _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)()); - - // ******************** - // THREE ARGUMENTS: - - // non-void arg: - template - _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3) const); - - // non-void arg: - template - _Arg2 _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3) const); - - // non-void arg: - template - _Arg3 _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3) const); - - template - _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3) const); - - template - _ThreeArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3) const); - - // ******************** - // TWO ARGUMENTS: - - // non-void arg: - template - _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2) const); - - // non-void arg: - template - _Arg2 _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2) const); - - // non-void arg: - template - void _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2) const); - - template - _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2) const); - - template - _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1, _Arg2) const); - - // ******************** - // ONE ARGUMENT: - - // non-void arg: - template - _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1) const); - - // non-void arg: - template - void _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1) const); - - // non-void arg: - template - void _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1) const); - - template - _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1) const); - - template - _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1) const); - - // ******************** - // ZERO ARGUMENT: - - // void arg: - template - void _Arg1ClassHelperThunk(_ReturnType(_Class::*)() const); - - // void arg: - template - void _Arg2ClassHelperThunk(_ReturnType(_Class::*)() const); - - // void arg: - template - void _Arg3ClassHelperThunk(_ReturnType(_Class::*)() const); - - // void arg: - template - _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)() const); - - template - _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)() const); - - // **************************************** - // POINTER TYPES: - - // ******************** - // THREE ARGUMENTS: - - template - _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2, _Arg3)); - - template - _Arg2 _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2, _Arg3)); - - template - _Arg3 _Arg3PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2, _Arg3)); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2, _Arg3)); - - template - _ThreeArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1, _Arg2, _Arg3)); - - template - _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2, _Arg3)); - - template - _Arg2 _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2, _Arg3)); - - template - _Arg3 _Arg3PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2, _Arg3)); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2, _Arg3)); - - template - _ThreeArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1, _Arg2, _Arg3)); - - template - _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2, _Arg3)); - - template - _Arg2 _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2, _Arg3)); - - template - _Arg3 _Arg3PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2, _Arg3)); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2, _Arg3)); - - template - _ThreeArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1, _Arg2, _Arg3)); - - // ******************** - // TWO ARGUMENTS: - - template - _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); - - template - _Arg2 _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); - - template - void _Arg3PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); - - template - _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1, _Arg2)); - - template - _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); - - template - _Arg2 _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); - - template - void _Arg3PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); - - template - _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1, _Arg2)); - - template - _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); - - template - _Arg2 _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); - - template - void _Arg3PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); - - template - _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1, _Arg2)); - - // ******************** - // ONE ARGUMENT: - - template - _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); - - template - void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); - - template - void _Arg3PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); - - template - _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1)); - - template - _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); - - template - void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); - - template - void _Arg3PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); - - template - _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1)); - - template - _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); - - template - void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); - - template - void _Arg3PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); - - template - _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1)); - - // ******************** - // ZERO ARGUMENT: - - template - void _Arg1PFNHelperThunk(_ReturnType(__cdecl *)()); - - template - void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)()); - - template - void _Arg3PFNHelperThunk(_ReturnType(__cdecl *)()); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)()); - - template - _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)()); - - template - void _Arg1PFNHelperThunk(_ReturnType(__stdcall *)()); - - template - void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)()); - - template - void _Arg3PFNHelperThunk(_ReturnType(__stdcall *)()); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)()); - - template - _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)()); - - template - void _Arg1PFNHelperThunk(_ReturnType(__fastcall *)()); - - template - void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)()); - - template - void _Arg3PFNHelperThunk(_ReturnType(__fastcall *)()); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)()); - - template - _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)()); - - template - struct _FunctorArguments - { - static const size_t _Count = 0; - }; - - template<> - struct _FunctorArguments<_OneArgumentFunctor> - { - static const size_t _Count = 1; - }; - - template<> - struct _FunctorArguments<_TwoArgumentFunctor> - { - static const size_t _Count = 2; - }; - - template<> - struct _FunctorArguments<_ThreeArgumentFunctor> - { - static const size_t _Count = 3; - }; - - template - struct _FunctorTypeTraits - { - typedef decltype(_ArgumentCountHelper(&(_T::operator()))) _ArgumentCountType; - static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; - - typedef decltype(_ReturnTypeClassHelperThunk(&(_T::operator()))) _ReturnType; - typedef decltype(_Arg1ClassHelperThunk(&(_T::operator()))) _Argument1Type; - typedef decltype(_Arg2ClassHelperThunk(&(_T::operator()))) _Argument2Type; - typedef decltype(_Arg3ClassHelperThunk(&(_T::operator()))) _Argument3Type; - }; - - template - struct _FunctorTypeTraits<_T *> - { - typedef decltype(_ArgumentCountHelper(stdx::declval<_T*>())) _ArgumentCountType; - static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; - - typedef decltype(_ReturnTypePFNHelperThunk(stdx::declval<_T*>())) _ReturnType; - typedef decltype(_Arg1PFNHelperThunk(stdx::declval<_T*>())) _Argument1Type; - typedef decltype(_Arg2PFNHelperThunk(stdx::declval<_T*>())) _Argument2Type; - typedef decltype(_Arg3PFNHelperThunk(stdx::declval<_T*>())) _Argument3Type; - }; - - task _To_task(); - - template auto _IsVoidConversionHelper(_Function _Func, int) -> typename decltype(_Func(_To_task()), std::true_type()); - template std::false_type _IsVoidConversionHelper(_Function _Func, ...); - - template std::true_type _VoidIsTaskHelper(task _Arg, int); - template std::false_type _VoidIsTaskHelper(T _Arg, ...); - - template(), 0)), std::true_type>::value, const size_t _Count = _FunctorTypeTraits<_Function>::_ArgumentCount> - struct _FunctionTypeTraits - { - typedef typename _Unhat::_Argument2Type>::_Value _FuncRetType; - static_assert(std::is_same::_Argument1Type, _ExpectedParameterType>::value || - std::is_same::_Argument1Type, task<_ExpectedParameterType>>::value, "incorrect parameter type for the callable object in 'then'; consider _ExpectedParameterType or task<_ExpectedParameterType> (see below)"); - - typedef decltype(_VoidIsTaskHelper(stdx::declval<_FunctorTypeTraits<_Function>::_Argument1Type>(), 0)) _Takes_task; - }; - - //if there is a continuation parameter, then must use void/no return value - template - struct _FunctionTypeTraits<_Function, _ExpectedParameterType, _IsVoidConversion, 1> - { - typedef void _FuncRetType; - static_assert(std::is_same::_Argument1Type, _ExpectedParameterType>::value || - std::is_same::_Argument1Type, task<_ExpectedParameterType>>::value, "incorrect parameter type for the callable object in 'then'; consider _ExpectedParameterType or task<_ExpectedParameterType> (see below)"); - - typedef decltype(_VoidIsTaskHelper(stdx::declval<_FunctorTypeTraits<_Function>::_Argument1Type>(), 0)) _Takes_task; - }; - - template - struct _FunctionTypeTraits<_Function, void, true, 1> - { - typedef void _FuncRetType; - static_assert(std::is_same::_Argument1Type, decltype(_To_task())>::value, "incorrect parameter type for the callable object in 'then'; consider _ExpectedParameterType or task<_ExpectedParameterType> (see below)"); - - typedef decltype(_VoidIsTaskHelper(stdx::declval<_FunctorTypeTraits<_Function>::_Argument1Type>(), 0)) _Takes_task; - }; - - template - struct _FunctionTypeTraits<_Function, void, false, 1> - { - typedef typename _Unhat::_Argument1Type>::_Value _FuncRetType; - - typedef std::false_type _Takes_task; - }; - - template - struct _FunctionTypeTraits<_Function, _ExpectedParameterType, _IsVoidConversion, 0> - { - typedef void _FuncRetType; - - typedef std::false_type _Takes_task; - }; - - template - struct _ContinuationTypeTraits - { - typedef typename task::_FuncRetType>::_TaskRetType_abi> _TaskOfType; - }; - - // _InitFunctorTypeTraits is used to decide whether a task constructed with a lambda should be unwrapped. Depending on how the variable is - // declared, the constructor may or may not perform unwrapping. For eg. - // - // This declaration SHOULD NOT cause unwrapping - // task> t1([]() -> task { - // task t2([]() {}); - // return t2; - // }); - // - // This declaration SHOULD cause unwrapping - // task> t1([]() -> task { - // task t2([]() {}); - // return t2; - // }); - // If the type of the task is the same as the return type of the function, no unwrapping should take place. Else normal rules apply. - template - struct _InitFunctorTypeTraits - { - typedef typename _TaskTypeTraits<_FuncRetType>::_AsyncKind _AsyncKind; - static const bool _IsAsyncTask = _TaskTypeTraits<_FuncRetType>::_IsAsyncTask; - static const bool _IsUnwrappedTaskOrAsync = _TaskTypeTraits<_FuncRetType>::_IsUnwrappedTaskOrAsync; - }; - - template - struct _InitFunctorTypeTraits - { - typedef _TypeSelectorNoAsync _AsyncKind; - static const bool _IsAsyncTask = false; - static const bool _IsUnwrappedTaskOrAsync = false; - }; - /// - /// Helper object used for LWT invocation. - /// - struct _TaskProcThunk - { - _TaskProcThunk(const std::function & _Callback) : - _M_func(_Callback) - { - } - - static void __cdecl _Bridge(void *_PData) - { - _TaskProcThunk *_PThunk = reinterpret_cast<_TaskProcThunk *>(_PData); -#if _MSC_VER >= 1800 - _Holder _ThunkHolder(_PThunk); -#endif - _PThunk->_M_func(); -#if _MSC_VER < 1800 - delete _PThunk; -#endif - } - private: -#if _MSC_VER >= 1800 - // RAII holder - struct _Holder - { - _Holder(_TaskProcThunk * _PThunk) : _M_pThunk(_PThunk) - { - } - - ~_Holder() - { - delete _M_pThunk; - } - - _TaskProcThunk * _M_pThunk; - - private: - _Holder& operator=(const _Holder&); - }; -#endif - std::function _M_func; - _TaskProcThunk& operator=(const _TaskProcThunk&); - }; - - /// - /// Schedule a functor with automatic inlining. Note that this is "fire and forget" scheduling, which cannot be - /// waited on or canceled after scheduling. - /// This schedule method will perform automatic inlining base on . - /// - /// - /// The user functor need to be scheduled. - /// - /// - /// The inlining scheduling policy for current functor. - /// -#if _MSC_VER >= 1800 - typedef Concurrency::details::_TaskInliningMode_t _TaskInliningMode; -#else - typedef Concurrency::details::_TaskInliningMode _TaskInliningMode; -#endif - static void _ScheduleFuncWithAutoInline(const std::function & _Func, _TaskInliningMode _InliningMode) - { -#if _MSC_VER >= 1800 - Concurrency::details::_TaskCollection_t::_RunTask(&_TaskProcThunk::_Bridge, new _TaskProcThunk(_Func), _InliningMode); -#else - Concurrency::details::_StackGuard _Guard; - if (_Guard._ShouldInline(_InliningMode)) - { - _Func(); - } - else - { - Concurrency::details::_CurrentScheduler::_ScheduleTask(reinterpret_cast(&_TaskProcThunk::_Bridge), new _TaskProcThunk(_Func)); - } -#endif - } - class _ContextCallback - { - typedef std::function _CallbackFunction; - - public: - - static _ContextCallback _CaptureCurrent() - { - _ContextCallback _Context; - _Context._Capture(); - return _Context; - } - - ~_ContextCallback() - { - _Reset(); - } - - _ContextCallback(bool _DeferCapture = false) - { - if (_DeferCapture) - { - _M_context._M_captureMethod = _S_captureDeferred; - } - else - { - _M_context._M_pContextCallback = nullptr; - } - } - - // Resolves a context that was created as _S_captureDeferred based on the environment (ancestor, current context). - void _Resolve(bool _CaptureCurrent) - { - if (_M_context._M_captureMethod == _S_captureDeferred) - { - _M_context._M_pContextCallback = nullptr; - - if (_CaptureCurrent) - { - if (_IsCurrentOriginSTA()) - { - _Capture(); - } -#if _UITHREADCTXT_SUPPORT - else - { - // This method will fail if not called from the UI thread. - HRESULT _Hr = CaptureUiThreadContext(&_M_context._M_pContextCallback); - if (FAILED(_Hr)) - { - _M_context._M_pContextCallback = nullptr; - } - } -#endif // _UITHREADCTXT_SUPPORT - } - } - } - - void _Capture() - { - HRESULT _Hr = CoGetObjectContext(IID_IContextCallback, reinterpret_cast(&_M_context._M_pContextCallback)); - if (FAILED(_Hr)) - { - _M_context._M_pContextCallback = nullptr; - } - } - - _ContextCallback(const _ContextCallback& _Src) - { - _Assign(_Src._M_context._M_pContextCallback); - } - - _ContextCallback(_ContextCallback&& _Src) - { - _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; - _Src._M_context._M_pContextCallback = nullptr; - } - - _ContextCallback& operator=(const _ContextCallback& _Src) - { - if (this != &_Src) - { - _Reset(); - _Assign(_Src._M_context._M_pContextCallback); - } - return *this; - } - - _ContextCallback& operator=(_ContextCallback&& _Src) - { - if (this != &_Src) - { - _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; - _Src._M_context._M_pContextCallback = nullptr; - } - return *this; - } - - bool _HasCapturedContext() const - { - _CONCRT_ASSERT(_M_context._M_captureMethod != _S_captureDeferred); - return (_M_context._M_pContextCallback != nullptr); - } - - HRESULT _CallInContext(_CallbackFunction _Func) const - { - if (!_HasCapturedContext()) - { - _Func(); - } - else - { - ComCallData callData; - ZeroMemory(&callData, sizeof(callData)); - callData.pUserDefined = reinterpret_cast(&_Func); - - HRESULT _Hr = _M_context._M_pContextCallback->ContextCallback(&_Bridge, &callData, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr); - if (FAILED(_Hr)) - { - return _Hr; - } - } - return S_OK; - } - - bool operator==(const _ContextCallback& _Rhs) const - { - return (_M_context._M_pContextCallback == _Rhs._M_context._M_pContextCallback); - } - - bool operator!=(const _ContextCallback& _Rhs) const - { - return !(operator==(_Rhs)); - } - - private: - - void _Reset() - { - if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) - { - _M_context._M_pContextCallback->Release(); - } - } - - void _Assign(IContextCallback *_PContextCallback) - { - _M_context._M_pContextCallback = _PContextCallback; - if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) - { - _M_context._M_pContextCallback->AddRef(); - } - } - - static HRESULT __stdcall _Bridge(ComCallData *_PParam) - { - _CallbackFunction *pFunc = reinterpret_cast<_CallbackFunction *>(_PParam->pUserDefined); - return (*pFunc)(); - } - - // Returns the origin information for the caller (runtime / Windows Runtime apartment as far as task continuations need know) - static bool _IsCurrentOriginSTA() - { - APTTYPE _AptType; - APTTYPEQUALIFIER _AptTypeQualifier; - - HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); - if (SUCCEEDED(hr)) - { - // We determine the origin of a task continuation by looking at where .then is called, so we can tell whether - // to need to marshal the continuation back to the originating apartment. If an STA thread is in executing in - // a neutral aparment when it schedules a continuation, we will not marshal continuations back to the STA, - // since variables used within a neutral apartment are expected to be apartment neutral. - switch (_AptType) - { - case APTTYPE_MAINSTA: - case APTTYPE_STA: - return true; - default: - break; - } - } - return false; - } - - union - { - IContextCallback *_M_pContextCallback; - size_t _M_captureMethod; - } _M_context; - - static const size_t _S_captureDeferred = 1; - }; - -#if _MSC_VER >= 1800 - template - struct _ResultHolder - { - void Set(const _Type& _type) - { - _Result = _type; - } - - _Type Get() - { - return _Result; - } - - _Type _Result; - }; - - template - struct _ResultHolder<_Type*> - { - void Set(_Type* const & _type) - { - _M_Result = _type; - } - - _Type* Get() - { - return _M_Result.Get(); - } - private: - // ::Platform::Agile handle specialization of all hats - // including ::Platform::String and ::Platform::Array - Agile<_Type*> _M_Result; - }; - - // - // The below are for composability with tasks auto-created from when_any / when_all / && / || constructs. - // - template - struct _ResultHolder> - { - void Set(const std::vector<_Type*>& _type) - { - _Result.reserve(_type.size()); - - for (auto _PTask = _type.begin(); _PTask != _type.end(); ++_PTask) - { - _Result.emplace_back(*_PTask); - } - } - - std::vector<_Type*> Get() - { - // Return vectory with the objects that are marshaled in the proper appartment - std::vector<_Type*> _Return; - _Return.reserve(_Result.size()); - - for (auto _PTask = _Result.begin(); _PTask != _Result.end(); ++_PTask) - { - _Return.push_back(_PTask->Get()); // Agile will marshal the object to appropriate appartment if neccessary - } - - return _Return; - } - - std::vector< Agile<_Type*> > _Result; - }; - - template - struct _ResultHolder > - { - void Set(const std::pair<_Type*, size_t>& _type) - { - _M_Result = _type; - } - - std::pair<_Type*, size_t> Get() - { - return std::make_pair(_M_Result.first, _M_Result.second); - } - private: - std::pair, size_t> _M_Result; - }; -#else - template - struct _ResultContext - { - static _ContextCallback _GetContext(bool /* _RuntimeAggregate */) - { - return _ContextCallback(); - } - - static _Type _GetValue(_Type _ObjInCtx, const _ContextCallback & /* _Ctx */, bool /* _RuntimeAggregate */) - { - return _ObjInCtx; - } - }; - - template::value> - struct _MarshalHelper - { - }; - template - struct _MarshalHelper<_Type, N, true> - { - static _Type* _Perform(_Type(&_ObjInCtx)[N], const _ContextCallback& _Ctx) - { - static_assert(__is_valid_winrt_type(_Type*), "must be a WinRT array compatible type"); - if (_ObjInCtx == nullptr) - { - return nullptr; - } - - HRESULT _Hr; - IStream * _PStream; - _Ctx._CallInContext([&]() -> HRESULT { - // It isn't safe to simply reinterpret_cast a hat type to IUnknown* because some types do not have a real vtable ptr. - // Instead, we could to create a property value to make it "grow" the vtable ptr but instead primitives are not marshalled. - - IUnknown * _PUnk = winrt_array_type::create(_ObjInCtx, N); - _Hr = CoMarshalInterThreadInterfaceInStream(winrt_type<_Type>::getuuid(), _PUnk, &_PStream); - return S_OK; - }); - - // With an APPX manifest, this call should never fail. - _CONCRT_ASSERT(SUCCEEDED(_Hr)); - - _Type* _Proxy; - // - // Cannot use IID_PPV_ARGS with ^ types. - // - _Hr = CoGetInterfaceAndReleaseStream(_PStream, winrt_type<_Type>::getuuid(), reinterpret_cast(&_Proxy)); - if (FAILED(_Hr)) - { - throw std::make_exception_ptr(_Hr); - } - return _Proxy; - } - }; - template - struct _MarshalHelper<_Type, 0, false> - { - static _Type* _Perform(_Type* _ObjInCtx, const _ContextCallback& _Ctx) - { - static_assert(std::is_base_of::value || __is_valid_winrt_type(_Type), "must be a COM or WinRT type"); - if (_ObjInCtx == nullptr) - { - return nullptr; - } - - HRESULT _Hr; - IStream * _PStream; - _Ctx._CallInContext([&]() -> HRESULT { - // It isn't safe to simply reinterpret_cast a hat type to IUnknown* because some types do not have a real vtable ptr. - // Instead, we could to create a property value to make it "grow" the vtable ptr but instead primitives are not marshalled. - - IUnknown * _PUnk = winrt_type<_Type>::create(_ObjInCtx); - _Hr = CoMarshalInterThreadInterfaceInStream(winrt_type<_Type>::getuuid(), _PUnk, &_PStream); - return S_OK; - }); - - // With an APPX manifest, this call should never fail. - _CONCRT_ASSERT(SUCCEEDED(_Hr)); - - _Type* _Proxy; - // - // Cannot use IID_PPV_ARGS with ^ types. - // - _Hr = CoGetInterfaceAndReleaseStream(_PStream, winrt_type<_Type>::getuuid(), reinterpret_cast(&_Proxy)); - if (FAILED(_Hr)) - { - throw std::make_exception_ptr(_Hr); - } - return _Proxy; - } - }; - - // Arrays must be converted to IPropertyValue objects. - - template<> - struct _MarshalHelper - { - static HSTRING _Perform(HSTRING _ObjInCtx, const _ContextCallback& _Ctx) - { - return _ObjInCtx; - } - }; - - template - _Type* _Marshal(_Type* _ObjInCtx, const _ContextCallback& _Ctx) - { - return _MarshalHelper<_Type>::_Perform(_ObjInCtx, _Ctx); - } - - template - struct _InContext - { - static _Type _Get(_Type _ObjInCtx, const _ContextCallback& _Ctx) - { - return _ObjInCtx; - } - }; - - template - struct _InContext<_Type*> - { - static _Type* _Get(_Type* _ObjInCtx, const _ContextCallback& _Ctx) - { - _ContextCallback _CurrentContext = _ContextCallback::_CaptureCurrent(); - if (!_Ctx._HasCapturedContext() || _Ctx == _CurrentContext) - { - return _ObjInCtx; - } - - // - // The object is from another apartment. If it's marshalable, do so. - // - return _Marshal<_Type>(_ObjInCtx, _Ctx); - } - }; - - template - struct _ResultContext<_Type*> - { - static _Type* _GetValue(_Type* _ObjInCtx, const _ContextCallback& _Ctx, bool /* _RuntimeAggregate */) - { - return _InContext<_Type*>::_Get(_ObjInCtx, _Ctx); - } - - static _ContextCallback _GetContext(bool /* _RuntimeAggregate */) - { - return _ContextCallback::_CaptureCurrent(); - } - }; - - // - // The below are for composability with tasks auto-created from when_any / when_all / && / || constructs. - // - template - struct _ResultContext> - { - static std::vector<_Type*> _GetValue(std::vector<_Type*> _ObjInCtx, const _ContextCallback& _Ctx, bool _RuntimeAggregate) - { - if (!_RuntimeAggregate) - { - return _ObjInCtx; - } - - _ContextCallback _CurrentContext = _ContextCallback::_CaptureCurrent(); - if (!_Ctx._HasCapturedContext() || _Ctx == _CurrentContext) - { - return _ObjInCtx; - } - - for (auto _It = _ObjInCtx.begin(); _It != _ObjInCtx.end(); ++_It) - { - *_It = _Marshal<_Type>(*_It, _Ctx); - } - - return _ObjInCtx; - } - - static _ContextCallback _GetContext(bool _RuntimeAggregate) - { - if (!_RuntimeAggregate) - { - return _ContextCallback(); - } - else - { - return _ContextCallback::_CaptureCurrent(); - } - } - }; - - template - struct _ResultContext> - { - static std::pair<_Type*, size_t> _GetValue(std::pair<_Type*, size_t> _ObjInCtx, const _ContextCallback& _Ctx, bool _RuntimeAggregate) - { - if (!_RuntimeAggregate) - { - return _ObjInCtx; - } - - _ContextCallback _CurrentContext = _ContextCallback::_CaptureCurrent(); - if (!_Ctx._HasCapturedContext() || _Ctx == _CurrentContext) - { - return _ObjInCtx; - } - - return std::pair<_Type*, size_t>(_Marshal<_Type>(_ObjInCtx.first, _Ctx), _ObjInCtx.second); - } - - static _ContextCallback _GetContext(bool _RuntimeAggregate) - { - if (!_RuntimeAggregate) - { - return _ContextCallback(); - } - else - { - return _ContextCallback::_CaptureCurrent(); - } - } - }; -#endif - // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. - // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception - // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. - struct _ExceptionHolder - { -#if _MSC_VER >= 1800 - private: - void ReportUnhandledError() - { - if (_M_winRTException != nullptr) - { - throw _M_winRTException.Get(); - } - } - public: - explicit _ExceptionHolder(const std::exception_ptr& _E, const _TaskCreationCallstack &_stackTrace) : - _M_exceptionObserved(0), _M_stdException(_E), _M_stackTrace(_stackTrace) - { - } - - explicit _ExceptionHolder(IRestrictedErrorInfo*& _E, const _TaskCreationCallstack &_stackTrace) : - _M_exceptionObserved(0), _M_winRTException(_E), _M_stackTrace(_stackTrace) - { - } -#else - explicit _ExceptionHolder(const std::exception_ptr& _E, void* _SourceAddressHint) : - _M_exceptionObserved(0), _M_stdException(_E), _M_disassembleMe(_SourceAddressHint) - { - } - - explicit _ExceptionHolder(IRestrictedErrorInfo*& _E, void* _SourceAddressHint) : - _M_exceptionObserved(0), _M_disassembleMe(_SourceAddressHint), _M_winRTException(_E) - { - } -#endif - __declspec(noinline) - ~_ExceptionHolder() - { - if (_M_exceptionObserved == 0) - { -#if _MSC_VER >= 1800 - // If you are trapped here, it means an exception thrown in task chain didn't get handled. - // Please add task-based continuation to handle all exceptions coming from tasks. - // this->_M_stackTrace keeps the creation callstack of the task generates this exception. - _REPORT_PPLTASK_UNOBSERVED_EXCEPTION(); -#else - // Disassemble at this->_M_disassembleMe to get to the source location right after either the creation of the task (constructor - // or then method) that encountered this exception, or the set_exception call for a task_completion_event. - Concurrency::details::_ReportUnobservedException(); -#endif - } - } - - void _RethrowUserException() - { - if (_M_exceptionObserved == 0) - { -#if _MSC_VER >= 1800 - Concurrency::details::atomic_exchange(_M_exceptionObserved, 1l); -#else - _InterlockedExchange(&_M_exceptionObserved, 1); -#endif - } - - if (_M_winRTException != nullptr) - { - throw _M_winRTException.Get(); - } - std::rethrow_exception(_M_stdException); - } - - // A variable that remembers if this exception was every rethrown into user code (and hence handled by the user). Exceptions that - // are unobserved when the exception holder is destructed will terminate the process. -#if _MSC_VER >= 1800 - Concurrency::details::atomic_long _M_exceptionObserved; -#else - long volatile _M_exceptionObserved; -#endif - - // Either _M_stdException or _M_winRTException is populated based on the type of exception encountered. - std::exception_ptr _M_stdException; - Microsoft::WRL::ComPtr _M_winRTException; - - // Disassembling this value will point to a source instruction right after a call instruction. If the call is to create_task, - // a task constructor or the then method, the task created by that method is the one that encountered this exception. If the call - // is to task_completion_event::set_exception, the set_exception method was the source of the exception. - // DO NOT REMOVE THIS VARIABLE. It is extremely helpful for debugging. -#if _MSC_VER >= 1800 - _TaskCreationCallstack _M_stackTrace; -#else - void* _M_disassembleMe; -#endif - }; - -#ifndef RUNTIMECLASS_Concurrency_winrt_details__AsyncInfoImpl_DEFINED -#define RUNTIMECLASS_Concurrency_winrt_details__AsyncInfoImpl_DEFINED - extern const __declspec(selectany) WCHAR RuntimeClass_Concurrency_winrt_details__AsyncInfoImpl[] = L"Concurrency_winrt.details._AsyncInfoImpl"; -#endif - - /// - /// Base converter class for converting asynchronous interfaces to IAsyncOperation - /// - template - struct _AsyncInfoImpl abstract : public Microsoft::WRL::RuntimeClass< - Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::WinRt>, - Microsoft::WRL::Implements>> - { - InspectableClass(RuntimeClass_Concurrency_winrt_details__AsyncInfoImpl, BaseTrust) - public: - // The async action, action with progress or operation with progress that this stub forwards to. -#if _MSC_VER >= 1800 - Agile<_AsyncOperationType> _M_asyncInfo; -#else - Microsoft::WRL::ComPtr<_AsyncOperationType> _M_asyncInfo; - // The context in which this async info is valid - may be different from the context where the completion handler runs, - // and may require marshalling before it is used. - _ContextCallback _M_asyncInfoContext; -#endif - - Microsoft::WRL::ComPtr<_CompletionHandlerType> _M_CompletedHandler; - - _AsyncInfoImpl(_AsyncOperationType* _AsyncInfo) : _M_asyncInfo(_AsyncInfo) -#if _MSC_VER < 1800 - , _M_asyncInfoContext(_ContextCallback::_CaptureCurrent()) -#endif - {} - - public: - virtual HRESULT OnStart() { return S_OK; } - virtual void OnCancel() { - Microsoft::WRL::ComPtr pAsyncInfo; - HRESULT hr; -#if _MSC_VER >= 1800 - if (SUCCEEDED(hr = _M_asyncInfo.Get()->QueryInterface(pAsyncInfo.GetAddressOf()))) -#else - if (SUCCEEDED(hr = _M_asyncInfo.As(&pAsyncInfo))) -#endif - pAsyncInfo->Cancel(); - else - throw std::make_exception_ptr(hr); - } - virtual void OnClose() { - Microsoft::WRL::ComPtr pAsyncInfo; - HRESULT hr; -#if _MSC_VER >= 1800 - if (SUCCEEDED(hr = _M_asyncInfo.Get()->QueryInterface(pAsyncInfo.GetAddressOf()))) -#else - if (SUCCEEDED(hr = _M_asyncInfo.As(&pAsyncInfo))) -#endif - pAsyncInfo->Close(); - else - throw std::make_exception_ptr(hr); - } - - virtual STDMETHODIMP get_ErrorCode(HRESULT* errorCode) - { - Microsoft::WRL::ComPtr pAsyncInfo; - HRESULT hr; -#if _MSC_VER >= 1800 - if (SUCCEEDED(hr = _M_asyncInfo.Get()->QueryInterface(pAsyncInfo.GetAddressOf()))) -#else - if (SUCCEEDED(hr = _M_asyncInfo.As(&pAsyncInfo))) -#endif - return pAsyncInfo->get_ErrorCode(errorCode); - return hr; - } - - virtual STDMETHODIMP get_Id(UINT* id) - { - Microsoft::WRL::ComPtr pAsyncInfo; - HRESULT hr; -#if _MSC_VER >= 1800 - if (SUCCEEDED(hr = _M_asyncInfo.Get()->QueryInterface(pAsyncInfo.GetAddressOf()))) -#else - if (SUCCEEDED(hr = _M_asyncInfo.As(&pAsyncInfo))) -#endif - return pAsyncInfo->get_Id(id); - return hr; - } - - virtual STDMETHODIMP get_Status(ABI::Windows::Foundation::AsyncStatus *status) - { - Microsoft::WRL::ComPtr pAsyncInfo; - HRESULT hr; -#if _MSC_VER >= 1800 - if (SUCCEEDED(hr = _M_asyncInfo.Get()->QueryInterface(pAsyncInfo.GetAddressOf()))) -#else - if (SUCCEEDED(hr = _M_asyncInfo.As(&pAsyncInfo))) -#endif - return pAsyncInfo->get_Status(status); - return hr; - } - - virtual STDMETHODIMP GetResults(_Result_abi*) { throw std::runtime_error("derived class must implement"); } - - virtual STDMETHODIMP get_Completed(_CompletionHandlerType** handler) - { - if (!handler) return E_POINTER; - _M_CompletedHandler.CopyTo(handler); - return S_OK; - } - - virtual STDMETHODIMP put_Completed(_CompletionHandlerType* value) - { - _M_CompletedHandler = value; - Microsoft::WRL::ComPtr<_CompletionHandlerType> handler = Microsoft::WRL::Callback<_CompletionHandlerType>([&](_AsyncOperationType*, ABI::Windows::Foundation::AsyncStatus status) -> HRESULT { -#if _MSC_VER < 1800 - // Update the saved _M_asyncInfo with a proxy valid in the current context if required. Some Windows APIs return an IAsyncInfo - // that is only valid for the thread that called the API to retrieve. Since this completion handler can run on any thread, we - // need to ensure that the async info is valid in the current apartment. _M_asyncInfo will be accessed via calls to 'this' inside - // _AsyncInit. - _M_asyncInfo = _ResultContext<_AsyncOperationType*>::_GetValue(_M_asyncInfo.Get(), _M_asyncInfoContext, false); -#endif - return _M_CompletedHandler->Invoke(_M_asyncInfo.Get(), status); - }); -#if _MSC_VER >= 1800 - return _M_asyncInfo.Get()->put_Completed(handler.Get()); -#else - return _M_asyncInfo->put_Completed(handler.Get()); -#endif - } - }; - - extern const __declspec(selectany) WCHAR RuntimeClass_IAsyncOperationToAsyncOperationConverter[] = L"_IAsyncOperationToAsyncOperationConverter"; - - /// - /// Class _IAsyncOperationToAsyncOperationConverter is used to convert an instance of IAsyncOperationWithProgress into IAsyncOperation - /// - template - struct _IAsyncOperationToAsyncOperationConverter : - _AsyncInfoImpl, - ABI::Windows::Foundation::IAsyncOperationCompletedHandler<_Result>, - typename ABI::Windows::Foundation::Internal::GetAbiType*>()))>::type> - { - typedef typename ABI::Windows::Foundation::Internal::GetAbiType*>()))>::type _Result_abi; - - InspectableClass(RuntimeClass_IAsyncOperationToAsyncOperationConverter, BaseTrust) - public: - _IAsyncOperationToAsyncOperationConverter(ABI::Windows::Foundation::IAsyncOperation<_Result>* _Operation) : - _AsyncInfoImpl, - ABI::Windows::Foundation::IAsyncOperationCompletedHandler<_Result>, - _Result_abi>(_Operation) {} - public: - virtual STDMETHODIMP GetResults(_Result_abi* results) override { - if (!results) return E_POINTER; -#if _MSC_VER >= 1800 - return _M_asyncInfo.Get()->GetResults(results); -#else - return _M_asyncInfo->GetResults(results); -#endif - } - }; - - extern const __declspec(selectany) WCHAR RuntimeClass_IAsyncOperationWithProgressToAsyncOperationConverter[] = L"_IAsyncOperationWithProgressToAsyncOperationConverter"; - - /// - /// Class _IAsyncOperationWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncOperationWithProgress into IAsyncOperation - /// - template - struct _IAsyncOperationWithProgressToAsyncOperationConverter : - _AsyncInfoImpl, - ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<_Result, _Progress>, - typename ABI::Windows::Foundation::Internal::GetAbiType*>()))>::type> - { - typedef typename ABI::Windows::Foundation::Internal::GetAbiType*>()))>::type _Result_abi; - - InspectableClass(RuntimeClass_IAsyncOperationWithProgressToAsyncOperationConverter, BaseTrust) - public: - _IAsyncOperationWithProgressToAsyncOperationConverter(ABI::Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress>* _Operation) : - _AsyncInfoImpl, - ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<_Result, _Progress>, - _Result_abi>(_Operation) {} - public: - virtual STDMETHODIMP GetResults(_Result_abi* results) override { - if (!results) return E_POINTER; -#if _MSC_VER >= 1800 - return _M_asyncInfo.Get()->GetResults(results); -#else - return _M_asyncInfo->GetResults(results); -#endif - } - }; - - extern const __declspec(selectany) WCHAR RuntimeClass_IAsyncActionToAsyncOperationConverter[] = L"_IAsyncActionToAsyncOperationConverter"; - - /// - /// Class _IAsyncActionToAsyncOperationConverter is used to convert an instance of IAsyncAction into IAsyncOperation<_Unit_type> - /// - struct _IAsyncActionToAsyncOperationConverter : - _AsyncInfoImpl - { - InspectableClass(RuntimeClass_IAsyncActionToAsyncOperationConverter, BaseTrust) - public: - _IAsyncActionToAsyncOperationConverter(ABI::Windows::Foundation::IAsyncAction* _Operation) : - _AsyncInfoImpl(_Operation) {} - - public: - virtual STDMETHODIMP GetResults(details::_Unit_type* results) - { - if (!results) return E_POINTER; - // Invoke GetResults on the IAsyncAction to allow exceptions to be thrown to higher layers before returning a dummy value. -#if _MSC_VER >= 1800 - HRESULT hr = _M_asyncInfo.Get()->GetResults(); -#else - HRESULT hr = _M_asyncInfo->GetResults(); -#endif - if (SUCCEEDED(hr)) *results = _Unit_type(); - return hr; - } - }; - - extern const __declspec(selectany) WCHAR RuntimeClass_IAsyncActionWithProgressToAsyncOperationConverter[] = L"_IAsyncActionWithProgressToAsyncOperationConverter"; - - /// - /// Class _IAsyncActionWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncActionWithProgress into IAsyncOperation<_Unit_type> - /// - template - struct _IAsyncActionWithProgressToAsyncOperationConverter : - _AsyncInfoImpl, - ABI::Windows::Foundation::IAsyncActionWithProgressCompletedHandler<_Progress>, - _Unit_type> - { - InspectableClass(RuntimeClass_IAsyncActionWithProgressToAsyncOperationConverter, BaseTrust) - public: - _IAsyncActionWithProgressToAsyncOperationConverter(ABI::Windows::Foundation::IAsyncActionWithProgress<_Progress>* _Action) : - _AsyncInfoImpl, - ABI::Windows::Foundation::IAsyncActionWithProgressCompletedHandler<_Progress>, - _Unit_type>(_Action) {} - public: - virtual STDMETHODIMP GetResults(_Unit_type* results) override - { - if (!results) return E_POINTER; - // Invoke GetResults on the IAsyncActionWithProgress to allow exceptions to be thrown before returning a dummy value. -#if _MSC_VER >= 1800 - HRESULT hr = _M_asyncInfo.Get()->GetResults(); -#else - HRESULT hr = _M_asyncInfo->GetResults(); -#endif - if (SUCCEEDED(hr)) *results = _Unit_type(); - return hr; - } - }; -} - -/// -/// The task_continuation_context class allows you to specify where you would like a continuation to be executed. -/// It is only useful to use this class from a Windows Store app. For non-Windows Store apps, the task continuation's -/// execution context is determined by the runtime, and not configurable. -/// -/// -/**/ -class task_continuation_context : public details::_ContextCallback -{ -public: - - /// - /// Creates the default task continuation context. - /// - /// - /// The default continuation context. - /// - /// - /// The default context is used if you don't specifiy a continuation context when you call the then method. In Windows - /// applications for Windows 7 and below, as well as desktop applications on Windows 8 and higher, the runtime determines where - /// task continuations will execute. However, in a Windows Store app, the default continuation context for a continuation on an - /// apartment aware task is the apartment where then is invoked. - /// An apartment aware task is a task that unwraps a Windows Runtime IAsyncInfo interface, or a task that is descended from such - /// a task. Therefore, if you schedule a continuation on an apartment aware task in a Windows Runtime STA, the continuation will execute in - /// that STA. - /// A continuation on a non-apartment aware task will execute in a context the Runtime chooses. - /// - /**/ - static task_continuation_context use_default() - { - // The callback context is created with the context set to CaptureDeferred and resolved when it is used in .then() - return task_continuation_context(true); // sets it to deferred, is resolved in the constructor of _ContinuationTaskHandle - } - - /// - /// Creates a task continuation context which allows the Runtime to choose the execution context for a continuation. - /// - /// - /// A task continuation context that represents an arbitrary location. - /// - /// - /// When this continuation context is used the continuation will execute in a context the runtime chooses even if the antecedent task - /// is apartment aware. - /// use_arbitrary can be used to turn off the default behavior for a continuation on an apartment - /// aware task created in an STA. - /// This method is only available to Windows Store apps. - /// - /**/ - static task_continuation_context use_arbitrary() - { - task_continuation_context _Arbitrary(true); - _Arbitrary._Resolve(false); - return _Arbitrary; - } - - /// - /// Returns a task continuation context object that represents the current execution context. - /// - /// - /// The current execution context. - /// - /// - /// This method captures the caller's Windows Runtime context so that continuations can be executed in the right apartment. - /// The value returned by use_current can be used to indicate to the Runtime that the continuation should execute in - /// the captured context (STA vs MTA) regardless of whether or not the antecedent task is apartment aware. An apartment aware task is - /// a task that unwraps a Windows Runtime IAsyncInfo interface, or a task that is descended from such a task. - /// This method is only available to Windows Store apps. - /// - /**/ - static task_continuation_context use_current() - { - task_continuation_context _Current(true); - _Current._Resolve(true); - return _Current; - } - -private: - - task_continuation_context(bool _DeferCapture = false) : details::_ContextCallback(_DeferCapture) - { - } -}; - -#if _MSC_VER >= 1800 -class task_options; -namespace details -{ - struct _Internal_task_options - { - bool _M_hasPresetCreationCallstack; - _TaskCreationCallstack _M_presetCreationCallstack; - - void _set_creation_callstack(const _TaskCreationCallstack &_callstack) - { - _M_hasPresetCreationCallstack = true; - _M_presetCreationCallstack = _callstack; - } - _Internal_task_options() - { - _M_hasPresetCreationCallstack = false; - } - }; - - inline _Internal_task_options &_get_internal_task_options(task_options &options); - inline const _Internal_task_options &_get_internal_task_options(const task_options &options); -} -/// -/// Represents the allowed options for creating a task -/// -class task_options -{ -public: - - - /// - /// Default list of task creation options - /// - task_options() - : _M_Scheduler(Concurrency::get_ambient_scheduler()), - _M_CancellationToken(Concurrency::cancellation_token::none()), - _M_ContinuationContext(task_continuation_context::use_default()), - _M_HasCancellationToken(false), - _M_HasScheduler(false) - { - } - - /// - /// Task option that specify a cancellation token - /// - task_options(Concurrency::cancellation_token _Token) - : _M_Scheduler(Concurrency::get_ambient_scheduler()), - _M_CancellationToken(_Token), - _M_ContinuationContext(task_continuation_context::use_default()), - _M_HasCancellationToken(true), - _M_HasScheduler(false) - { - } - - /// - /// Task option that specify a continuation context. This is valid only for continuations (then) - /// - task_options(task_continuation_context _ContinuationContext) - : _M_Scheduler(Concurrency::get_ambient_scheduler()), - _M_CancellationToken(Concurrency::cancellation_token::none()), - _M_ContinuationContext(_ContinuationContext), - _M_HasCancellationToken(false), - _M_HasScheduler(false) - { - } - - /// - /// Task option that specify a cancellation token and a continuation context. This is valid only for continuations (then) - /// - task_options(Concurrency::cancellation_token _Token, task_continuation_context _ContinuationContext) - : _M_Scheduler(Concurrency::get_ambient_scheduler()), - _M_CancellationToken(_Token), - _M_ContinuationContext(_ContinuationContext), - _M_HasCancellationToken(false), - _M_HasScheduler(false) - { - } - - /// - /// Task option that specify a scheduler with shared lifetime - /// - template - task_options(std::shared_ptr<_SchedType> _Scheduler) - : _M_Scheduler(std::move(_Scheduler)), - _M_CancellationToken(cancellation_token::none()), - _M_ContinuationContext(task_continuation_context::use_default()), - _M_HasCancellationToken(false), - _M_HasScheduler(true) - { - } - - /// - /// Task option that specify a scheduler reference - /// - task_options(Concurrency::scheduler_interface& _Scheduler) - : _M_Scheduler(&_Scheduler), - _M_CancellationToken(Concurrency::cancellation_token::none()), - _M_ContinuationContext(task_continuation_context::use_default()), - _M_HasCancellationToken(false), - _M_HasScheduler(true) - { - } - - /// - /// Task option that specify a scheduler - /// - task_options(Concurrency::scheduler_ptr _Scheduler) - : _M_Scheduler(std::move(_Scheduler)), - _M_CancellationToken(Concurrency::cancellation_token::none()), - _M_ContinuationContext(task_continuation_context::use_default()), - _M_HasCancellationToken(false), - _M_HasScheduler(true) - { - } - - /// - /// Task option copy constructor - /// - task_options(const task_options& _TaskOptions) - : _M_Scheduler(_TaskOptions.get_scheduler()), - _M_CancellationToken(_TaskOptions.get_cancellation_token()), - _M_ContinuationContext(_TaskOptions.get_continuation_context()), - _M_HasCancellationToken(_TaskOptions.has_cancellation_token()), - _M_HasScheduler(_TaskOptions.has_scheduler()) - { - } - - /// - /// Sets the given token in the options - /// - void set_cancellation_token(Concurrency::cancellation_token _Token) - { - _M_CancellationToken = _Token; - _M_HasCancellationToken = true; - } - - /// - /// Sets the given continuation context in the options - /// - void set_continuation_context(task_continuation_context _ContinuationContext) - { - _M_ContinuationContext = _ContinuationContext; - } - - /// - /// Indicates whether a cancellation token was specified by the user - /// - bool has_cancellation_token() const - { - return _M_HasCancellationToken; - } - - /// - /// Returns the cancellation token - /// - Concurrency::cancellation_token get_cancellation_token() const - { - return _M_CancellationToken; - } - - /// - /// Returns the continuation context - /// - task_continuation_context get_continuation_context() const - { - return _M_ContinuationContext; - } - - /// - /// Indicates whether a scheduler n was specified by the user - /// - bool has_scheduler() const - { - return _M_HasScheduler; - } - - /// - /// Returns the scheduler - /// - Concurrency::scheduler_ptr get_scheduler() const - { - return _M_Scheduler; - } - -private: - - task_options const& operator=(task_options const& _Right); - friend details::_Internal_task_options &details::_get_internal_task_options(task_options &); - friend const details::_Internal_task_options &details::_get_internal_task_options(const task_options &); - - Concurrency::scheduler_ptr _M_Scheduler; - Concurrency::cancellation_token _M_CancellationToken; - task_continuation_context _M_ContinuationContext; - details::_Internal_task_options _M_InternalTaskOptions; - bool _M_HasCancellationToken; - bool _M_HasScheduler; -}; -#endif - -namespace details -{ -#if _MSC_VER >= 1800 - inline _Internal_task_options & _get_internal_task_options(task_options &options) - { - return options._M_InternalTaskOptions; - } - inline const _Internal_task_options & _get_internal_task_options(const task_options &options) - { - return options._M_InternalTaskOptions; - } -#endif - struct _Task_impl_base; - template struct _Task_impl; - - template - struct _Task_ptr - { - typedef std::shared_ptr<_Task_impl<_ReturnType>> _Type; -#if _MSC_VER >= 1800 - static _Type _Make(Concurrency::details::_CancellationTokenState * _Ct, Concurrency::scheduler_ptr _Scheduler_arg) { return std::make_shared<_Task_impl<_ReturnType>>(_Ct, _Scheduler_arg); } -#else - static _Type _Make(Concurrency::details::_CancellationTokenState * _Ct) { return std::make_shared<_Task_impl<_ReturnType>>(_Ct); } -#endif - }; -#if _MSC_VER >= 1800 - typedef Concurrency::details::_TaskCollection_t::_TaskProcHandle_t _UnrealizedChore_t; - typedef _UnrealizedChore_t _UnrealizedChore; - typedef Concurrency::extensibility::scoped_critical_section_t scoped_lock; - typedef Concurrency::extensibility::critical_section_t critical_section; - typedef Concurrency::details::atomic_size_t atomic_size_t; -#else - typedef Concurrency::details::_UnrealizedChore _UnrealizedChore; - typedef Concurrency::critical_section::scoped_lock scoped_lock; - typedef Concurrency::critical_section critical_section; - typedef volatile size_t atomic_size_t; -#endif - typedef std::shared_ptr<_Task_impl_base> _Task_ptr_base; - // The weak-typed base task handler for continuation tasks. - struct _ContinuationTaskHandleBase : _UnrealizedChore - { - _ContinuationTaskHandleBase * _M_next; - task_continuation_context _M_continuationContext; - bool _M_isTaskBasedContinuation; - - // This field gives inlining scheduling policy for current chore. - _TaskInliningMode _M_inliningMode; - - virtual _Task_ptr_base _GetTaskImplBase() const = 0; - - _ContinuationTaskHandleBase() : - _M_next(nullptr), _M_isTaskBasedContinuation(false), _M_continuationContext(task_continuation_context::use_default()), _M_inliningMode(Concurrency::details::_NoInline) - { - } - virtual ~_ContinuationTaskHandleBase() {} - }; -#if _MSC_VER >= 1800 -#if _PPLTASK_ASYNC_LOGGING - // GUID used for identifying causality logs from PPLTask - const ::Platform::Guid _PPLTaskCausalityPlatformID(0x7A76B220, 0xA758, 0x4E6E, 0xB0, 0xE0, 0xD7, 0xC6, 0xD7, 0x4A, 0x88, 0xFE); - - __declspec(selectany) volatile long _isCausalitySupported = 0; - - inline bool _IsCausalitySupported() - { -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) - if (_isCausalitySupported == 0) - { - long _causality = 1; - OSVERSIONINFOEX _osvi = {}; - _osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - - // The Causality is supported on Windows version higher than Windows 8 - _osvi.dwMajorVersion = 6; - _osvi.dwMinorVersion = 3; - - DWORDLONG _conditionMask = 0; - VER_SET_CONDITION(_conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); - VER_SET_CONDITION(_conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); - - if (::VerifyVersionInfo(&_osvi, VER_MAJORVERSION | VER_MINORVERSION, _conditionMask)) - { - _causality = 2; - } - - _isCausalitySupported = _causality; - return _causality == 2; - } - - return _isCausalitySupported == 2 ? true : false; -#else - return true; -#endif - } - - // Stateful logger rests inside task_impl_base. - struct _TaskEventLogger - { - _Task_impl_base *_M_task; - bool _M_scheduled; - bool _M_taskPostEventStarted; - - // Log before scheduling task - void _LogScheduleTask(bool _isContinuation) - { - if (details::_IsCausalitySupported()) - { - ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCreation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, - _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), - _isContinuation ? "Concurrency::PPLTask::ScheduleContinuationTask" : "Concurrency::PPLTask::ScheduleTask", 0); - _M_scheduled = true; - } - } - - // It will log the cancel event but not canceled state. _LogTaskCompleted will log the terminal state, which includes cancel state. - void _LogCancelTask() - { - if (details::_IsCausalitySupported()) - { - ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationRelation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Important, ::Windows::Foundation::Diagnostics::CausalitySource::Library, - _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalityRelation::Cancel); - - } - } - - // Log when task reaches terminal state. Note: the task can reach a terminal state (by cancellation or exception) without having run - void _LogTaskCompleted(); - - // Log when task body (which includes user lambda and other scheduling code) begin to run - void _LogTaskExecutionStarted() { } - - // Log when task body finish executing - void _LogTaskExecutionCompleted() - { - if (_M_taskPostEventStarted && details::_IsCausalitySupported()) - { - ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, - ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); - } - } - - // Log right before user lambda being invoked - void _LogWorkItemStarted() - { - if (details::_IsCausalitySupported()) - { - ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, - _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); - } - } - - // Log right after user lambda being invoked - void _LogWorkItemCompleted() - { - if (details::_IsCausalitySupported()) - { - ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, - ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); - - ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, - _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); - _M_taskPostEventStarted = true; - } - } - - _TaskEventLogger(_Task_impl_base *_task) : _M_task(_task) - { - _M_scheduled = false; - _M_taskPostEventStarted = false; - } - }; - - // Exception safe logger for user lambda - struct _TaskWorkItemRAIILogger - { - _TaskEventLogger &_M_logger; - _TaskWorkItemRAIILogger(_TaskEventLogger &_taskHandleLogger) : _M_logger(_taskHandleLogger) - { - _M_logger._LogWorkItemStarted(); - } - - ~_TaskWorkItemRAIILogger() - { - _M_logger._LogWorkItemCompleted(); - } - _TaskWorkItemRAIILogger &operator =(const _TaskWorkItemRAIILogger &); // cannot be assigned - }; - -#else - inline void _LogCancelTask(_Task_impl_base *) {} - struct _TaskEventLogger - { - void _LogScheduleTask(bool) {} - void _LogCancelTask() {} - void _LogWorkItemStarted() {} - void _LogWorkItemCompleted() {} - void _LogTaskExecutionStarted() {} - void _LogTaskExecutionCompleted() {} - void _LogTaskCompleted() {} - _TaskEventLogger(_Task_impl_base *) {} - }; - struct _TaskWorkItemRAIILogger - { - _TaskWorkItemRAIILogger(_TaskEventLogger &) {} - }; -#endif -#endif - /// - /// The _PPLTaskHandle is the strong-typed task handle base. All user task functions need to be wrapped in this task handler - /// to be executable by PPL. By deriving from a different _BaseTaskHandle, it can be used for both initial tasks and continuation tasks. - /// For initial tasks, _PPLTaskHandle will be derived from _UnrealizedChore, and for continuation tasks, it will be derived from - /// _ContinuationTaskHandleBase. The life time of the _PPLTaskHandle object is be managed by runtime if task handle is scheduled. - /// - /// - /// The result type of the _Task_impl. - /// - /// - /// The derived task handle class. The operator () needs to be implemented. - /// - /// - /// The base class from which _PPLTaskHandle should be derived. This is either _UnrealizedChore or _ContinuationTaskHandleBase. - /// - template - struct _PPLTaskHandle : _BaseTaskHandle - { - _PPLTaskHandle(const typename _Task_ptr<_ReturnType>::_Type & _PTask) : _M_pTask(_PTask) - { -#if _MSC_VER < 1800 - m_pFunction = reinterpret_cast (&_UnrealizedChore::_InvokeBridge<_PPLTaskHandle>); - _SetRuntimeOwnsLifetime(true); -#endif - } - virtual ~_PPLTaskHandle() { -#if _MSC_VER >= 1800 - // Here is the sink of all task completion code paths - _M_pTask->_M_taskEventLogger._LogTaskCompleted(); -#endif - } -#if _MSC_VER >= 1800 - virtual void invoke() const -#else - void operator()() const -#endif - { - // All exceptions should be rethrown to finish cleanup of the task collection. They will be caught and handled - // by the runtime. - _CONCRT_ASSERT(_M_pTask != nullptr); - if (!_M_pTask->_TransitionedToStarted()) { -#if _MSC_VER >= 1800 - static_cast(this)->_SyncCancelAndPropagateException(); -#endif - return; - } -#if _MSC_VER >= 1800 - _M_pTask->_M_taskEventLogger._LogTaskExecutionStarted(); -#endif - try - { - // All derived task handle must implement this contract function. - static_cast(this)->_Perform(); - } - catch (const Concurrency::task_canceled &) - { - _M_pTask->_Cancel(true); -#if _MSC_VER < 1800 - throw; -#endif - } - catch (const Concurrency::details::_Interruption_exception &) - { - _M_pTask->_Cancel(true); -#if _MSC_VER < 1800 - throw; -#endif - } - catch (IRestrictedErrorInfo*& _E) - { - _M_pTask->_CancelWithException(_E); -#if _MSC_VER < 1800 - throw; -#endif - } - catch (...) - { - _M_pTask->_CancelWithException(std::current_exception()); -#if _MSC_VER < 1800 - throw; -#endif - } -#if _MSC_VER >= 1800 - _M_pTask->_M_taskEventLogger._LogTaskExecutionCompleted(); -#endif - } - - // Cast _M_pTask pointer to "type-less" _Task_impl_base pointer, which can be used in _ContinuationTaskHandleBase. - // The return value should be automatically optimized by R-value ref. - _Task_ptr_base _GetTaskImplBase() const - { - return _M_pTask; - } - - typename _Task_ptr<_ReturnType>::_Type _M_pTask; - - private: - _PPLTaskHandle const & operator=(_PPLTaskHandle const&); // no assignment operator - }; - - /// - /// The base implementation of a first-class task. This class contains all the non-type specific - /// implementation details of the task. - /// - /**/ - struct _Task_impl_base - { - enum _TaskInternalState - { - // Tracks the state of the task, rather than the task collection on which the task is scheduled - _Created, - _Started, - _PendingCancel, - _Completed, - _Canceled - }; -#if _MSC_VER >= 1800 - _Task_impl_base(Concurrency::details::_CancellationTokenState * _PTokenState, Concurrency::scheduler_ptr _Scheduler_arg) - : _M_TaskState(_Created), - _M_fFromAsync(false), _M_fUnwrappedTask(false), - _M_pRegistration(nullptr), _M_Continuations(nullptr), _M_TaskCollection(_Scheduler_arg), - _M_taskEventLogger(this) -#else - _Task_impl_base(Concurrency::details::_CancellationTokenState * _PTokenState) : _M_TaskState(_Created), - _M_fFromAsync(false), _M_fRuntimeAggregate(false), _M_fUnwrappedTask(false), - _M_pRegistration(nullptr), _M_Continuations(nullptr), _M_pTaskCollection(nullptr), - _M_pTaskCreationAddressHint(nullptr) -#endif - { - // Set cancelation token - _M_pTokenState = _PTokenState; - _CONCRT_ASSERT(_M_pTokenState != nullptr); - if (_M_pTokenState != Concurrency::details::_CancellationTokenState::_None()) - _M_pTokenState->_Reference(); - - } - - virtual ~_Task_impl_base() - { - _CONCRT_ASSERT(_M_pTokenState != nullptr); - if (_M_pTokenState != Concurrency::details::_CancellationTokenState::_None()) - { - _M_pTokenState->_Release(); - } -#if _MSC_VER < 1800 - if (_M_pTaskCollection != nullptr) - { - _M_pTaskCollection->_Release(); - _M_pTaskCollection = nullptr; - } -#endif - } - - task_status _Wait() - { - bool _DoWait = true; - - if (_IsNonBlockingThread()) - { - // In order to prevent Windows Runtime STA threads from blocking the UI, calling task.wait() task.get() is illegal - // if task has not been completed. - if (!_IsCompleted() && !_IsCanceled()) - { - throw Concurrency::invalid_operation("Illegal to wait on a task in a Windows Runtime STA"); - } - else - { - // Task Continuations are 'scheduled' *inside* the chore that is executing on the ancestors's task group. If a continuation - // needs to be marshalled to a different apartment, instead of scheduling, we make a synchronous cross apartment COM - // call to execute the continuation. If it then happens to do something which waits on the ancestor (say it calls .get(), which - // task based continuations are wont to do), waiting on the task group results in on the chore that is making this - // synchronous callback, which causes a deadlock. To avoid this, we test the state ancestor's event , and we will NOT wait on - // if it has finished execution (which means now we are on the inline synchronous callback). - _DoWait = false; - } - } - if (_DoWait) - { -#if _MSC_VER < 1800 - // Wait for the task to be actually scheduled, otherwise the underlying task collection - // might not be created yet. If we don't wait, we will miss the chance to inline this task. - _M_Scheduled.wait(); - - - // A PPL task created by a task_completion_event does not have an underlying TaskCollection. For - // These tasks, a call to wait should wait for the event to be set. The TaskCollection must either - // be nullptr or allocated (the setting of _M_Scheduled) ensures that. -#endif - // If this task was created from a Windows Runtime async operation, do not attempt to inline it. The - // async operation will take place on a thread in the appropriate apartment Simply wait for the completed - // event to be set. -#if _MSC_VER >= 1800 - if (_M_fFromAsync) -#else - if ((_M_pTaskCollection == nullptr) || _M_fFromAsync) -#endif - { -#if _MSC_VER >= 1800 - _M_TaskCollection._Wait(); -#else - _M_Completed.wait(); -#endif - } - else - { - // Wait on the task collection to complete. The task collection is guaranteed to still be - // valid since the task must be still within scope so that the _Task_impl_base destructor - // has not yet been called. This call to _Wait potentially inlines execution of work. - try - { - // Invoking wait on a task collection resets the state of the task collection. This means that - // if the task collection itself were canceled, or had encountered an exception, only the first - // call to wait will receive this status. However, both cancellation and exceptions flowing through - // tasks set state in the task impl itself. - - // When it returns cancelled, either work chore or the cancel thread should already have set task's state - // properly -- cancelled state or completed state (because there was no interruption point). - // For tasks with unwrapped tasks, we should not change the state of current task, since the unwrapped task are still running. -#if _MSC_VER >= 1800 - _M_TaskCollection._RunAndWait(); -#else - _M_pTaskCollection->_RunAndWait(); -#endif - } - catch (Concurrency::details::_Interruption_exception&) - { - // The _TaskCollection will never be an interruption point since it has a none token. - _CONCRT_ASSERT(false); - } - catch (Concurrency::task_canceled&) - { - // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task - // must be called from code that is executed within the task (throwing it from parallel work created by and waited - // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen - // the exception and canceled the task. Swallow the exception here. - _CONCRT_ASSERT(_IsCanceled()); - } - catch (IRestrictedErrorInfo*& _E) - { - // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. - if(!_HasUserException()) - { - _CancelWithException(_E); - } - // Rethrow will mark the exception as observed. - _M_exceptionHolder->_RethrowUserException(); - } - catch (...) - { - // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. - if (!_HasUserException()) - { - _CancelWithException(std::current_exception()); - } - // Rethrow will mark the exception as observed. - _M_exceptionHolder->_RethrowUserException(); - } - - // If the lambda body for this task (executed or waited upon in _RunAndWait above) happened to return a task - // which is to be unwrapped and plumbed to the output of this task, we must not only wait on the lambda body, we must - // wait on the **INNER** body. It is in theory possible that we could inline such if we plumb a series of things through; - // however, this takes the tact of simply waiting upon the completion signal. - if (_M_fUnwrappedTask) - { -#if _MSC_VER >= 1800 - _M_TaskCollection._Wait(); -#else - _M_Completed.wait(); -#endif - } - } - } - - if (_HasUserException()) - { - _M_exceptionHolder->_RethrowUserException(); - } - else if (_IsCanceled()) - { - return Concurrency::canceled; - } - _CONCRT_ASSERT(_IsCompleted()); - return Concurrency::completed; - } - /// - /// Requests cancellation on the task and schedules continuations if the task can be transitioned to a terminal state. - /// - /// - /// Set to true if the cancel takes place as a result of the task body encountering an exception, or because an ancestor or task_completion_event the task - /// was registered with were canceled with an exception. A synchronous cancel is one that assures the task could not be running on a different thread at - /// the time the cancellation is in progress. An asynchronous cancel is one where the thread performing the cancel has no control over the thread that could - /// be executing the task, that is the task could execute concurrently while the cancellation is in progress. - /// - /// - /// Whether an exception other than the internal runtime cancellation exceptions caused this cancellation. - /// - /// - /// Whether this exception came from an ancestor task or a task_completion_event as opposed to an exception that was encountered by the task itself. Only valid when - /// _UserException is set to true. - /// - /// - /// The exception holder that represents the exception. Only valid when _UserException is set to true. - /// - virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder>& _ExHolder) = 0; - - bool _Cancel(bool _SynchronousCancel) - { - // Send in a dummy value for exception. It is not used when the first parameter is false. - return _CancelAndRunContinuations(_SynchronousCancel, false, false, _M_exceptionHolder); - } - - bool _CancelWithExceptionHolder(const std::shared_ptr<_ExceptionHolder>& _ExHolder, bool _PropagatedFromAncestor) - { - // This task was canceled because an ancestor task encountered an exception. - return _CancelAndRunContinuations(true, true, _PropagatedFromAncestor, _ExHolder); - } - - bool _CancelWithException(IRestrictedErrorInfo*& _Exception) - { - // This task was canceled because the task body encountered an exception. - _CONCRT_ASSERT(!_HasUserException()); -#if _MSC_VER >= 1800 - return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); -#else - return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationAddressHint())); -#endif - } - bool _CancelWithException(const std::exception_ptr& _Exception) - { - // This task was canceled because the task body encountered an exception. - _CONCRT_ASSERT(!_HasUserException()); -#if _MSC_VER >= 1800 - return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); -#else - return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationAddressHint())); -#endif - } - -#if _MSC_VER >= 1800 - void _RegisterCancellation(std::weak_ptr<_Task_impl_base> _WeakPtr) -#else - void _RegisterCancellation() -#endif - { - _CONCRT_ASSERT(Concurrency::details::_CancellationTokenState::_IsValid(_M_pTokenState)); -#if _MSC_VER >= 1800 - auto _CancellationCallback = [_WeakPtr](){ - // Taking ownership of the task prevents dead lock during destruction - // if the destructor waits for the cancellations to be finished - auto _task = _WeakPtr.lock(); - if (_task != nullptr) - _task->_Cancel(false); - }; - - _M_pRegistration = new Concurrency::details::_CancellationTokenCallback(_CancellationCallback); - _M_pTokenState->_RegisterCallback(_M_pRegistration); -#else - _M_pRegistration = _M_pTokenState->_RegisterCallback(reinterpret_cast(&_CancelViaToken), (_Task_impl_base *)this); -#endif - } - - void _DeregisterCancellation() - { - if (_M_pRegistration != nullptr) - { - _M_pTokenState->_DeregisterCallback(_M_pRegistration); - _M_pRegistration->_Release(); - _M_pRegistration = nullptr; - } - } -#if _MSC_VER < 1800 - static void _CancelViaToken(_Task_impl_base *_PImpl) - { - _PImpl->_Cancel(false); - } -#endif - bool _IsCreated() - { - return (_M_TaskState == _Created); - } - - bool _IsStarted() - { - return (_M_TaskState == _Started); - } - - bool _IsPendingCancel() - { - return (_M_TaskState == _PendingCancel); - } - - bool _IsCompleted() - { - return (_M_TaskState == _Completed); - } - - bool _IsCanceled() - { - return (_M_TaskState == _Canceled); - } - - bool _HasUserException() - { - return static_cast(_M_exceptionHolder); - } -#if _MSC_VER < 1800 - void _SetScheduledEvent() - { - _M_Scheduled.set(); - } -#endif - const std::shared_ptr<_ExceptionHolder>& _GetExceptionHolder() - { - _CONCRT_ASSERT(_HasUserException()); - return _M_exceptionHolder; - } - - bool _IsApartmentAware() - { - return _M_fFromAsync; - } - - void _SetAsync(bool _Async = true) - { - _M_fFromAsync = _Async; - } -#if _MSC_VER >= 1800 - _TaskCreationCallstack _GetTaskCreationCallstack() - { - return _M_pTaskCreationCallstack; - } - - void _SetTaskCreationCallstack(const _TaskCreationCallstack &_Callstack) - { - _M_pTaskCreationCallstack = _Callstack; - } -#else - void* _GetTaskCreationAddressHint() - { - return _M_pTaskCreationAddressHint; - } - - void _SetTaskCreationAddressHint(void* _AddressHint) - { - _M_pTaskCreationAddressHint = _AddressHint; - } -#endif - /// - /// Helper function to schedule the task on the Task Collection. - /// - /// - /// The task chore handle that need to be executed. - /// - /// - /// The inlining scheduling policy for current _PTaskHandle. - /// - void _ScheduleTask(_UnrealizedChore * _PTaskHandle, _TaskInliningMode _InliningMode) - { -#if _MSC_VER < 1800 - // Construct the task collection; We use none token to provent it becoming interruption point. - _M_pTaskCollection = Concurrency::details::_AsyncTaskCollection::_NewCollection(Concurrency::details::_CancellationTokenState::_None()); - // _M_pTaskCollection->_ScheduleWithAutoInline will schedule the chore onto AsyncTaskCollection with automatic inlining, in a way that honors cancellation etc. -#endif - try - { -#if _MSC_VER >= 1800 - _M_TaskCollection._ScheduleTask(_PTaskHandle, _InliningMode); -#else - // Do not need to check its returning state, more details please refer to _Wait method. - _M_pTaskCollection->_ScheduleWithAutoInline(_PTaskHandle, _InliningMode); -#endif - } - catch (const Concurrency::task_canceled &) - { - // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task - // must be called from code that is executed within the task (throwing it from parallel work created by and waited - // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen - // the exception and canceled the task. Swallow the exception here. - _CONCRT_ASSERT(_IsCanceled()); - } - catch (const Concurrency::details::_Interruption_exception &) - { - // The _TaskCollection will never be an interruption point since it has a none token. - _CONCRT_ASSERT(false); - } - catch (...) - { - // This exception could only have come from within the chore body. It should've been caught - // and the task should be canceled with exception. Swallow the exception here. - _CONCRT_ASSERT(_HasUserException()); - } -#if _MSC_VER < 1800 - // Set the event in case anyone is waiting to notify that this task has been scheduled. In the case where we - // execute the chore inline, the event should be set after the chore has executed, to prevent a different thread - // performing a wait on the task from waiting on the task collection before the chore is actually added to it, - // and thereby returning from the wait() before the chore has executed. - _SetScheduledEvent(); -#endif - } - - /// - /// Function executes a continuation. This function is recorded by a parent task implementation - /// when a continuation is created in order to execute later. - /// - /// - /// The continuation task chore handle that need to be executed. - /// - /**/ - void _RunContinuation(_ContinuationTaskHandleBase * _PTaskHandle) - { - _Task_ptr_base _ImplBase = _PTaskHandle->_GetTaskImplBase(); - if (_IsCanceled() && !_PTaskHandle->_M_isTaskBasedContinuation) - { - if (_HasUserException()) - { - // If the ancestor encountered an exception, transfer the exception to the continuation - // This traverses down the tree to propagate the exception. - _ImplBase->_CancelWithExceptionHolder(_GetExceptionHolder(), true); - } - else - { - // If the ancestor was canceled, then your own execution should be canceled. - // This traverses down the tree to cancel it. - _ImplBase->_Cancel(true); - } - } - else - { - // This can only run when the ancestor has completed or it's a task based continuation that fires when a task is canceled - // (with or without a user exception). - _CONCRT_ASSERT(_IsCompleted() || _PTaskHandle->_M_isTaskBasedContinuation); - -#if _MSC_VER >= 1800 - _CONCRT_ASSERT(!_ImplBase->_IsCanceled()); - return _ImplBase->_ScheduleContinuationTask(_PTaskHandle); -#else - // If it has been canceled here (before starting), do nothing. The guy firing cancel will do the clean up. - if (!_ImplBase->_IsCanceled()) - { - return _ImplBase->_ScheduleContinuationTask(_PTaskHandle); - } -#endif - } - - // If the handle is not scheduled, we need to manually delete it. - delete _PTaskHandle; - } - - // Schedule a continuation to run - void _ScheduleContinuationTask(_ContinuationTaskHandleBase * _PTaskHandle) - { -#if _MSC_VER >= 1800 - _M_taskEventLogger._LogScheduleTask(true); -#endif - // Ensure that the continuation runs in proper context (this might be on a Concurrency Runtime thread or in a different Windows Runtime apartment) - if (_PTaskHandle->_M_continuationContext._HasCapturedContext()) - { - // For those continuations need to be scheduled inside captured context, we will try to apply automatic inlining to their inline modes, - // if they haven't been specified as _ForceInline yet. This change will encourage those continuations to be executed inline so that reduce - // the cost of marshaling. - // For normal continuations we won't do any change here, and their inline policies are completely decided by ._ThenImpl method. - if (_PTaskHandle->_M_inliningMode != Concurrency::details::_ForceInline) - { - _PTaskHandle->_M_inliningMode = Concurrency::details::_DefaultAutoInline; - } - details::_ScheduleFuncWithAutoInline([_PTaskHandle]() -> HRESULT { - // Note that we cannot directly capture "this" pointer, instead, we should use _TaskImplPtr, a shared_ptr to the _Task_impl_base. - // Because "this" pointer will be invalid as soon as _PTaskHandle get deleted. _PTaskHandle will be deleted after being scheduled. - auto _TaskImplPtr = _PTaskHandle->_GetTaskImplBase(); - if (details::_ContextCallback::_CaptureCurrent() == _PTaskHandle->_M_continuationContext) - { - _TaskImplPtr->_ScheduleTask(_PTaskHandle, Concurrency::details::_ForceInline); - } - else - { - // - // It's entirely possible that the attempt to marshal the call into a differing context will fail. In this case, we need to handle - // the exception and mark the continuation as canceled with the appropriate exception. There is one slight hitch to this: - // - // NOTE: COM's legacy behavior is to swallow SEH exceptions and marshal them back as HRESULTS. This will in effect turn an SEH into - // a C++ exception that gets tagged on the task. One unfortunate result of this is that various pieces of the task infrastructure will - // not be in a valid state after this in /EHsc (due to the lack of destructors running, etc...). - // - try - { - // Dev10 compiler needs this! - auto _PTaskHandle1 = _PTaskHandle; - _PTaskHandle->_M_continuationContext._CallInContext([_PTaskHandle1, _TaskImplPtr]() -> HRESULT { - _TaskImplPtr->_ScheduleTask(_PTaskHandle1, Concurrency::details::_ForceInline); - return S_OK; - }); - } - catch (IRestrictedErrorInfo*& _E) - { - _TaskImplPtr->_CancelWithException(_E); - } - catch (...) - { - _TaskImplPtr->_CancelWithException(std::current_exception()); - } - } - return S_OK; - }, _PTaskHandle->_M_inliningMode); - } - else - { - _ScheduleTask(_PTaskHandle, _PTaskHandle->_M_inliningMode); - } - } - - /// - /// Schedule the actual continuation. This will either schedule the function on the continuation task's implementation - /// if the task has completed or append it to a list of functions to execute when the task actually does complete. - /// - /// - /// The input type of the task. - /// - /// - /// The output type of the task. - /// - /**/ - void _ScheduleContinuation(_ContinuationTaskHandleBase * _PTaskHandle) - { - enum { _Nothing, _Schedule, _Cancel, _CancelWithException } _Do = _Nothing; - - // If the task has canceled, cancel the continuation. If the task has completed, execute the continuation right away. - // Otherwise, add it to the list of pending continuations - { - scoped_lock _LockHolder(_M_ContinuationsCritSec); - if (_IsCompleted() || (_IsCanceled() && _PTaskHandle->_M_isTaskBasedContinuation)) - { - _Do = _Schedule; - } - else if (_IsCanceled()) - { - if (_HasUserException()) - { - _Do = _CancelWithException; - } - else - { - _Do = _Cancel; - } - } - else - { - // chain itself on the continuation chain. - _PTaskHandle->_M_next = _M_Continuations; - _M_Continuations = _PTaskHandle; - } - } - - // Cancellation and execution of continuations should be performed after releasing the lock. Continuations off of - // async tasks may execute inline. - switch (_Do) - { - case _Schedule: - { - _PTaskHandle->_GetTaskImplBase()->_ScheduleContinuationTask(_PTaskHandle); - break; - } - case _Cancel: - { - // If the ancestor was canceled, then your own execution should be canceled. - // This traverses down the tree to cancel it. - _PTaskHandle->_GetTaskImplBase()->_Cancel(true); - - delete _PTaskHandle; - break; - } - case _CancelWithException: - { - // If the ancestor encountered an exception, transfer the exception to the continuation - // This traverses down the tree to propagate the exception. - _PTaskHandle->_GetTaskImplBase()->_CancelWithExceptionHolder(_GetExceptionHolder(), true); - - delete _PTaskHandle; - break; - } - case _Nothing: - default: - // In this case, we have inserted continuation to continuation chain, - // nothing more need to be done, just leave. - break; - } - } - - void _RunTaskContinuations() - { - // The link list can no longer be modified at this point, - // since all following up continuations will be scheduled by themselves. - _ContinuationList _Cur = _M_Continuations, _Next; - _M_Continuations = nullptr; - while (_Cur) - { - // Current node might be deleted after running, - // so we must fetch the next first. - _Next = _Cur->_M_next; - _RunContinuation(_Cur); - _Cur = _Next; - } - } - static bool _IsNonBlockingThread() - { - APTTYPE _AptType; - APTTYPEQUALIFIER _AptTypeQualifier; - - HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); - // - // If it failed, it's not a Windows Runtime/COM initialized thread. This is not a failure. - // - if (SUCCEEDED(hr)) - { - switch (_AptType) - { - case APTTYPE_STA: - case APTTYPE_MAINSTA: - return true; - break; - case APTTYPE_NA: - switch (_AptTypeQualifier) - { - // A thread executing in a neutral apartment is either STA or MTA. To find out if this thread is allowed - // to wait, we check the app qualifier. If it is an STA thread executing in a neutral apartment, waiting - // is illegal, because the thread is responsible for pumping messages and waiting on a task could take the - // thread out of circulation for a while. - case APTTYPEQUALIFIER_NA_ON_STA: - case APTTYPEQUALIFIER_NA_ON_MAINSTA: - return true; - break; - } - break; - } - } -#if _UITHREADCTXT_SUPPORT - // This method is used to throw an exepection in _Wait() if called within STA. We - // want the same behavior if _Wait is called on the UI thread. - if (SUCCEEDED(CaptureUiThreadContext(nullptr))) - { - return true; - } -#endif // _UITHREADCTXT_SUPPORT - - return false; - } - - template - static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type & _OuterTask, - _AsyncInfoImpl<_OpType, _CompHandlerType, _ResultType>* _AsyncOp) - { - typedef typename ABI::Windows::Foundation::Internal::GetAbiType()))>::type _Result_abi; - // This method is invoked either when a task is created from an existing async operation or - // when a lambda that creates an async operation executes. - - // If the outer task is pending cancel, cancel the async operation before setting the completed handler. The COM reference on - // the IAsyncInfo object will be released when all *references to the operation go out of scope. - - // This assertion uses the existence of taskcollection to determine if the task was created from an event. - // That is no longer valid as even tasks created from a user lambda could have no underlying taskcollection - // when a custom scheduler is used. -#if _MSC_VER < 1800 - _CONCRT_ASSERT(((_OuterTask->_M_pTaskCollection == nullptr) || _OuterTask->_M_fUnwrappedTask) && !_OuterTask->_IsCanceled()); -#endif - - // Pass the shared_ptr by value into the lambda instead of using 'this'. - - _AsyncOp->put_Completed(Microsoft::WRL::Callback<_CompHandlerType>( - [_OuterTask, _AsyncOp](_OpType* _Operation, ABI::Windows::Foundation::AsyncStatus _Status) mutable -> HRESULT - { - HRESULT hr = S_OK; - if (_Status == ABI::Windows::Foundation::AsyncStatus::Canceled) - { - _OuterTask->_Cancel(true); - } - else if (_Status == ABI::Windows::Foundation::AsyncStatus::Error) - { - HRESULT _hr; - Microsoft::WRL::ComPtr pAsyncInfo; - if (SUCCEEDED(hr = _Operation->QueryInterface(pAsyncInfo.GetAddressOf())) && SUCCEEDED(hr = pAsyncInfo->get_ErrorCode(&_hr))) - _OuterTask->_CancelWithException(std::make_exception_ptr(_hr)); - } - else - { - _CONCRT_ASSERT(_Status == ABI::Windows::Foundation::AsyncStatus::Completed); - _NormalizeVoidToUnitType<_Result_abi>::_Type results; - if (SUCCEEDED(hr = _AsyncOp->GetResults(&results))) - _OuterTask->_FinalizeAndRunContinuations(results); - } - // Take away this shared pointers reference on the task instead of waiting for the delegate to be released. It could - // be released on a different thread after a delay, and not releasing the reference here could cause the tasks to hold - // on to resources longer than they should. As an example, without this reset, writing to a file followed by reading from - // it using the Windows Runtime Async APIs causes a sharing violation. - // Using const_cast is the workaround for failed mutable keywords - const_cast<_Task_ptr<_ReturnType>::_Type &>(_OuterTask).reset(); - return hr; - }).Get()); - _OuterTask->_SetUnwrappedAsyncOp(_AsyncOp); - } - template - static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type& _OuterTask, const task<_InternalReturnType> & _UnwrappedTask) - { - _CONCRT_ASSERT(_OuterTask->_M_fUnwrappedTask && !_OuterTask->_IsCanceled()); - // - // We must ensure that continuations off _OuterTask (especially exception handling ones) continue to function in the - // presence of an exception flowing out of the inner task _UnwrappedTask. This requires an exception handling continuation - // off the inner task which does the appropriate funnelling to the outer one. We use _Then instead of then to prevent - // the exception from being marked as observed by our internal continuation. This continuation must be scheduled regardless - // of whether or not the _OuterTask task is canceled. - // - _UnwrappedTask._Then([_OuterTask](task<_InternalReturnType> _AncestorTask) -> HRESULT { - - if (_AncestorTask._GetImpl()->_IsCompleted()) - { - _OuterTask->_FinalizeAndRunContinuations(_AncestorTask._GetImpl()->_GetResult()); - } - else - { - _CONCRT_ASSERT(_AncestorTask._GetImpl()->_IsCanceled()); - if (_AncestorTask._GetImpl()->_HasUserException()) - { - // Set _PropagatedFromAncestor to false, since _AncestorTask is not an ancestor of _UnwrappedTask. - // Instead, it is the enclosing task. - _OuterTask->_CancelWithExceptionHolder(_AncestorTask._GetImpl()->_GetExceptionHolder(), false); - } - else - { - _OuterTask->_Cancel(true); - } - } - return S_OK; -#if _MSC_VER >= 1800 - }, nullptr, Concurrency::details::_DefaultAutoInline); -#else - }, nullptr, false, Concurrency::details::_DefaultAutoInline); -#endif - } - -#if _MSC_VER >= 1800 - Concurrency::scheduler_ptr _GetScheduler() const - { - return _M_TaskCollection._GetScheduler(); - } -#else - Concurrency::event _M_Completed; - Concurrency::event _M_Scheduled; -#endif - - // Tracks the internal state of the task - volatile _TaskInternalState _M_TaskState; - // Set to true either if the ancestor task had the flag set to true, or if the lambda that does the work of this task returns an - // async operation or async action that is unwrapped by the runtime. - bool _M_fFromAsync; -#if _MSC_VER < 1800 - // Set to true if we need to marshal the inner parts of an aggregate type like std::vector or std::pair. We only marshal - // the contained T^s if we create the vector or pair, such as on a when_any or a when_all operation. - bool _M_fRuntimeAggregate; -#endif - // Set to true when a continuation unwraps a task or async operation. - bool _M_fUnwrappedTask; - - // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. - // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception - // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. - std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; - - typedef _ContinuationTaskHandleBase * _ContinuationList; - - critical_section _M_ContinuationsCritSec; - _ContinuationList _M_Continuations; - - // The cancellation token state. - Concurrency::details::_CancellationTokenState * _M_pTokenState; - - // The registration on the token. - Concurrency::details::_CancellationTokenRegistration * _M_pRegistration; - - // The async task collection wrapper -#if _MSC_VER >= 1800 - Concurrency::details::_TaskCollection_t _M_TaskCollection; - - // Callstack for function call (constructor or .then) that created this task impl. - _TaskCreationCallstack _M_pTaskCreationCallstack; - - _TaskEventLogger _M_taskEventLogger; -#else - Concurrency::details::_AsyncTaskCollection * _M_pTaskCollection; - - // Points to the source code instruction right after the function call (constructor or .then) that created this task impl. - void* _M_pTaskCreationAddressHint; -#endif - - private: - // Must not be copied by value: - _Task_impl_base(const _Task_impl_base&); - _Task_impl_base const & operator=(_Task_impl_base const&); - }; - -#if _MSC_VER >= 1800 -#if _PPLTASK_ASYNC_LOGGING - inline void _TaskEventLogger::_LogTaskCompleted() - { - if (_M_scheduled) - { - ::Windows::Foundation::AsyncStatus _State; - if (_M_task->_IsCompleted()) - _State = ::Windows::Foundation::AsyncStatus::Completed; - else if (_M_task->_HasUserException()) - _State = ::Windows::Foundation::AsyncStatus::Error; - else - _State = ::Windows::Foundation::AsyncStatus::Canceled; - - if (details::_IsCausalitySupported()) - { - ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, - _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), _State); - } - } - } -#endif -#endif - - template - struct _Task_impl : public _Task_impl_base - { - typedef ABI::Windows::Foundation::IAsyncInfo _AsyncOperationType; -#if _MSC_VER >= 1800 - _Task_impl(Concurrency::details::_CancellationTokenState * _Ct, Concurrency::scheduler_ptr _Scheduler_arg) - : _Task_impl_base(_Ct, _Scheduler_arg) -#else - _Task_impl(Concurrency::details::_CancellationTokenState * _Ct) : _Task_impl_base(_Ct) -#endif - { - _M_unwrapped_async_op = nullptr; - } - virtual ~_Task_impl() - { - // We must invoke _DeregisterCancellation in the derived class destructor. Calling it in the base class destructor could cause - // a partially initialized _Task_impl to be in the list of registrations for a cancellation token. - _DeregisterCancellation(); - } - virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder> & _ExceptionHolder) - { - enum { _Nothing, _RunContinuations, _Cancel } _Do = _Nothing; - { - scoped_lock _LockHolder(_M_ContinuationsCritSec); - if (_UserException) - { - _CONCRT_ASSERT(_SynchronousCancel && !_IsCompleted()); - // If the state is _Canceled, the exception has to be coming from an ancestor. - _CONCRT_ASSERT(!_IsCanceled() || _PropagatedFromAncestor); -#if _MSC_VER < 1800 - // If the state is _Started or _PendingCancel, the exception cannot be coming from an ancestor. - _CONCRT_ASSERT((!_IsStarted() && !_IsPendingCancel()) || !_PropagatedFromAncestor); -#endif - // We should not be canceled with an exception more than once. - _CONCRT_ASSERT(!_HasUserException()); - - if (_M_TaskState == _Canceled) - { - // If the task has finished cancelling there should not be any continuation records in the array. - return false; - } - else - { - _CONCRT_ASSERT(_M_TaskState != _Completed); - _M_exceptionHolder = _ExceptionHolder; - } - } - else - { - // Completed is a non-cancellable state, and if this is an asynchronous cancel, we're unable to do better than the last async cancel - // which is to say, cancellation is already initiated, so return early. - if (_IsCompleted() || _IsCanceled() || (_IsPendingCancel() && !_SynchronousCancel)) - { - _CONCRT_ASSERT(!_IsCompleted() || !_HasUserException()); - return false; - } - _CONCRT_ASSERT(!_SynchronousCancel || !_HasUserException()); - } - -#if _MSC_VER >= 1800 - if (_SynchronousCancel) -#else - if (_SynchronousCancel || _IsCreated()) -#endif - { - // Be aware that this set must be done BEFORE _M_Scheduled being set, or race will happen between this and wait() - _M_TaskState = _Canceled; -#if _MSC_VER < 1800 - _M_Scheduled.set(); -#endif - - // Cancellation completes the task, so all dependent tasks must be run to cancel them - // They are canceled when they begin running (see _RunContinuation) and see that their - // ancestor has been canceled. - _Do = _RunContinuations; - } - else - { -#if _MSC_VER >= 1800 - _CONCRT_ASSERT(!_UserException); - - if (_IsStarted()) - { - // should not initiate cancellation under a lock - _Do = _Cancel; - } - - // The _M_TaskState variable transitions to _Canceled when cancellation is completed (the task is not executing user code anymore). - // In the case of a synchronous cancel, this can happen immediately, whereas with an asynchronous cancel, the task has to move from - // _Started to _PendingCancel before it can move to _Canceled when it is finished executing. - _M_TaskState = _PendingCancel; - - _M_taskEventLogger._LogCancelTask(); - } - } - - switch (_Do) - { - case _Cancel: - { -#else - _CONCRT_ASSERT(_IsStarted() && !_UserException); -#endif - // The _M_TaskState variable transitions to _Canceled when cancellation is completed (the task is not executing user code anymore). - // In the case of a synchronous cancel, this can happen immediately, whereas with an asynchronous cancel, the task has to move from - // _Started to _PendingCancel before it can move to _Canceled when it is finished executing. - _M_TaskState = _PendingCancel; - if (_M_unwrapped_async_op != nullptr) - { - // We will only try to cancel async operation but not unwrapped tasks, since unwrapped tasks cannot be canceled without its token. - if (_M_unwrapped_async_op) _M_unwrapped_async_op->Cancel(); - } -#if _MSC_VER >= 1800 - _M_TaskCollection._Cancel(); - break; -#else - // Optimistic trying for cancelation - if (_M_pTaskCollection != nullptr) - { - _M_pTaskCollection->_Cancel(); - } -#endif - } -#if _MSC_VER < 1800 - } -#endif - - // Only execute continuations and mark the task as completed if we were able to move the task to the _Canceled state. -#if _MSC_VER >= 1800 - case _RunContinuations: - { - _M_TaskCollection._Complete(); -#else - if (_RunContinuations) - { - _M_Completed.set(); -#endif - - if (_M_Continuations) - { - // Scheduling cancellation with automatic inlining. - details::_ScheduleFuncWithAutoInline([=]() -> HRESULT { _RunTaskContinuations(); return S_OK; }, Concurrency::details::_DefaultAutoInline); - } -#if _MSC_VER >= 1800 - break; - } -#endif - } - return true; - } - void _FinalizeAndRunContinuations(_ReturnType _Result) - { - -#if _MSC_VER >= 1800 - _M_Result.Set(_Result); -#else - _M_Result = _Result; - _M_ResultContext = _ResultContext<_ReturnType>::_GetContext(_M_fRuntimeAggregate); -#endif - { - // - // Hold this lock to ensure continuations being concurrently either get added - // to the _M_Continuations vector or wait for the result - // - scoped_lock _LockHolder(_M_ContinuationsCritSec); - - // A task could still be in the _Created state if it was created with a task_completion_event. - // It could also be in the _Canceled state for the same reason. - _CONCRT_ASSERT(!_HasUserException() && !_IsCompleted()); - if (_IsCanceled()) - { - return; - } - - // Always transition to "completed" state, even in the face of unacknowledged pending cancellation - _M_TaskState = _Completed; - } -#if _MSC_VER >= 1800 - _M_TaskCollection._Complete(); -#else - _M_Completed.set(); -#endif - _RunTaskContinuations(); - } - // - // This method is invoked when the starts executing. The task returns early if this method returns true. - // - bool _TransitionedToStarted() - { - scoped_lock _LockHolder(_M_ContinuationsCritSec); -#if _MSC_VER >= 1800 - // Canceled state could only result from antecedent task's canceled state, but that code path will not reach here. - _ASSERT(!_IsCanceled()); - if (_IsPendingCancel()) -#else - if (_IsCanceled()) -#endif - { - return false; - } - _CONCRT_ASSERT(_IsCreated()); - _M_TaskState = _Started; - return true; - } - void _SetUnwrappedAsyncOp(_AsyncOperationType* _AsyncOp) - { - scoped_lock _LockHolder(_M_ContinuationsCritSec); - // Cancel the async operation if the task itself is canceled, since the thread that canceled the task missed it. - if (_IsPendingCancel()) - { - _CONCRT_ASSERT(!_IsCanceled()); - if (_AsyncOp) _AsyncOp->Cancel(); - } - else - { - _M_unwrapped_async_op = _AsyncOp; - } - } -#if _MSC_VER >= 1800 - // Return true if the task has reached a terminal state - bool _IsDone() - { - return _IsCompleted() || _IsCanceled(); - } -#endif - _ReturnType _GetResult() - { -#if _MSC_VER >= 1800 - return _M_Result.Get(); -#else - return _ResultContext<_ReturnType>::_GetValue(_M_Result, _M_ResultContext, _M_fRuntimeAggregate); -#endif - } -#if _MSC_VER >= 1800 - _ResultHolder<_ReturnType> _M_Result; // this means that the result type must have a public default ctor. -#else - _ReturnType _M_Result; // this means that the result type must have a public default ctor. -#endif - Microsoft::WRL::ComPtr<_AsyncOperationType> _M_unwrapped_async_op; -#if _MSC_VER < 1800 - _ContextCallback _M_ResultContext; -#endif - }; - - template - struct _Task_completion_event_impl - { -#if _MSC_VER >= 1800 - private: - _Task_completion_event_impl(const _Task_completion_event_impl&); - _Task_completion_event_impl& operator=(const _Task_completion_event_impl&); - - public: -#endif - typedef std::vector::_Type> _TaskList; - - _Task_completion_event_impl() : _M_fHasValue(false), _M_fIsCanceled(false) - { - } - - bool _HasUserException() - { - return _M_exceptionHolder != nullptr; - } - - ~_Task_completion_event_impl() - { - for (auto _TaskIt = _M_tasks.begin(); _TaskIt != _M_tasks.end(); ++_TaskIt) - { - _CONCRT_ASSERT(!_M_fHasValue && !_M_fIsCanceled); - // Cancel the tasks since the event was never signaled or canceled. - (*_TaskIt)->_Cancel(true); - } - } - - // We need to protect the loop over the array, so concurrent_vector would not have helped - _TaskList _M_tasks; - critical_section _M_taskListCritSec; -#if _MSC_VER >= 1800 - _ResultHolder<_ResultType> _M_value; -#else - _ResultType _M_value; -#endif - std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; - bool _M_fHasValue; - bool _M_fIsCanceled; - }; - - // Utility method for dealing with void functions - inline std::function _MakeVoidToUnitFunc(const std::function& _Func) - { - return [=](_Unit_type* retVal) -> HRESULT { HRESULT hr = _Func(); *retVal = _Unit_type(); return hr; }; - } - - template - std::function _MakeUnitToTFunc(const std::function& _Func) - { - return [=](_Unit_type, _Type* retVal) -> HRESULT { HRESULT hr = _Func(retVal); return hr; }; - } - - template - std::function _MakeTToUnitFunc(const std::function& _Func) - { - return[=](_Type t, _Unit_type* retVal) -> HRESULT { HRESULT hr = _Func(t); *retVal = _Unit_type(); return hr; }; - } - - inline std::function _MakeUnitToUnitFunc(const std::function& _Func) - { - return [=](_Unit_type, _Unit_type* retVal) -> HRESULT { HRESULT hr = _Func(); *retVal = _Unit_type(); return hr; }; - } -} - - -/// -/// The task_completion_event class allows you to delay the execution of a task until a condition is satisfied, -/// or start a task in response to an external event. -/// -/// -/// The result type of this task_completion_event class. -/// -/// -/// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and -/// thereby have its continuations scheduled for execution, at some point in the future. The task_completion_event must -/// have the same type as the task you create, and calling the set method on the task completion event with a value of that type -/// will cause the associated task to complete, and provide that value as a result to its continuations. -/// If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed. -/// task_completion_event behaves like a smart pointer, and should be passed by value. -/// -/// -/**/ -template -class task_completion_event -{ -public: - /// - /// Constructs a task_completion_event object. - /// - /**/ - task_completion_event() : _M_Impl(std::make_shared>()) - { - } - - /// - /// Sets the task completion event. - /// - /// - /// The result to set this event with. - /// - /// - /// The method returns true if it was successful in setting the event. It returns false if the event is already set. - /// - /// - /// In the presence of multiple or concurrent calls to set, only the first call will succeed and its result (if any) will be stored in the - /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the - /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have - /// a other than void will pass the value to their continuations. - /// - /**/ - bool set(_ResultType _Result) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas - { - // Subsequent sets are ignored. This makes races to set benign: the first setter wins and all others are ignored. - if (_IsTriggered()) - { - return false; - } - - _TaskList _Tasks; - bool _RunContinuations = false; - { - details::scoped_lock _LockHolder(_M_Impl->_M_taskListCritSec); - - if (!_IsTriggered()) - { -#if _MSC_VER >= 1800 - _M_Impl->_M_value.Set(_Result); -#else - _M_Impl->_M_value = _Result; -#endif - _M_Impl->_M_fHasValue = true; - - _Tasks.swap(_M_Impl->_M_tasks); - _RunContinuations = true; - } - } - - if (_RunContinuations) - { - for (auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt) - { -#if _MSC_VER >= 1800 - // If current task was cancelled by a cancellation_token, it would be in cancel pending state. - if ((*_TaskIt)->_IsPendingCancel()) - (*_TaskIt)->_Cancel(true); - else - { - // Tasks created with task_completion_events can be marked as async, (we do this in when_any and when_all - // if one of the tasks involved is an async task). Since continuations of async tasks can execute inline, we - // need to run continuations after the lock is released. - (*_TaskIt)->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); - } -#else - // Tasks created with task_completion_events can be marked as async, (we do this in when_any and when_all - // if one of the tasks involved is an async task). Since continuations of async tasks can execute inline, we - // need to run continuations after the lock is released. - (*_TaskIt)->_FinalizeAndRunContinuations(_M_Impl->_M_value); -#endif - } - if (_M_Impl->_HasUserException()) - { - _M_Impl->_M_exceptionHolder.reset(); - } - return true; - } - - return false; - } -#if _MSC_VER >= 1800 - - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result - bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas - { - // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. - return _Cancel(std::make_exception_ptr(_Except), _CAPTURE_CALLSTACK()); - } -#endif - - /// - /// Propagates an exception to all tasks associated with this event. - /// - /// - /// The exception_ptr that indicates the exception to set this event with. - /// - /**/ - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result - bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas - { - // It is important that _ReturnAddress() evaluate to the instruction after the call instruction for set_exception. -#if _MSC_VER >= 1800 - return _Cancel(_ExceptionPtr, _CAPTURE_CALLSTACK()); -#else - return _Cancel(_ExceptionPtr, _ReturnAddress()); -#endif - } - - /// - /// Internal method to cancel the task_completion_event. Any task created using this event will be marked as canceled if it has - /// not already been set. - /// - bool _Cancel() const - { - // Cancel with the stored exception if one exists. - return _CancelInternal(); - } - - /// - /// Internal method to cancel the task_completion_event with the exception provided. Any task created using this event will be canceled - /// with the same exception. - /// - template -#if _MSC_VER >= 1800 - bool _Cancel(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack()) const -#else - bool _Cancel(_ExHolderType _ExHolder, void* _SetExceptionAddressHint = nullptr) const -#endif - { - (void)_SetExceptionAddressHint; - bool _Canceled; -#if _MSC_VER >= 1800 - if(_StoreException(_ExHolder, _SetExceptionAddressHint)) -#else - if (_StoreException(_ExHolder)) -#endif - { - _Canceled = _CancelInternal(); - _CONCRT_ASSERT(_Canceled); - } - else - { - _Canceled = false; - } - return _Canceled; - } - - /// - /// Internal method that stores an exception in the task completion event. This is used internally by when_any. - /// Note, this does not cancel the task completion event. A task completion event with a stored exception - /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. - /// - template -#if _MSC_VER >= 1800 - bool _StoreException(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack()) const -#else - bool _StoreException(_ExHolderType _ExHolder, void* _SetExceptionAddressHint = nullptr) const -#endif - { - details::scoped_lock _LockHolder(_M_Impl->_M_taskListCritSec); - if (!_IsTriggered() && !_M_Impl->_HasUserException()) - { - // Create the exception holder only if we have ensured there we will be successful in setting it onto the - // task completion event. Failing to do so will result in an unobserved task exception. - _M_Impl->_M_exceptionHolder = _ToExceptionHolder(_ExHolder, _SetExceptionAddressHint); - return true; - } - return false; - } - - /// - /// Tests whether current event has been either Set, or Canceled. - /// - bool _IsTriggered() const - { - return _M_Impl->_M_fHasValue || _M_Impl->_M_fIsCanceled; - } - -private: - -#if _MSC_VER >= 1800 - static std::shared_ptr _ToExceptionHolder(const std::shared_ptr& _ExHolder, const details::_TaskCreationCallstack&) -#else - static std::shared_ptr _ToExceptionHolder(const std::shared_ptr& _ExHolder, void*) -#endif - { - return _ExHolder; - } - -#if _MSC_VER >= 1800 - static std::shared_ptr _ToExceptionHolder(std::exception_ptr _ExceptionPtr, const details::_TaskCreationCallstack &_SetExceptionAddressHint) -#else - static std::shared_ptr _ToExceptionHolder(std::exception_ptr _ExceptionPtr, void* _SetExceptionAddressHint) -#endif - { - return std::make_shared(_ExceptionPtr, _SetExceptionAddressHint); - } - - template friend class task; // task can register itself with the event by calling the private _RegisterTask - template friend class task_completion_event; - - typedef typename details::_Task_completion_event_impl<_ResultType>::_TaskList _TaskList; - - /// - /// Cancels the task_completion_event. - /// - bool _CancelInternal() const - { - // Cancellation of task completion events is an internal only utility. Our usage is such that _CancelInternal - // will never be invoked if the task completion event has been set. - _CONCRT_ASSERT(!_M_Impl->_M_fHasValue); - if (_M_Impl->_M_fIsCanceled) - { - return false; - } - - _TaskList _Tasks; - bool _Cancel = false; - { - details::scoped_lock _LockHolder(_M_Impl->_M_taskListCritSec); - _CONCRT_ASSERT(!_M_Impl->_M_fHasValue); - if (!_M_Impl->_M_fIsCanceled) - { - _M_Impl->_M_fIsCanceled = true; - _Tasks.swap(_M_Impl->_M_tasks); - _Cancel = true; - } - } - - bool _UserException = _M_Impl->_HasUserException(); - - if (_Cancel) - { - for (auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt) - { - // Need to call this after the lock is released. See comments in set(). - if (_UserException) - { - (*_TaskIt)->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); - } - else - { - (*_TaskIt)->_Cancel(true); - } - } - } - return _Cancel; - } - - /// - /// Register a task with this event. This function is called when a task is constructed using - /// a task_completion_event. - /// - void _RegisterTask(const typename details::_Task_ptr<_ResultType>::_Type & _TaskParam) - { - details::scoped_lock _LockHolder(_M_Impl->_M_taskListCritSec); -#if _MSC_VER < 1800 - _TaskParam->_SetScheduledEvent(); -#endif - //If an exception was already set on this event, then cancel the task with the stored exception. - if (_M_Impl->_HasUserException()) - { - _TaskParam->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); - } - else if (_M_Impl->_M_fHasValue) - { -#if _MSC_VER >= 1800 - _TaskParam->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); -#else - _TaskParam->_FinalizeAndRunContinuations(_M_Impl->_M_value); -#endif - } - else - { - _M_Impl->_M_tasks.push_back(_TaskParam); - } - } - - std::shared_ptr> _M_Impl; -}; - -/// -/// The task_completion_event class allows you to delay the execution of a task until a condition is satisfied, -/// or start a task in response to an external event. -/// -/// -/// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and -/// thereby have its continuations scheduled for execution, at some point in the future. The task_completion_event must -/// have the same type as the task you create, and calling the set method on the task completion event with a value of that type -/// will cause the associated task to complete, and provide that value as a result to its continuations. -/// If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed. -/// task_completion_event behaves like a smart pointer, and should be passed by value. -/// -/// -/**/ -template<> -class task_completion_event -{ -public: - /// - /// Sets the task completion event. - /// - /// - /// The method returns true if it was successful in setting the event. It returns false if the event is already set. - /// - /// - /// In the presence of multiple or concurrent calls to set, only the first call will succeed and its result (if any) will be stored in the - /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the - /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have - /// a other than void will pass the value to their continuations. - /// - /**/ - bool set() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas - { - return _M_unitEvent.set(details::_Unit_type()); - } -#if _MSC_VER >= 1800 - - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result - bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas - { - return _M_unitEvent._Cancel(std::make_exception_ptr(_Except), _CAPTURE_CALLSTACK()); - } -#endif - - /// - /// Propagates an exception to all tasks associated with this event. - /// - /// - /// The exception_ptr that indicates the exception to set this event with. - /// - /**/ - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result - bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas - { - // It is important that _ReturnAddress() evaluate to the instruction after the call instruction for set_exception. -#if _MSC_VER >= 1800 - return _M_unitEvent._Cancel(_ExceptionPtr, _CAPTURE_CALLSTACK()); -#else - return _M_unitEvent._Cancel(_ExceptionPtr, _ReturnAddress()); -#endif - } - - /// - /// Cancel the task_completion_event. Any task created using this event will be marked as canceled if it has - /// not already been set. - /// - void _Cancel() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas - { - _M_unitEvent._Cancel(); - } - - /// - /// Cancel the task_completion_event with the exception holder provided. Any task created using this event will be canceled - /// with the same exception. - /// - void _Cancel(const std::shared_ptr& _ExHolder) const - { - _M_unitEvent._Cancel(_ExHolder); - } - - /// - /// Method that stores an exception in the task completion event. This is used internally by when_any. - /// Note, this does not cancel the task completion event. A task completion event with a stored exception - /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. - /// - bool _StoreException(const std::shared_ptr& _ExHolder) const - { - return _M_unitEvent._StoreException(_ExHolder); - } - - /// - /// Test whether current event has been either Set, or Canceled. - /// - bool _IsTriggered() const - { - return _M_unitEvent._IsTriggered(); - } - -private: - template friend class task; // task can register itself with the event by calling the private _RegisterTask - - /// - /// Register a task with this event. This function is called when a task is constructed using - /// a task_completion_event. - /// - void _RegisterTask(details::_Task_ptr::_Type _TaskParam) - { - _M_unitEvent._RegisterTask(_TaskParam); - } - - // The void event contains an event a dummy type so common code can be used for events with void and non-void results. - task_completion_event _M_unitEvent; -}; -namespace details -{ - // - // Compile-time validation helpers - // - - // Task constructor validation: issue helpful diagnostics for common user errors. Do not attempt full validation here. - // - // Anything callable is fine - template - auto _IsValidTaskCtor(_Ty _Param, int, int, int, int, int, int, int) -> typename decltype(_Param(), std::true_type()); - - // Anything callable with a task return value is fine - template - auto _IsValidTaskCtor(_Ty _Param, int, int, int, int, int, int, ...) -> typename decltype(_Param(stdx::declval*>()), std::true_type()); - - // Anything callable with a return value is fine - template - auto _IsValidTaskCtor(_Ty _Param, int, int, int, int, int, ...) -> typename decltype(_Param(stdx::declval<_ReturnType*>()), std::true_type()); - - // Anything that has GetResults is fine: this covers AsyncAction* - template - auto _IsValidTaskCtor(_Ty _Param, int, int, int, int, ...) -> typename decltype(_Param->GetResults(), std::true_type()); - - // Anything that has GetResults(TResult_abi*) is fine: this covers AsyncOperation* - template - auto _IsValidTaskCtor(_Ty _Param, int, int, int, ...) -> typename decltype(_Param->GetResults(stdx::declval()))*>()), std::true_type()); - - // Allow parameters with set: this covers task_completion_event - template - auto _IsValidTaskCtor(_Ty _Param, int, int, ...) -> typename decltype(_Param.set(stdx::declval<_ReturnType>()), std::true_type()); - - template - auto _IsValidTaskCtor(_Ty _Param, int, ...) -> typename decltype(_Param.set(), std::true_type()); - - // All else is invalid - template - std::false_type _IsValidTaskCtor(_Ty _Param, ...); - - template - void _ValidateTaskConstructorArgs(_Ty _Param) - { - (void)_Param; - static_assert(std::is_same(_Param, 0, 0, 0, 0, 0, 0, 0)), std::true_type>::value, - "incorrect argument for task constructor; can be a callable object, an asynchronous operation, or a task_completion_event" - ); - static_assert(!(std::is_same<_Ty, _ReturnType>::value && details::_IsIAsyncInfo<_Ty>::_Value), - "incorrect template argument for task; consider using the return type of the async operation"); - } - // Helpers for create_async validation - // - // A parameter lambda taking no arguments is valid - template - static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int, int, int, int, int) -> typename decltype(_Param(), std::true_type()); - - // A parameter lambda taking a result argument is valid - template - static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int, int, int, int, ...) -> typename decltype(_Param(stdx::declval<_ReturnType*>()), std::true_type()); - - // A parameter lambda taking an cancellation_token argument is valid - template - static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int, int, int, ...) -> typename decltype(_Param(Concurrency::cancellation_token::none()), std::true_type()); - - // A parameter lambda taking an cancellation_token argument and a result argument is valid - template - static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int, int, ...) -> typename decltype(_Param(Concurrency::cancellation_token::none(), stdx::declval<_ReturnType*>()), std::true_type()); - - // A parameter lambda taking a progress report argument is valid - template - static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int, ...) -> typename decltype(_Param(details::_ProgressReporterCtorArgType()), std::true_type()); - - // A parameter lambda taking a progress report argument and a result argument is valid - template - static auto _IsValidCreateAsync(_Ty _Param, int, int, int, ...) -> typename decltype(_Param(details::_ProgressReporterCtorArgType(), stdx::declval<_ReturnType*>()), std::true_type()); - - // A parameter lambda taking a progress report and a cancellation_token argument is valid - template - static auto _IsValidCreateAsync(_Ty _Param, int, int, ...) -> typename decltype(_Param(details::_ProgressReporterCtorArgType(), Concurrency::cancellation_token::none()), std::true_type()); - - // A parameter lambda taking a progress report and a cancellation_token argument and a result argument is valid - template - static auto _IsValidCreateAsync(_Ty _Param, int, ...) -> typename decltype(_Param(details::_ProgressReporterCtorArgType(), Concurrency::cancellation_token::none(), stdx::declval<_ReturnType*>()), std::true_type()); - - // All else is invalid - template - static std::false_type _IsValidCreateAsync(_Ty _Param, ...); -} - -/// -/// The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed asynchronously, -/// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces -/// a result of type on successful completion. Tasks of type task<void> produce no result. -/// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using -/// continuations(then), and join(when_all) and choice(when_any) patterns. -/// -/// -/// The result type of this task. -/// -/// -/// For more information, see . -/// -/**/ -template -class task -{ -public: - /// - /// The type of the result an object of this class produces. - /// - /**/ - typedef _ReturnType result_type; - - /// - /// Constructs a task object. - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lamda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - task() : _M_Impl(nullptr) - { - // The default constructor should create a task with a nullptr impl. This is a signal that the - // task is not usable and should throw if any wait(), get() or then() APIs are used. - } - - /// - /// Constructs a task object. - /// - /// - /// The type of the parameter from which the task is to be constructed. - /// - /// - /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> - /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function - /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, - /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. - /// - /// - /// The cancellation token to associate with this task. A task created without a cancellation token cannot be canceled. It implicitly receives - /// the token cancellation_token::none(). - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lamda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result - explicit task(_Ty _Param) - { -#if _MSC_VER >= 1800 - task_options _TaskOptions; -#endif - details::_ValidateTaskConstructorArgs<_ReturnType, _Ty>(_Param); - -#if _MSC_VER >= 1800 - _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); -#else - _CreateImpl(Concurrency::cancellation_token::none()._GetImplValue()); -#endif - // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of the task constructor. -#if _MSC_VER >= 1800 - _SetTaskCreationCallstack(_CAPTURE_CALLSTACK()); -#else - _SetTaskCreationAddressHint(_ReturnAddress()); -#endif - _TaskInitMaybeFunctor(_Param, details::_IsCallable<_ReturnType>(_Param, 0, 0, 0)); - } - - /// - /// Constructs a task object. - /// - /// - /// The type of the parameter from which the task is to be constructed. - /// - /// - /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> - /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function - /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, - /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. - /// - /// - /// The cancellation token to associate with this task. A task created without a cancellation token cannot be canceled. It implicitly receives - /// the token cancellation_token::none(). - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lamda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result -#if _MSC_VER >= 1800 - explicit task(_Ty _Param, const task_options &_TaskOptions) -#else - explicit task(_Ty _Param, Concurrency::cancellation_token _Token) -#endif - { - details::_ValidateTaskConstructorArgs<_ReturnType, _Ty>(_Param); - -#if _MSC_VER >= 1800 - _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); -#else - _CreateImpl(_Token._GetImplValue()); -#endif - // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of the task constructor. -#if _MSC_VER >= 1800 - _SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : _CAPTURE_CALLSTACK()); -#else - _SetTaskCreationAddressHint(_ReturnAddress()); -#endif - _TaskInitMaybeFunctor(_Param, details::_IsCallable<_ReturnType>(_Param, 0, 0, 0)); - } - - /// - /// Constructs a task object. - /// - /// - /// The source task object. - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lamda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - task(const task& _Other) : _M_Impl(_Other._M_Impl) {} - - /// - /// Constructs a task object. - /// - /// - /// The source task object. - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lamda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - task(task&& _Other) : _M_Impl(std::move(_Other._M_Impl)) {} - - /// - /// Replaces the contents of one task object with another. - /// - /// - /// The source task object. - /// - /// - /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same - /// actual task as does. - /// - /**/ - task& operator=(const task& _Other) - { - if (this != &_Other) - { - _M_Impl = _Other._M_Impl; - } - return *this; - } - - /// - /// Replaces the contents of one task object with another. - /// - /// - /// The source task object. - /// - /// - /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same - /// actual task as does. - /// - /**/ - task& operator=(task&& _Other) - { - if (this != &_Other) - { - _M_Impl = std::move(_Other._M_Impl); - } - return *this; - } - - /// - /// Adds a continuation task to this task. - /// - /// - /// The type of the function object that will be invoked by this task. - /// - /// - /// The continuation function to execute when this task completes. This continuation function must take as input - /// a variable of either result_type or task<result_type>, where result_type is the type - /// of the result this task produces. - /// - /// - /// The newly created continuation task. The result type of the returned task is determined by what returns. - /// - /// - /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available - /// to Windows Store apps. - /// For more information on how to use task continuations to compose asynchronous work, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result - auto then(const _Function& _Func) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType - { -#if _MSC_VER >= 1800 - task_options _TaskOptions; - details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); -#else - auto _ContinuationTask = _ThenImpl<_ReturnType, _Function>(_Func, nullptr, task_continuation_context::use_default()); - // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. - _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); - return _ContinuationTask; -#endif - } - - /// - /// Adds a continuation task to this task. - /// - /// - /// The type of the function object that will be invoked by this task. - /// - /// - /// The continuation function to execute when this task completes. This continuation function must take as input - /// a variable of either result_type or task<result_type>, where result_type is the type - /// of the result this task produces. - /// - /// - /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit - /// the token of its antecedent task. - /// - /// - /// The newly created continuation task. The result type of the returned task is determined by what returns. - /// - /// - /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available - /// to Windows Store apps. - /// For more information on how to use task continuations to compose asynchronous work, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result -#if _MSC_VER >= 1800 - auto then(const _Function& _Func, task_options _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType -#else - auto then(const _Function& _Func, Concurrency::cancellation_token _CancellationToken) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType -#endif - { -#if _MSC_VER >= 1800 - details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); -#else - auto _ContinuationTask = _ThenImpl<_ReturnType, _Function>(_Func, _CancellationToken._GetImplValue(), task_continuation_context::use_default()); - // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. - _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); - return _ContinuationTask; -#endif - } -#if _MSC_VER < 1800 - /// - /// Adds a continuation task to this task. - /// - /// - /// The type of the function object that will be invoked by this task. - /// - /// - /// The continuation function to execute when this task completes. This continuation function must take as input - /// a variable of either result_type or task<result_type>, where result_type is the type - /// of the result this task produces. - /// - /// - /// A variable that specifies where the continuation should execute. This variable is only useful when used in a - /// Windows Store app. For more information, see task_continuation_context - /// - /// - /// The newly created continuation task. The result type of the returned task is determined by what returns. - /// - /// - /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available - /// to Windows Store apps. - /// For more information on how to use task continuations to compose asynchronous work, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result - auto then(const _Function& _Func, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType - { - auto _ContinuationTask = _ThenImpl<_ReturnType, _Function>(_Func, nullptr, _ContinuationContext); - // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. - _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); - return _ContinuationTask; - } -#endif - /// - /// Adds a continuation task to this task. - /// - /// - /// The type of the function object that will be invoked by this task. - /// - /// - /// The continuation function to execute when this task completes. This continuation function must take as input - /// a variable of either result_type or task<result_type>, where result_type is the type - /// of the result this task produces. - /// - /// - /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit - /// the token of its antecedent task. - /// - /// - /// A variable that specifies where the continuation should execute. This variable is only useful when used in a - /// Windows Store app. For more information, see task_continuation_context - /// - /// - /// The newly created continuation task. The result type of the returned task is determined by what returns. - /// - /// - /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available - /// to Windows Store apps. - /// For more information on how to use task continuations to compose asynchronous work, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result - auto then(const _Function& _Func, Concurrency::cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType - { -#if _MSC_VER >= 1800 - task_options _TaskOptions(_CancellationToken, _ContinuationContext); - details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); -#else - auto _ContinuationTask = _ThenImpl<_ReturnType, _Function>(_Func, _CancellationToken._GetImplValue(), _ContinuationContext); - // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. - _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); - return _ContinuationTask; -#endif - } - - /// - /// Waits for this task to reach a terminal state. It is possible for wait to execute the task inline, if all of the tasks - /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. - /// - /// - /// A task_status value which could be either completed or canceled. If the task encountered an exception - /// during execution, or an exception was propagated to it from an antecedent task, wait will throw that exception. - /// - /**/ - task_status wait() const - { - if (_M_Impl == nullptr) - { - throw Concurrency::invalid_operation("wait() cannot be called on a default constructed task."); - } - - return _M_Impl->_Wait(); - } - - /// - /// Returns the result this task produced. If the task is not in a terminal state, a call to get will wait for the task to - /// finish. This method does not return a value when called on a task with a result_type of void. - /// - /// - /// The result of the task. - /// - /// - /// If the task is canceled, a call to get will throw a task_canceled exception. If the task - /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to get will throw that exception. - /// - /**/ - _ReturnType get() const - { - if (_M_Impl == nullptr) - { - throw Concurrency::invalid_operation("get() cannot be called on a default constructed task."); - } - - if (_M_Impl->_Wait() == Concurrency::canceled) - { - throw Concurrency::task_canceled(); - } - - return _M_Impl->_GetResult(); - } -#if _MSC_VER >= 1800 - /// - /// Determines if the task is completed. - /// - /// - /// True if the task has completed, false otherwise. - /// - /// - /// The function returns true if the task is completed or canceled (with or without user exception). - /// - bool is_done() const - { - if (!_M_Impl) - { - throw Concurrency::invalid_operation("is_done() cannot be called on a default constructed task."); - } - - return _M_Impl->_IsDone(); - } - - /// - /// Returns the scheduler for this task - /// - /// - /// A pointer to the scheduler - /// - Concurrency::scheduler_ptr scheduler() const - { - if (!_M_Impl) - { - throw Concurrency::invalid_operation("scheduler() cannot be called on a default constructed task."); - } - - return _M_Impl->_GetScheduler(); - } -#endif - /// - /// Determines whether the task unwraps a Windows Runtime IAsyncInfo interface or is descended from such a task. - /// - /// - /// true if the task unwraps an IAsyncInfo interface or is descended from such a task, false otherwise. - /// - /**/ - bool is_apartment_aware() const - { - if (_M_Impl == nullptr) - { - throw Concurrency::invalid_operation("is_apartment_aware() cannot be called on a default constructed task."); - } - return _M_Impl->_IsApartmentAware(); - } - - /// - /// Determines whether two task objects represent the same internal task. - /// - /// - /// true if the objects refer to the same underlying task, and false otherwise. - /// - /**/ - bool operator==(const task<_ReturnType>& _Rhs) const - { - return (_M_Impl == _Rhs._M_Impl); - } - - /// - /// Determines whether two task objects represent different internal tasks. - /// - /// - /// true if the objects refer to different underlying tasks, and false otherwise. - /// - /**/ - bool operator!=(const task<_ReturnType>& _Rhs) const - { - return !operator==(_Rhs); - } - - /// - /// Create an underlying task implementation. - /// -#if _MSC_VER >= 1800 - void _CreateImpl(Concurrency::details::_CancellationTokenState * _Ct, Concurrency::scheduler_ptr _Scheduler) -#else - void _CreateImpl(Concurrency::details::_CancellationTokenState * _Ct) -#endif - { - _CONCRT_ASSERT(_Ct != nullptr); -#if _MSC_VER >= 1800 - _M_Impl = details::_Task_ptr<_ReturnType>::_Make(_Ct, _Scheduler); -#else - _M_Impl = details::_Task_ptr<_ReturnType>::_Make(_Ct); -#endif - if (_Ct != Concurrency::details::_CancellationTokenState::_None()) - { -#if _MSC_VER >= 1800 - _M_Impl->_RegisterCancellation(_M_Impl); -#else - _M_Impl->_RegisterCancellation(); -#endif - } - } - - /// - /// Return the underlying implementation for this task. - /// - const typename details::_Task_ptr<_ReturnType>::_Type & _GetImpl() const - { - return _M_Impl; - } - - /// - /// Set the implementation of the task to be the supplied implementaion. - /// - void _SetImpl(const typename details::_Task_ptr<_ReturnType>::_Type & _Impl) - { - _CONCRT_ASSERT(_M_Impl == nullptr); - _M_Impl = _Impl; - } - - /// - /// Set the implementation of the task to be the supplied implementaion using a move instead of a copy. - /// - void _SetImpl(typename details::_Task_ptr<_ReturnType>::_Type && _Impl) - { - _CONCRT_ASSERT(_M_Impl == nullptr); - _M_Impl = std::move(_Impl); - } - - /// - /// Sets a property determining whether the task is apartment aware. - /// - void _SetAsync(bool _Async = true) - { - _GetImpl()->_SetAsync(_Async); - } - - /// - /// Sets a field in the task impl to the return address for calls to the task constructors and the then method. - /// -#if _MSC_VER >= 1800 - void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) - { - _GetImpl()->_SetTaskCreationCallstack(_callstack); - } -#else - void _SetTaskCreationAddressHint(void* _Address) - { - _GetImpl()->_SetTaskCreationAddressHint(_Address); - } -#endif - /// - /// An internal version of then that takes additional flags and always execute the continuation inline by default. - /// When _ForceInline is set to false, continuations inlining will be limited to default _DefaultAutoInline. - /// This function is Used for runtime internal continuations only. - /// - template -#if _MSC_VER >= 1800 - auto _Then(const _Function& _Func, Concurrency::details::_CancellationTokenState *_PTokenState, - details::_TaskInliningMode _InliningMode = Concurrency::details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType - { - // inherit from antecedent - auto _Scheduler = _GetImpl()->_GetScheduler(); - - return _ThenImpl<_ReturnType, _Function>(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); - } -#else - auto _Then(const _Function& _Func, Concurrency::details::_CancellationTokenState *_PTokenState, bool _Aggregating, - details::_TaskInliningMode _InliningMode = Concurrency::details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType - { - return _ThenImpl<_ReturnType, _Function>(_Func, _PTokenState, task_continuation_context::use_default(), _Aggregating, _InliningMode); - } -#endif - -private: - template friend class task; - - // A helper class template that transforms an intial task lambda returns void into a lambda that returns a non-void type (details::_Unit_type is used - // to substitute for void). This is to minimize the special handling required for 'void'. - template - class _Init_func_transformer - { - public: - static auto _Perform(std::function _Func) -> decltype(_Func) - { - return _Func; - } - }; - - template<> - class _Init_func_transformer - { - public: - static auto _Perform(std::function _Func) -> decltype(details::_MakeVoidToUnitFunc(_Func)) - { - return details::_MakeVoidToUnitFunc(_Func); - } - }; - - // The task handle type used to construct an 'initial task' - a task with no dependents. - template - struct _InitialTaskHandle : - details::_PPLTaskHandle<_ReturnType, _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>, details::_UnrealizedChore> - { - _Function _M_function; - _InitialTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _TaskImpl, const _Function & _Function) : _M_function(_Function), _PPLTaskHandle(_TaskImpl) - { - } - virtual ~_InitialTaskHandle() {} - -#if _MSC_VER >= 1800 - template - auto _LogWorkItemAndInvokeUserLambda(_Func && _func, _RetArg && _retArg) const -> decltype(_func(std::forward<_RetArg>(_retArg))) - { - details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); - return _func(std::forward<_RetArg>(_retArg)); - } -#endif - - void _Perform() const - { - _Init(_TypeSelection()); - } -#if _MSC_VER >= 1800 - - void _SyncCancelAndPropagateException() const - { - this->_M_pTask->_Cancel(true); - } -#endif - // - // Overload 0: returns _InternalReturnType - // - // This is the most basic task with no unwrapping - // - void _Init(details::_TypeSelectorNoAsync) const - { - _ReturnType retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Init_func_transformer<_InternalReturnType>::_Perform(_M_function), &retVal); -#else - HRESULT hr = _Init_func_transformer<_InternalReturnType>::_Perform(_M_function)(&retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - _M_pTask->_FinalizeAndRunContinuations(retVal); - } - - // - // Overload 1: returns IAsyncOperation<_InternalReturnType>* - // or - // returns task<_InternalReturnType> - // - // This is task whose functor returns an async operation or a task which will be unwrapped for continuation - // Depending on the output type, the right _AsyncInit gets invoked - // - void _Init(details::_TypeSelectorAsyncTask) const - { - task<_InternalReturnType> retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, &retVal); -#else - HRESULT hr = _M_function(&retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(_M_pTask, retVal); - } - void _Init(details::_TypeSelectorAsyncOperation) const - { - _ReturnType retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, &retVal); -#else - HRESULT hr = _M_function(&retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(_M_pTask, - Microsoft::WRL::Make>(retVal).Get()); - } - - // - // Overload 2: returns IAsyncAction* - // - // This is task whose functor returns an async action which will be unwrapped for continuation - // - void _Init(details::_TypeSelectorAsyncAction) const - { - _ReturnType retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, &retVal); -#else - HRESULT hr = _M_function(&retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(_M_pTask, Microsoft::WRL::Make(retVal).Get()); - } - - // - // Overload 3: returns IAsyncOperationWithProgress<_InternalReturnType, _ProgressType>* - // - // This is task whose functor returns an async operation with progress which will be unwrapped for continuation - // - void _Init(details::_TypeSelectorAsyncOperationWithProgress) const - { - typedef details::_GetProgressType::_Value _ProgressType; - _ReturnType retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, &retVal); -#else - HRESULT hr = _M_function(&retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(_M_pTask, - Microsoft::WRL::Make>(retVal).Get()); - } - - // - // Overload 4: returns IAsyncActionWithProgress<_ProgressType>* - // - // This is task whose functor returns an async action with progress which will be unwrapped for continuation - // - void _Init(details::_TypeSelectorAsyncActionWithProgress) const - { - typedef details::_GetProgressType::_Value _ProgressType; - _ReturnType retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, &retVal); -#else - HRESULT hr = _M_function(&retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(_M_pTask, - Microsoft::WRL::Make>(retVal).Get()); - } - }; - - /// - /// A helper class template that transforms a continuation lambda that either takes or returns void, or both, into a lambda that takes and returns a - /// non-void type (details::_Unit_type is used to substitute for void). This is to minimize the special handling required for 'void'. - /// - template - class _Continuation_func_transformer - { - public: - static auto _Perform(std::function _Func) -> decltype(_Func) - { - return _Func; - } - }; - - template - class _Continuation_func_transformer - { - public: - static auto _Perform(std::function _Func) -> decltype(details::_MakeUnitToTFunc<_OutType>(_Func)) - { - return details::_MakeUnitToTFunc<_OutType>(_Func); - } - }; - - template - class _Continuation_func_transformer<_InType, void> - { - public: - static auto _Perform(std::function _Func) -> decltype(details::_MakeTToUnitFunc<_InType>(_Func)) - { - return details::_MakeTToUnitFunc<_InType>(_Func); - } - }; - - template<> - class _Continuation_func_transformer - { - public: - static auto _Perform(std::function _Func) -> decltype(details::_MakeUnitToUnitFunc(_Func)) - { - return details::_MakeUnitToUnitFunc(_Func); - } - }; - /// - /// The task handle type used to create a 'continuation task'. - /// - template - struct _ContinuationTaskHandle : - details::_PPLTaskHandle::_Type, - _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> - { - typedef typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type _NormalizedContinuationReturnType; - - typename details::_Task_ptr<_ReturnType>::_Type _M_ancestorTaskImpl; - _Function _M_function; - - _ContinuationTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _AncestorImpl, - const typename details::_Task_ptr<_NormalizedContinuationReturnType>::_Type & _ContinuationImpl, - const _Function & _Func, const task_continuation_context & _Context, details::_TaskInliningMode _InliningMode) : -#if _MSC_VER >= 1800 - details::_PPLTaskHandle::_Type, - _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> - ::_PPLTaskHandle(_ContinuationImpl) - , _M_ancestorTaskImpl(_AncestorImpl) - , _M_function(_Func) -#else - _M_ancestorTaskImpl(_AncestorImpl), _PPLTaskHandle(_ContinuationImpl), _M_function(_Func) -#endif - { - _M_isTaskBasedContinuation = _IsTaskBased::value; - _M_continuationContext = _Context; - _M_continuationContext._Resolve(_AncestorImpl->_IsApartmentAware()); - _M_inliningMode = _InliningMode; - } - - virtual ~_ContinuationTaskHandle() {} - -#if _MSC_VER >= 1800 - template - auto _LogWorkItemAndInvokeUserLambda(_Func && _func, _Arg && _value, _RetArg && _retArg) const -> decltype(_func(std::forward<_Arg>(_value), std::forward<_RetArg>(_retArg))) - { - details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); - return _func(std::forward<_Arg>(_value), std::forward<_RetArg>(_retArg)); - } -#endif - - void _Perform() const - { - _Continue(_IsTaskBased(), _TypeSelection()); - } - -#if _MSC_VER >= 1800 - void _SyncCancelAndPropagateException() const - { - if (_M_ancestorTaskImpl->_HasUserException()) - { - // If the ancestor encountered an exception, transfer the exception to the continuation - // This traverses down the tree to propagate the exception. - this->_M_pTask->_CancelWithExceptionHolder(_M_ancestorTaskImpl->_GetExceptionHolder(), true); - } - else - { - // If the ancestor was canceled, then your own execution should be canceled. - // This traverses down the tree to cancel it. - this->_M_pTask->_Cancel(true); - } - } -#endif - - // - // Overload 0-0: _InternalReturnType -> _TaskType - // - // This is a straight task continuation which simply invokes its target with the ancestor's completion argument - // - void _Continue(std::false_type, details::_TypeSelectorNoAsync) const - { - _NormalizedContinuationReturnType retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _ContinuationReturnType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult(), &retVal); -#else - HRESULT hr =_Continuation_func_transformer<_InternalReturnType, _ContinuationReturnType>::_Perform(_M_function)(_M_ancestorTaskImpl->_GetResult(), &retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - _M_pTask->_FinalizeAndRunContinuations(retVal); - } - - // - // Overload 0-1: _InternalReturnType -> IAsyncOperation<_TaskType>* - // or - // _InternalReturnType -> task<_TaskType> - // - // This is a straight task continuation which returns an async operation or a task which will be unwrapped for continuation - // Depending on the output type, the right _AsyncInit gets invoked - // - void _Continue(std::false_type, details::_TypeSelectorAsyncTask) const - { - typedef typename details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; - _FuncOutputType retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult(), &retVal); -#else - HRESULT hr = _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function)(_M_ancestorTaskImpl->_GetResult(), &retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( - _M_pTask, - retVal - ); - } - void _Continue(std::false_type, details::_TypeSelectorAsyncOperation) const - { - typedef typename details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; - _FuncOutputType retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult(), &retVal); -#else - HRESULT hr = _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function)(_M_ancestorTaskImpl->_GetResult(), &retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( - _M_pTask, - Microsoft::WRL::Make>(retVal).Get()); - } - - // - // Overload 0-2: _InternalReturnType -> IAsyncAction* - // - // This is a straight task continuation which returns an async action which will be unwrapped for continuation - // - void _Continue(std::false_type, details::_TypeSelectorAsyncAction) const - { - typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; - _FuncOutputType retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult(), &retVal); -#else - HRESULT hr = _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function)(_M_ancestorTaskImpl->_GetResult(), &retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( - _M_pTask, - Microsoft::WRL::Make( - retVal).Get()); - } - - // - // Overload 0-3: _InternalReturnType -> IAsyncOperationWithProgress<_TaskType, _ProgressType>* - // - // This is a straight task continuation which returns an async operation with progress which will be unwrapped for continuation - // - void _Continue(std::false_type, details::_TypeSelectorAsyncOperationWithProgress) const - { - typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; - - _FuncOutputType _OpWithProgress; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult(), &_OpWithProgress); -#else - HRESULT hr = _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function)(_M_ancestorTaskImpl->_GetResult(), &_OpWithProgress); -#endif - typedef details::_GetProgressType::_Value _ProgressType; - - if (FAILED(hr)) throw std::make_exception_ptr(hr); - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( - _M_pTask, - Microsoft::WRL::Make>(_OpWithProgress).Get()); - } - - // - // Overload 0-4: _InternalReturnType -> IAsyncActionWithProgress<_ProgressType>* - // - // This is a straight task continuation which returns an async action with progress which will be unwrapped for continuation - // - void _Continue(std::false_type, details::_TypeSelectorAsyncActionWithProgress) const - { - typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; - - _FuncOutputType _OpWithProgress; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult(), &_OpWithProgress); -#else - HRESULT hr = _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function)(_M_ancestorTaskImpl->_GetResult(), &_OpWithProgress); -#endif - typedef details::_GetProgressType::_Value _ProgressType; - - if (FAILED(hr)) throw std::make_exception_ptr(hr); - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( - _M_pTask, - Microsoft::WRL::Make>(_OpWithProgress).Get()); - } - - - // - // Overload 1-0: task<_InternalReturnType> -> _TaskType - // - // This is an exception handling type of continuation which takes the task rather than the task's result. - // - void _Continue(std::true_type, details::_TypeSelectorNoAsync) const - { - typedef task<_InternalReturnType> _FuncInputType; - task<_InternalReturnType> _ResultTask; - _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); - _NormalizedContinuationReturnType retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_FuncInputType, _ContinuationReturnType>::_Perform(_M_function), std::move(_ResultTask), &retVal); -#else - HRESULT hr = _Continuation_func_transformer<_FuncInputType, _ContinuationReturnType>::_Perform(_M_function)(std::move(_ResultTask), &retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - _M_pTask->_FinalizeAndRunContinuations(retVal); - } - - // - // Overload 1-1: task<_InternalReturnType> -> IAsyncOperation<_TaskType>^ - // or - // task<_TaskType> - // - // This is an exception handling type of continuation which takes the task rather than - // the task's result. It also returns an async operation or a task which will be unwrapped - // for continuation - // - void _Continue(std::true_type, details::_TypeSelectorAsyncTask) const - { - // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. - task<_InternalReturnType> _ResultTask; - _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); - _ContinuationReturnType retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask), &retVal); -#else - HRESULT hr = _M_function(std::move(_ResultTask), &retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(_M_pTask, retVal); - } - void _Continue(std::true_type, details::_TypeSelectorAsyncOperation) const - { - // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. - task<_InternalReturnType> _ResultTask; - _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); - _ContinuationReturnType retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask), &retVal); -#else - HRESULT hr = _M_function(std::move(_ResultTask), &retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(_M_pTask, - Microsoft::WRL::Make>(retVal)); - } - - // - // Overload 1-2: task<_InternalReturnType> -> IAsyncAction* - // - // This is an exception handling type of continuation which takes the task rather than - // the task's result. It also returns an async action which will be unwrapped for continuation - // - void _Continue(std::true_type, details::_TypeSelectorAsyncAction) const - { - // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. - task<_InternalReturnType> _ResultTask; - _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); - _ContinuationReturnType retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask), &retVal); -#else - HRESULT hr = _M_function(std::move(_ResultTask), &retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(_M_pTask, - Microsoft::WRL::Make(retVal)); - } - - // - // Overload 1-3: task<_InternalReturnType> -> IAsyncOperationWithProgress<_TaskType, _ProgressType>* - // - // This is an exception handling type of continuation which takes the task rather than - // the task's result. It also returns an async operation with progress which will be unwrapped - // for continuation - // - void _Continue(std::true_type, details::_TypeSelectorAsyncOperationWithProgress) const - { - // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. - task<_InternalReturnType> _ResultTask; - _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); - - typedef details::_GetProgressType::_Value _ProgressType; - _ContinuationReturnType retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask), &retVal); -#else - HRESULT hr = _M_function(std::move(_ResultTask), &retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(_M_pTask, - Microsoft::WRL::Make>(retVal)); - } - - // - // Overload 1-4: task<_InternalReturnType> -> IAsyncActionWithProgress<_ProgressType>* - // - // This is an exception handling type of continuation which takes the task rather than - // the task's result. It also returns an async operation with progress which will be unwrapped - // for continuation - // - void _Continue(std::true_type, details::_TypeSelectorAsyncActionWithProgress) const - { - // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. - task<_InternalReturnType> _ResultTask; - _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); - - typedef details::_GetProgressType::_Value _ProgressType; - _ContinuationReturnType retVal; -#if _MSC_VER >= 1800 - HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask), &retVal); -#else - HRESULT hr = _M_function(std::move(_ResultTask), &retVal); -#endif - if (FAILED(hr)) throw std::make_exception_ptr(hr); - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(_M_pTask, - Microsoft::WRL::Make>(retVal)); - } - }; - /// - /// Initializes a task using a lambda, function pointer or function object. - /// - template - void _TaskInitWithFunctor(const _Function& _Func) - { - typedef details::_InitFunctorTypeTraits<_InternalReturnType, details::_FunctionTypeTraits<_Function, void>::_FuncRetType> _Async_type_traits; - - _M_Impl->_M_fFromAsync = _Async_type_traits::_IsAsyncTask; - _M_Impl->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; -#if _MSC_VER >= 1800 - _M_Impl->_M_taskEventLogger._LogScheduleTask(false); -#endif - _M_Impl->_ScheduleTask(new _InitialTaskHandle<_InternalReturnType, _Function, typename _Async_type_traits::_AsyncKind>(_GetImpl(), _Func), Concurrency::details::_NoInline); - } - - /// - /// Initializes a task using a task completion event. - /// - void _TaskInitNoFunctor(task_completion_event<_ReturnType>& _Event) - { - _Event._RegisterTask(_M_Impl); - } - - /// - /// Initializes a task using an asynchronous operation IAsyncOperation* - /// - template - void _TaskInitAsyncOp(details::_AsyncInfoImpl<_OpType, _CompHandlerType, _ResultType>* _AsyncOp) - { - _M_Impl->_M_fFromAsync = true; -#if _MSC_VER < 1800 - _M_Impl->_SetScheduledEvent(); -#endif - // Mark this task as started here since we can set the state in the constructor without acquiring a lock. Once _AsyncInit - // returns a completion could execute concurrently and the task must be fully initialized before that happens. - _M_Impl->_M_TaskState = details::_Task_impl_base::_Started; - // Pass the shared pointer into _AsyncInit for storage in the Async Callback. - details::_Task_impl_base::_AsyncInit<_ReturnType, _Result>(_M_Impl, _AsyncOp); - } - - /// - /// Initializes a task using an asynchronous operation IAsyncOperation* - /// - template - void _TaskInitNoFunctor(ABI::Windows::Foundation::IAsyncOperation<_Result>* _AsyncOp) - { - _TaskInitAsyncOp<_Result>(Microsoft::WRL::Make>(_AsyncOp).Get()); - } - - /// - /// Initializes a task using an asynchronous operation with progress IAsyncOperationWithProgress* - /// - template - void _TaskInitNoFunctor(ABI::Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress>* _AsyncOp) - { - _TaskInitAsyncOp<_Result>(Microsoft::WRL::Make>(_AsyncOp).Get()); - } - /// - /// Initializes a task using a callable object. - /// - template - void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) - { - _TaskInitWithFunctor<_ReturnType, _Function>(_Func); - } - - /// - /// Initializes a task using a non-callable object. - /// - template - void _TaskInitMaybeFunctor(_Ty & _Param, std::false_type) - { - _TaskInitNoFunctor(_Param); - } -#if _MSC_VER >= 1800 - template - auto _ThenImpl(const _Function& _Func, const task_options& _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType - { - if (!_M_Impl) - { - throw Concurrency::invalid_operation("then() cannot be called on a default constructed task."); - } - - Concurrency::details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; - auto _Scheduler = _TaskOptions.has_scheduler() ? _TaskOptions.get_scheduler() : _GetImpl()->_GetScheduler(); - auto _CreationStack = details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : details::_TaskCreationCallstack(); - return _ThenImpl<_InternalReturnType, _Function>(_Func, _PTokenState, _TaskOptions.get_continuation_context(), _Scheduler, _CreationStack); - } -#endif - /// - /// The one and only implementation of then for void and non-void tasks. - /// - template -#if _MSC_VER >= 1800 - auto _ThenImpl(const _Function& _Func, Concurrency::details::_CancellationTokenState *_PTokenState, const task_continuation_context& _ContinuationContext, Concurrency::scheduler_ptr _Scheduler, details::_TaskCreationCallstack _CreationStack, - details::_TaskInliningMode _InliningMode = Concurrency::details::_NoInline) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType -#else - auto _ThenImpl(const _Function& _Func, Concurrency::details::_CancellationTokenState *_PTokenState, const task_continuation_context& _ContinuationContext, - bool _Aggregating = false, details::_TaskInliningMode _InliningMode = Concurrency::details::_NoInline) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType -#endif - { - if (_M_Impl == nullptr) - { - throw Concurrency::invalid_operation("then() cannot be called on a default constructed task."); - } - - typedef details::_FunctionTypeTraits<_Function, _InternalReturnType> _Function_type_traits; - typedef details::_TaskTypeTraits _Async_type_traits; - typedef typename _Async_type_traits::_TaskRetType _TaskType; - - // - // A **nullptr** token state indicates that it was not provided by the user. In this case, we inherit the antecedent's token UNLESS this is a - // an exception handling continuation. In that case, we break the chain with a _None. That continuation is never canceled unless the user - // explicitly passes the same token. - // - if (_PTokenState == nullptr) - { -#if _MSC_VER >= 1800 - if (_Function_type_traits::_Takes_task::value) -#else - if (_Function_type_traits::_Takes_task()) -#endif - { - _PTokenState = Concurrency::details::_CancellationTokenState::_None(); - } - else - { - _PTokenState = _GetImpl()->_M_pTokenState; - } - } - - task<_TaskType> _ContinuationTask; -#if _MSC_VER >= 1800 - _ContinuationTask._CreateImpl(_PTokenState, _Scheduler); -#else - _ContinuationTask._CreateImpl(_PTokenState); -#endif - _ContinuationTask._GetImpl()->_M_fFromAsync = (_GetImpl()->_M_fFromAsync || _Async_type_traits::_IsAsyncTask); -#if _MSC_VER < 1800 - _ContinuationTask._GetImpl()->_M_fRuntimeAggregate = _Aggregating; -#endif - _ContinuationTask._GetImpl()->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; -#if _MSC_VER >= 1800 - _ContinuationTask._SetTaskCreationCallstack(_CreationStack); -#endif - _GetImpl()->_ScheduleContinuation(new _ContinuationTaskHandle<_InternalReturnType, _TaskType, _Function, typename _Function_type_traits::_Takes_task, typename _Async_type_traits::_AsyncKind>( - _GetImpl(), _ContinuationTask._GetImpl(), _Func, _ContinuationContext, _InliningMode)); - - return _ContinuationTask; - } - - // The underlying implementation for this task - typename details::_Task_ptr<_ReturnType>::_Type _M_Impl; -}; - -/// -/// The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed asynchronously, -/// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces -/// a result of type on successful completion. Tasks of type task<void> produce no result. -/// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using -/// continuations(then), and join(when_all) and choice(when_any) patterns. -/// -/// -/// For more information, see . -/// -/**/ -template<> -class task -{ -public: - /// - /// The type of the result an object of this class produces. - /// - /**/ - typedef void result_type; - - /// - /// Constructs a task object. - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lamda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - task() : _M_unitTask() - { - // The default constructor should create a task with a nullptr impl. This is a signal that the - // task is not usable and should throw if any wait(), get() or then() APIs are used. - } -#if _MSC_VER < 1800 - /// - /// Constructs a task object. - /// - /// - /// The type of the parameter from which the task is to be constructed. - /// - /// - /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> - /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function - /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, - /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lamda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result - explicit task(_Ty _Param) - { - details::_ValidateTaskConstructorArgs(_Param); - - _M_unitTask._CreateImpl(Concurrency::cancellation_token::none()._GetImplValue()); - // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of the task constructor. - _M_unitTask._SetTaskCreationAddressHint(_ReturnAddress()); - - _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param, 0, 0, 0)); - } -#endif - /// - /// Constructs a task object. - /// - /// - /// The type of the parameter from which the task is to be constructed. - /// - /// - /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> - /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function - /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, - /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. - /// - /// - /// The cancellation token to associate with this task. A task created without a cancellation token cannot be canceled. It implicitly receives - /// the token cancellation_token::none(). - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lamda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result -#if _MSC_VER >= 1800 - explicit task(_Ty _Param, const task_options& _TaskOptions = task_options()) -#else - explicit task(_Ty _Param, Concurrency::cancellation_token _CancellationToken) -#endif - { - details::_ValidateTaskConstructorArgs(_Param); -#if _MSC_VER >= 1800 - _M_unitTask._CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); -#else - _M_unitTask._CreateImpl(_CancellationToken._GetImplValue()); -#endif - // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of the task constructor. -#if _MSC_VER >= 1800 - _M_unitTask._SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : _CAPTURE_CALLSTACK()); -#else - _M_unitTask._SetTaskCreationAddressHint(_ReturnAddress()); -#endif - _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param, 0, 0, 0)); - } - - /// - /// Constructs a task object. - /// - /// - /// The source task object. - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lamda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - task(const task& _Other) : _M_unitTask(_Other._M_unitTask){} - - /// - /// Constructs a task object. - /// - /// - /// The source task object. - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lamda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - task(task&& _Other) : _M_unitTask(std::move(_Other._M_unitTask)) {} - - /// - /// Replaces the contents of one task object with another. - /// - /// - /// The source task object. - /// - /// - /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same - /// actual task as does. - /// - /**/ - task& operator=(const task& _Other) - { - if (this != &_Other) - { - _M_unitTask = _Other._M_unitTask; - } - return *this; - } - - /// - /// Replaces the contents of one task object with another. - /// - /// - /// The source task object. - /// - /// - /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same - /// actual task as does. - /// - /**/ - task& operator=(task&& _Other) - { - if (this != &_Other) - { - _M_unitTask = std::move(_Other._M_unitTask); - } - return *this; - } -#if _MSC_VER < 1800 - /// - /// Adds a continuation task to this task. - /// - /// - /// The type of the function object that will be invoked by this task. - /// - /// - /// The continuation function to execute when this task completes. This continuation function must take as input - /// a variable of either result_type or task<result_type>, where result_type is the type - /// of the result this task produces. - /// - /// - /// The newly created continuation task. The result type of the returned task is determined by what returns. - /// - /// - /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available - /// to Windows Store apps. - /// For more information on how to use task continuations to compose asynchronous work, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result - auto then(const _Function& _Func) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType - { - auto _ContinuationTask = _M_unitTask._ThenImpl(_Func, nullptr, task_continuation_context::use_default()); - // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. - _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); - return _ContinuationTask; - } -#endif - /// - /// Adds a continuation task to this task. - /// - /// - /// The type of the function object that will be invoked by this task. - /// - /// - /// The continuation function to execute when this task completes. This continuation function must take as input - /// a variable of either result_type or task<result_type>, where result_type is the type - /// of the result this task produces. - /// - /// - /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit - /// the token of its antecedent task. - /// - /// - /// The newly created continuation task. The result type of the returned task is determined by what returns. - /// - /// - /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available - /// to Windows Store apps. - /// For more information on how to use task continuations to compose asynchronous work, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result -#if _MSC_VER >= 1800 - auto then(const _Function& _Func, task_options _TaskOptions = task_options()) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType - { - details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - return _M_unitTask._ThenImpl(_Func, _TaskOptions); - } -#else - auto then(const _Function& _Func, Concurrency::cancellation_token _CancellationToken) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType - { - auto _ContinuationTask = _M_unitTask._ThenImpl(_Func, _CancellationToken._GetImplValue(), task_continuation_context::use_default()); - // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. - _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); - return _ContinuationTask; - } - /// - /// Adds a continuation task to this task. - /// - /// - /// The type of the function object that will be invoked by this task. - /// - /// - /// The continuation function to execute when this task completes. This continuation function must take as input - /// a variable of either result_type or task<result_type>, where result_type is the type - /// of the result this task produces. - /// - /// - /// A variable that specifies where the continuation should execute. This variable is only useful when used in a - /// Windows Store app. For more information, see task_continuation_context - /// - /// - /// The newly created continuation task. The result type of the returned task is determined by what returns. - /// - /// - /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available - /// to Windows Store apps. - /// For more information on how to use task continuations to compose asynchronous work, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result - auto then(const _Function& _Func, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType - { - auto _ContinuationTask = _M_unitTask._ThenImpl(_Func, nullptr, _ContinuationContext); - // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. - _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); - return _ContinuationTask; - - } -#endif - /// - /// Adds a continuation task to this task. - /// - /// - /// The type of the function object that will be invoked by this task. - /// - /// - /// The continuation function to execute when this task completes. This continuation function must take as input - /// a variable of either result_type or task<result_type>, where result_type is the type - /// of the result this task produces. - /// - /// - /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit - /// the token of its antecedent task. - /// - /// - /// A variable that specifies where the continuation should execute. This variable is only useful when used in a - /// Windows Store app. For more information, see task_continuation_context - /// - /// - /// The newly created continuation task. The result type of the returned task is determined by what returns. - /// - /// - /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available - /// to Windows Store apps. - /// For more information on how to use task continuations to compose asynchronous work, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result -#if _MSC_VER >= 1800 - auto then(const _Function& _Func, Concurrency::cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType - { - task_options _TaskOptions(_CancellationToken, _ContinuationContext); - details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - return _M_unitTask._ThenImpl(_Func, _TaskOptions); - } -#else - auto then(const _Function& _Func, Concurrency::cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType - { - auto _ContinuationTask = _M_unitTask._ThenImpl(_Func, _CancellationToken._GetImplValue(), _ContinuationContext); - // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. - _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); - return _ContinuationTask; - } -#endif - - /// - /// Waits for this task to reach a terminal state. It is possible for wait to execute the task inline, if all of the tasks - /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. - /// - /// - /// A task_status value which could be either completed or canceled. If the task encountered an exception - /// during execution, or an exception was propagated to it from an antecedent task, wait will throw that exception. - /// - /**/ - task_status wait() const - { - return _M_unitTask.wait(); - } - - /// - /// Returns the result this task produced. If the task is not in a terminal state, a call to get will wait for the task to - /// finish. This method does not return a value when called on a task with a result_type of void. - /// - /// - /// If the task is canceled, a call to get will throw a task_canceled exception. If the task - /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to get will throw that exception. - /// - /**/ - void get() const - { - _M_unitTask.get(); - } -#if _MSC_VER >= 1800 - - /// - /// Determines if the task is completed. - /// - /// - /// True if the task has completed, false otherwise. - /// - /// - /// The function returns true if the task is completed or canceled (with or without user exception). - /// - bool is_done() const - { - return _M_unitTask.is_done(); - } - - /// - /// Returns the scheduler for this task - /// - /// - /// A pointer to the scheduler - /// - Concurrency::scheduler_ptr scheduler() const - { - return _M_unitTask.scheduler(); - } -#endif - /// - /// Determines whether the task unwraps a Windows Runtime IAsyncInfo interface or is descended from such a task. - /// - /// - /// true if the task unwraps an IAsyncInfo interface or is descended from such a task, false otherwise. - /// - /**/ - bool is_apartment_aware() const - { - return _M_unitTask.is_apartment_aware(); - } - - /// - /// Determines whether two task objects represent the same internal task. - /// - /// - /// true if the objects refer to the same underlying task, and false otherwise. - /// - /**/ - bool operator==(const task& _Rhs) const - { - return (_M_unitTask == _Rhs._M_unitTask); - } - - /// - /// Determines whether two task objects represent different internal tasks. - /// - /// - /// true if the objects refer to different underlying tasks, and false otherwise. - /// - /**/ - bool operator!=(const task& _Rhs) const - { - return !operator==(_Rhs); - } - - /// - /// Create an underlying task implementation. - /// -#if _MSC_VER >= 1800 - void _CreateImpl(Concurrency::details::_CancellationTokenState * _Ct, Concurrency::scheduler_ptr _Scheduler) - { - _M_unitTask._CreateImpl(_Ct, _Scheduler); - } -#else - void _CreateImpl(Concurrency::details::_CancellationTokenState * _Ct) - { - _M_unitTask._CreateImpl(_Ct); - } -#endif - - /// - /// Return the underlying implementation for this task. - /// - const details::_Task_ptr::_Type & _GetImpl() const - { - return _M_unitTask._M_Impl; - } - - /// - /// Set the implementation of the task to be the supplied implementaion. - /// - void _SetImpl(const details::_Task_ptr::_Type & _Impl) - { - _M_unitTask._SetImpl(_Impl); - } - - /// - /// Set the implementation of the task to be the supplied implementaion using a move instead of a copy. - /// - void _SetImpl(details::_Task_ptr::_Type && _Impl) - { - _M_unitTask._SetImpl(std::move(_Impl)); - } - - /// - /// Sets a property determining whether the task is apartment aware. - /// - void _SetAsync(bool _Async = true) - { - _M_unitTask._SetAsync(_Async); - } - - /// - /// Sets a field in the task impl to the return address for calls to the task constructors and the then method. - /// -#if _MSC_VER >= 1800 - void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) - { - _M_unitTask._SetTaskCreationCallstack(_callstack); - } -#else - void _SetTaskCreationAddressHint(void* _Address) - { - _M_unitTask._SetTaskCreationAddressHint(_Address); - } -#endif - - /// - /// An internal version of then that takes additional flags and executes the continuation inline. Used for runtime internal continuations only. - /// - template -#if _MSC_VER >= 1800 - auto _Then(const _Function& _Func, Concurrency::details::_CancellationTokenState *_PTokenState, - details::_TaskInliningMode _InliningMode = Concurrency::details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType - { - // inherit from antecedent - auto _Scheduler = _GetImpl()->_GetScheduler(); - - return _M_unitTask._ThenImpl(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); - } -#else - auto _Then(const _Function& _Func, Concurrency::details::_CancellationTokenState *_PTokenState, - bool _Aggregating, details::_TaskInliningMode _InliningMode = Concurrency::details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType - { - return _M_unitTask._ThenImpl(_Func, _PTokenState, task_continuation_context::use_default(), _Aggregating, _InliningMode); - } -#endif - -private: - template friend class task; - template friend class task_completion_event; - - /// - /// Initializes a task using a task completion event. - /// - void _TaskInitNoFunctor(task_completion_event& _Event) - { - _M_unitTask._TaskInitNoFunctor(_Event._M_unitEvent); - } - /// - /// Initializes a task using an asynchronous action IAsyncAction* - /// - void _TaskInitNoFunctor(ABI::Windows::Foundation::IAsyncAction* _AsyncAction) - { - _M_unitTask._TaskInitAsyncOp(Microsoft::WRL::Make(_AsyncAction).Get()); - } - - /// - /// Initializes a task using an asynchronous action with progress IAsyncActionWithProgress<_P>* - /// - template - void _TaskInitNoFunctor(ABI::Windows::Foundation::IAsyncActionWithProgress<_P>* _AsyncActionWithProgress) - { - _M_unitTask._TaskInitAsyncOp(Microsoft::WRL::Make>(_AsyncActionWithProgress).Get()); - } - /// - /// Initializes a task using a callable object. - /// - template - void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) - { - _M_unitTask._TaskInitWithFunctor(_Func); - } - - /// - /// Initializes a task using a non-callable object. - /// - template - void _TaskInitMaybeFunctor(_T & _Param, std::false_type) - { - _TaskInitNoFunctor(_Param); - } - - // The void task contains a task of a dummy type so common code can be used for tasks with void and non-void results. - task _M_unitTask; -}; - -namespace details -{ - - /// - /// The following type traits are used for the create_task function. - /// - - // Unwrap task - template - _Ty _GetUnwrappedType(task<_Ty>); - - // Unwrap all supported types - template - auto _GetUnwrappedReturnType(_Ty _Arg, int) -> decltype(_GetUnwrappedType(_Arg)); - // fallback - template - _Ty _GetUnwrappedReturnType(_Ty, ...); - - /// - /// _GetTaskType functions will retrieve task type T in task[T](Arg), - /// for given constructor argument Arg and its property "callable". - /// It will automatically unwrap argument to get the final return type if necessary. - /// - - // Non-Callable - template - _Ty _GetTaskType(task_completion_event<_Ty>, std::false_type); - - // Non-Callable - template - auto _GetTaskType(_Ty _NonFunc, std::false_type) -> decltype(_GetUnwrappedType(_NonFunc)); - - // Callable - template - auto _GetTaskType(_Ty _Func, std::true_type) -> decltype(_GetUnwrappedReturnType(stdx::declval<_FunctionTypeTraits<_Ty, void>::_FuncRetType>(), 0)); - - // Special callable returns void - void _GetTaskType(std::function, std::true_type); - struct _BadArgType{}; - - template - auto _FilterValidTaskType(_Ty _Param, int) -> decltype(_GetTaskType(_Param, _IsCallable<_ReturnType>(_Param, 0, 0, 0))); - - template - _BadArgType _FilterValidTaskType(_Ty _Param, ...); - - template - struct _TaskTypeFromParam - { - typedef decltype(_FilterValidTaskType<_ReturnType>(stdx::declval<_Ty>(), 0)) _Type; - }; -} - - -/// -/// Creates a PPL task object. create_task can be used anywhere you would have used a task constructor. -/// It is provided mainly for convenience, because it allows use of the auto keyword while creating tasks. -/// -/// -/// The type of the parameter from which the task is to be constructed. -/// -/// -/// The parameter from which the task is to be constructed. This could be a lambda or function object, a task_completion_event -/// object, a different task object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. -/// -/// -/// A new task of type T, that is inferred from . -/// -/// -/// The first overload behaves like a task constructor that takes a single parameter. -/// The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not -/// allowed to pass in a different task object as the first parameter. -/// The type of the returned task is inferred from the first parameter to the function. If is a task_completion_event<T>, -/// a task<T>, or a functor that returns either type T or task<T>, the type of the created task is task<T>. -/// In a Windows Store app, if is of type Windows::Foundation::IAsyncOperation<T>^ or -/// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type task<T>. -/// If is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor -/// that returns either of those types, the created task will have type task<void>. -/// -/// -/// -/**/ -template -__declspec(noinline) -#if _MSC_VER >= 1800 -auto create_task(_Ty _Param, task_options _TaskOptions = task_options()) -> task::_Type> -#else -auto create_task(_Ty _Param) -> task::_Type> -#endif -{ - static_assert(!std::is_same::_Type, details::_BadArgType>::value, - "incorrect argument for create_task; can be a callable object, an asynchronous operation, or a task_completion_event" - ); -#if _MSC_VER >= 1800 - details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - task::_Type> _CreatedTask(_Param, _TaskOptions); -#else - task::_Type> _CreatedTask(_Param); - // Ideally we would like to forceinline create_task, but __forceinline does nothing on debug builds. Therefore, we ask for no inlining - // and overwrite the creation address hint set by the task constructor. DO NOT REMOVE this next line from create_task. It is - // essential that _ReturnAddress() evaluate to the instruction right after the call to create_task in client code. - _CreatedTask._SetTaskCreationAddressHint(_ReturnAddress()); -#endif - return _CreatedTask; -} - -/// -/// Creates a PPL task object. create_task can be used anywhere you would have used a task constructor. -/// It is provided mainly for convenience, because it allows use of the auto keyword while creating tasks. -/// -/// -/// The type of the parameter from which the task is to be constructed. -/// -/// -/// The parameter from which the task is to be constructed. This could be a lambda or function object, a task_completion_event -/// object, a different task object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. -/// -/// -/// The cancellation token to associate with the task. When the source for this token is canceled, cancellation will be requested on the task. -/// -/// -/// A new task of type T, that is inferred from . -/// -/// -/// The first overload behaves like a task constructor that takes a single parameter. -/// The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not -/// allowed to pass in a different task object as the first parameter. -/// The type of the returned task is inferred from the first parameter to the function. If is a task_completion_event<T>, -/// a task<T>, or a functor that returns either type T or task<T>, the type of the created task is task<T>. -/// In a Windows Store app, if is of type Windows::Foundation::IAsyncOperation<T>^ or -/// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type task<T>. -/// If is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor -/// that returns either of those types, the created task will have type task<void>. -/// -/// -/// -/**/ -#if _MSC_VER >= 1800 -template -__declspec(noinline) -task<_ReturnType> create_task(const task<_ReturnType>& _Task) -{ - task<_ReturnType> _CreatedTask(_Task); - return _CreatedTask; -} -#else -template -__declspec(noinline) -auto create_task(_Ty _Param, Concurrency::cancellation_token _Token) -> task::_Type> -{ - static_assert(!std::is_same::_Type, details::_BadArgType>::value, - "incorrect argument for create_task; can be a callable object, an asynchronous operation, or a task_completion_event" - ); - task::_Type> _CreatedTask(_Param, _Token); - // Ideally we would like to forceinline create_task, but __forceinline does nothing on debug builds. Therefore, we ask for no inlining - // and overwrite the creation address hint set by the task constructor. DO NOT REMOVE this next line from create_task. It is - // essential that _ReturnAddress() evaluate to the instruction right after the call to create_task in client code. - _CreatedTask._SetTaskCreationAddressHint(_ReturnAddress()); - return _CreatedTask; -} -#endif - -namespace details -{ - template - task*>()))>::type> _To_task_helper(ABI::Windows::Foundation::IAsyncOperation<_T>* op) - { - return task<_T>(op); - } - - template - task*>()))>::type> _To_task_helper(ABI::Windows::Foundation::IAsyncOperationWithProgress<_T, _Progress>* op) - { - return task<_T>(op); - } - - inline task _To_task_helper(ABI::Windows::Foundation::IAsyncAction* op) - { - return task(op); - } - - template - task _To_task_helper(ABI::Windows::Foundation::IAsyncActionWithProgress<_Progress>* op) - { - return task(op); - } - - template - class _ProgressDispatcherBase - { - public: - - virtual ~_ProgressDispatcherBase() - { - } - - virtual void _Report(const _ProgressType& _Val) = 0; - }; - - template - class _ProgressDispatcher : public _ProgressDispatcherBase<_ProgressType> - { - public: - - virtual ~_ProgressDispatcher() - { - } - - _ProgressDispatcher(_ClassPtrType _Ptr) : _M_ptr(_Ptr) - { - } - - virtual void _Report(const _ProgressType& _Val) - { - _M_ptr->_FireProgress(_Val); - } - - private: - - _ClassPtrType _M_ptr; - }; -} // namespace details - - -/// -/// The progress reporter class allows reporting progress notifications of a specific type. Each progress_reporter object is bound -/// to a particular asynchronous action or operation. -/// -/// -/// The payload type of each progress notification reported through the progress reporter. -/// -/// -/// This type is only available to Windows Store apps. -/// -/// -/**/ -template -class progress_reporter -{ - typedef std::shared_ptr> _PtrType; - -public: - - /// - /// Sends a progress report to the asynchronous action or operation to which this progress reporter is bound. - /// - /// - /// The payload to report through a progress notification. - /// - /**/ - void report(const _ProgressType& _Val) const - { - _M_dispatcher->_Report(_Val); - } - - template - static progress_reporter _CreateReporter(_ClassPtrType _Ptr) - { - progress_reporter _Reporter; - details::_ProgressDispatcherBase<_ProgressType> *_PDispatcher = new details::_ProgressDispatcher<_ProgressType, _ClassPtrType>(_Ptr); - _Reporter._M_dispatcher = _PtrType(_PDispatcher); - return _Reporter; - } - progress_reporter() {} - -private: - progress_reporter(details::_ProgressReporterCtorArgType); - - _PtrType _M_dispatcher; -}; - -namespace details -{ - // - // maps internal definitions for AsyncStatus and defines states that are not client visible - // - enum _AsyncStatusInternal - { - _AsyncCreated = -1, // externally invisible - // client visible states (must match AsyncStatus exactly) - _AsyncStarted = ABI::Windows::Foundation::AsyncStatus::Started, // 0 - _AsyncCompleted = ABI::Windows::Foundation::AsyncStatus::Completed, // 1 - _AsyncCanceled = ABI::Windows::Foundation::AsyncStatus::Canceled, // 2 - _AsyncError = ABI::Windows::Foundation::AsyncStatus::Error, // 3 - // non-client visible internal states - _AsyncCancelPending, - _AsyncClosed, - _AsyncUndefined - }; - - // - // designates whether the "GetResults" method returns a single result (after complete fires) or multiple results - // (which are progressively consumable between Start state and before Close is called) - // - enum _AsyncResultType - { - SingleResult = 0x0001, - MultipleResults = 0x0002 - }; - - template - struct _ProgressTypeTraits - { - static const bool _TakesProgress = false; - typedef void _ProgressType; - }; - - template - struct _ProgressTypeTraits> - { - static const bool _TakesProgress = true; - typedef typename _T _ProgressType; - }; - - template::value, bool bTakesProgress = _ProgressTypeTraits<_T>::_TakesProgress> - struct _TokenTypeTraits - { - static const bool _TakesToken = false; - typedef typename _T _ReturnType; - }; - - template - struct _TokenTypeTraits<_T, false, true> - { - static const bool _TakesToken = false; - typedef void _ReturnType; - }; - - template - struct _TokenTypeTraits<_T, true, false> - { - static const bool _TakesToken = true; - typedef void _ReturnType; - }; - - template::_ArgumentCount> - struct _CAFunctorOptions - { - static const bool _TakesProgress = false; - static const bool _TakesToken = false; - typedef void _ProgressType; - typedef void _ReturnType; - }; - - template - struct _CAFunctorOptions<_T, 1> - { - private: - - typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; - - public: - - static const bool _TakesProgress = _ProgressTypeTraits<_Argument1Type>::_TakesProgress; - static const bool _TakesToken = _TokenTypeTraits<_Argument1Type>::_TakesToken; - typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; - typedef typename _TokenTypeTraits<_Argument1Type>::_ReturnType _ReturnType; - }; - - template - struct _CAFunctorOptions<_T, 2> - { - private: - - typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; - typedef typename _FunctorTypeTraits<_T>::_Argument2Type _Argument2Type; - - public: - - static const bool _TakesProgress = _ProgressTypeTraits<_Argument1Type>::_TakesProgress; - static const bool _TakesToken = !_TakesProgress ? true : _TokenTypeTraits<_Argument2Type>::_TakesToken; - typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; - typedef typename _TokenTypeTraits<_Argument2Type>::_ReturnType _ReturnType; - }; - - template - struct _CAFunctorOptions<_T, 3> - { - private: - - typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; - - public: - - static const bool _TakesProgress = true; - static const bool _TakesToken = true; - typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; - typedef typename _FunctorTypeTraits<_T>::_Argument3Type _ReturnType; - }; - - class _Zip - { - }; - - // *************************************************************************** - // Async Operation Task Generators - // - - // - // Functor returns an IAsyncInfo - result needs to be wrapped in a task: - // - template - struct _SelectorTaskGenerator - { -#if _MSC_VER >= 1800 - template - static task<_ReturnType> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>(_Func(_pRet), _taskOptinos); - } - - template - static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>(_Func(_Cts.get_token(), _pRet), _taskOptinos); - } - - template - static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>(_Func(_Progress, _pRet), _taskOptinos); - } - - template - static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>(_Func(_Progress, _Cts.get_token(), _pRet), _taskOptinos); - } -#else - template - static task<_ReturnType> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - { - return task<_ReturnType>(_Func(_pRet), _Cts.get_token()); - } - - template - static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - { - return task<_ReturnType>(_Func(_Cts.get_token(), _pRet), _Cts.get_token()); - } - - template - static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - { - return task<_ReturnType>(_Func(_Progress, _pRet), _Cts.get_token()); - } - - template - static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - { - return task<_ReturnType>(_Func(_Progress, _Cts.get_token(), _pRet), _Cts.get_token()); - } -#endif - }; - - template - struct _SelectorTaskGenerator<_AsyncSelector, void> - { -#if _MSC_VER >= 1800 - template - static task _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task(_Func(), _taskOptinos); - } - - template - static task _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task(_Func(_Cts.get_token()), _taskOptinos); - } - - template - static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task(_Func(_Progress), _taskOptinos); - } - - template - static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task(_Func(_Progress, _Cts.get_token()), _taskOptinos); - } -#else - template - static task _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts) - { - return task(_Func(), _Cts.get_token()); - } - - template - static task _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts) - { - return task(_Func(_Cts.get_token()), _Cts.get_token()); - } - - template - static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts) - { - return task(_Func(_Progress), _Cts.get_token()); - } - - template - static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts) - { - return task(_Func(_Progress, _Cts.get_token()), _Cts.get_token()); - } -#endif - }; - -#if _MSC_VER < 1800 - // For create_async lambdas that return a (non-task) result, we oversubscriber the current task for the duration of the - // lambda. - struct _Task_generator_oversubscriber - { - _Task_generator_oversubscriber() - { - Concurrency::details::_Context::_Oversubscribe(true); - } - - ~_Task_generator_oversubscriber() - { - Concurrency::details::_Context::_Oversubscribe(false); - } - }; -#endif - - // - // Functor returns a result - it needs to be wrapped in a task: - // - template - struct _SelectorTaskGenerator - { -#if _MSC_VER >= 1800 - -#pragma warning(push) -#pragma warning(disable: 4702) - template - static task<_ReturnType> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { - Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - HRESULT hr = _Func(_pRet); - retVal = _pRet; - return hr; - }, _taskOptinos); - } -#pragma warning(pop) - - template - static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { - Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - HRESULT hr = _Func(_Cts.get_token(), _pRet); - retVal = _pRet; - return hr; - }, _taskOptinos); - } - - template - static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { - Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - HRESULT hr = _Func(_Progress, _pRet); - retVal = _pRet; - return hr; - }, _taskOptinos); - } - - template - static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { - Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - HRESULT hr = _Func(_Progress, _Cts.get_token(), _pRet); - retVal = _pRet; - return hr; - }, _taskOptinos); - } -#else - template - static task<_ReturnType> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - { - return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { - _Task_generator_oversubscriber _Oversubscriber; - HRESULT hr = _Func(_pRet); - retVal = _pRet; - return hr; - }, _Cts.get_token()); - } - - template - static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - { - return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { - _Task_generator_oversubscriber _Oversubscriber; - HRESULT hr = _Func(_Cts.get_token(), _pRet); - retVal = _pRet; - return hr; - }, _Cts.get_token()); - } - - template - static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - { - return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { - _Task_generator_oversubscriber _Oversubscriber; - HRESULT hr = _Func(_Progress, _pRet); - retVal = _pRet; - return hr; - }, _Cts.get_token()); - } - - template - static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - { - return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { - _Task_generator_oversubscriber _Oversubscriber; - HRESULT hr = _Func(_Progress, _Cts.get_token(), _pRet); - retVal = _pRet; - return hr; - }, _Cts.get_token()); - } -#endif - }; - - template<> - struct _SelectorTaskGenerator - { -#if _MSC_VER >= 1800 - template - static task _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task([=]() -> HRESULT { - Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - return _Func(); - }, _taskOptinos); - } - - template - static task _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task([=]() -> HRESULT { - Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - return _Func(_Cts.get_token()); - }, _taskOptinos); - } - - template - static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task([=]() -> HRESULT { - Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - return _Func(_Progress); - }, _taskOptinos); - } - - template - static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task([=]() -> HRESULT { - Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - return _Func(_Progress, _Cts.get_token()); - }, _taskOptinos); - } -#else - template - static task _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts) - { - return task([=]() -> HRESULT { - _Task_generator_oversubscriber _Oversubscriber; - return _Func(); - }, _Cts.get_token()); - } - - template - static task _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts) - { - return task([=]() -> HRESULT { - _Task_generator_oversubscriber _Oversubscriber; - return _Func(_Cts.get_token()); - }, _Cts.get_token()); - } - - template - static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts) - { - return task([=]() -> HRESULT { - _Task_generator_oversubscriber _Oversubscriber; - return _Func(_Progress); - }, _Cts.get_token()); - } - - template - static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts) - { - return task([=]() -> HRESULT { - _Task_generator_oversubscriber _Oversubscriber; - return _Func(_Progress, _Cts.get_token()); - }, _Cts.get_token()); - } -#endif - }; - - // - // Functor returns a task - the task can directly be returned: - // - template - struct _SelectorTaskGenerator - { - template -#if _MSC_VER >= 1800 - static task<_ReturnType> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) -#else - static task<_ReturnType> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) -#endif - { - task<_ReturnType> _task; - _Func(&_task); - return _task; - } - - template -#if _MSC_VER >= 1800 - static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) -#else - static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) -#endif - { - task<_ReturnType> _task; - _Func(_Cts.get_token(), &_task); - return _task; - } - - template -#if _MSC_VER >= 1800 - static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) -#else - static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) -#endif - { - task<_ReturnType> _task; - _Func(_Progress, &_task); - return _task; - } - - template -#if _MSC_VER >= 1800 - static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) -#else - static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) -#endif - { - task<_ReturnType> _task; - _Func(_Progress, _Cts.get_token(), &_task); - return _task; - } - }; - - template<> - struct _SelectorTaskGenerator - { - template -#if _MSC_VER >= 1800 - static task _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) -#else - static task _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts) -#endif - { - task _task; - _Func(&_task); - return _task; - } - - template -#if _MSC_VER >= 1800 - static task _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) -#else - static task _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts) -#endif - { - task _task; - _Func(_Cts.get_token(), &_task); - return _task; - } - - template -#if _MSC_VER >= 1800 - static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) -#else - static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts) -#endif - { - task _task; - _Func(_Progress, &_task); - return _task; - } - - template -#if _MSC_VER >= 1800 - static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) -#else - static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts) -#endif - { - task _task; - _Func(_Progress, _Cts.get_token(), &_task); - return _task; - } - }; - - template - struct _TaskGenerator - { - }; - - template - struct _TaskGenerator<_Generator, false, false> - { -#if _MSC_VER >= 1800 - template - static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) - { - (void)_Ptr; - return _Generator::_GenerateTask_0(_Func, _Cts, _callstack); - } - - template - static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet, _callstack)) - { - return _Generator::_GenerateTask_0(_Func, _Cts, _pRet, _callstack); - } -#else - template - static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts)) - { - (void)_Ptr; - return _Generator::_GenerateTask_0(_Func, _Cts); - } - - template - static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet)) - { - return _Generator::_GenerateTask_0(_Func, _Cts, _pRet); - } -#endif - }; - - template - struct _TaskGenerator<_Generator, true, false> - { -#if _MSC_VER >= 1800 - template - static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) - { - return _Generator::_GenerateTask_1C(_Func, _Cts, _callstack); - } - - template - static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet, _callstack)) - { - return _Generator::_GenerateTask_1C(_Func, _Cts, _pRet, _callstack); - } -#else - template - static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts)) - { - return _Generator::_GenerateTask_1C(_Func, _Cts); - } - - template - static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet)) - { - return _Generator::_GenerateTask_1C(_Func, _Cts, _pRet); - } -#endif - }; - - template - struct _TaskGenerator<_Generator, false, true> - { -#if _MSC_VER >= 1800 - template - static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) - { - return _Generator::_GenerateTask_1P(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); - } - - template - static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet, _callstack)) - { - return _Generator::_GenerateTask_1P(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _pRet, _callstack); - } -#else - template - static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts)) - { - return _Generator::_GenerateTask_1P(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts); - } - - template - static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet)) - { - return _Generator::_GenerateTask_1P(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _pRet); - } -#endif - }; - - template - struct _TaskGenerator<_Generator, true, true> - { -#if _MSC_VER >= 1800 - template - static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) - { - return _Generator::_GenerateTask_2PC(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); - } - - template - static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet, _callstack)) - { - return _Generator::_GenerateTask_2PC(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _pRet, _callstack); - } -#else - template - static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts)) - { - return _Generator::_GenerateTask_2PC(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts); - } - - template - static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet)) - { - return _Generator::_GenerateTask_2PC(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _pRet); - } -#endif - }; - - // *************************************************************************** - // Async Operation Attributes Classes - // - // These classes are passed through the hierarchy of async base classes in order to hold multiple attributes of a given async construct in - // a single container. An attribute class must define: - // - // Mandatory: - // ------------------------- - // - // _AsyncBaseType : The Windows Runtime interface which is being implemented. - // _CompletionDelegateType : The Windows Runtime completion delegate type for the interface. - // _ProgressDelegateType : If _TakesProgress is true, the Windows Runtime progress delegate type for the interface. If it is false, an empty Windows Runtime type. - // _ReturnType : The return type of the async construct (void for actions / non-void for operations) - // - // _TakesProgress : An indication as to whether or not - // - // _Generate_Task : A function adapting the user's function into what's necessary to produce the appropriate task - // - // Optional: - // ------------------------- - // - - template - struct _AsyncAttributes - { - }; - - template - struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, true> - { - typedef typename ABI::Windows::Foundation::IAsyncOperationWithProgress<_ReturnType, _ProgressType> _AsyncBaseType; - typedef typename ABI::Windows::Foundation::IAsyncOperationProgressHandler<_ReturnType, _ProgressType> _ProgressDelegateType; - typedef typename ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<_ReturnType, _ProgressType> _CompletionDelegateType; - typedef typename _ReturnType _ReturnType; - typedef typename ABI::Windows::Foundation::Internal::GetAbiType()))>::type _ReturnType_abi; - typedef typename _ProgressType _ProgressType; - typedef typename ABI::Windows::Foundation::Internal::GetAbiType()))>::type _ProgressType_abi; - typedef typename _TaskTraits::_AsyncKind _AsyncKind; - typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; - typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; - - static const bool _TakesProgress = true; - static const bool _TakesToken = _TakesToken; - - template -#if _MSC_VER >= 1800 - static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - { - return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType_abi, _ReturnType>(_Func, _Ptr, _Cts, _pRet, _callstack); - } -#else - static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - { - return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType_abi, _ReturnType>(_Func, _Ptr, _Cts, _pRet); - } -#endif - }; - - template - struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, false> - { - typedef typename ABI::Windows::Foundation::IAsyncOperation<_ReturnType> _AsyncBaseType; - typedef _Zip _ProgressDelegateType; - typedef typename ABI::Windows::Foundation::IAsyncOperationCompletedHandler<_ReturnType> _CompletionDelegateType; - typedef typename _ReturnType _ReturnType; - typedef typename ABI::Windows::Foundation::Internal::GetAbiType()))>::type _ReturnType_abi; - typedef typename _TaskTraits::_AsyncKind _AsyncKind; - typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; - typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; - - static const bool _TakesProgress = false; - static const bool _TakesToken = _TakesToken; - - template -#if _MSC_VER >= 1800 - static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - { - return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType, _ReturnType>(_Func, _Ptr, _Cts, _pRet, _callstack); - } -#else - static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - { - return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType, _ReturnType>(_Func, _Ptr, _Cts, _pRet); - } -#endif - }; - - template - struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, true> - { - typedef typename ABI::Windows::Foundation::IAsyncActionWithProgress<_ProgressType> _AsyncBaseType; - typedef typename ABI::Windows::Foundation::IAsyncActionProgressHandler<_ProgressType> _ProgressDelegateType; - typedef typename ABI::Windows::Foundation::IAsyncActionWithProgressCompletedHandler<_ProgressType> _CompletionDelegateType; - typedef void _ReturnType; - typedef void _ReturnType_abi; - typedef typename _ProgressType _ProgressType; - typedef typename ABI::Windows::Foundation::Internal::GetAbiType()))>::type _ProgressType_abi; - typedef typename _TaskTraits::_AsyncKind _AsyncKind; - typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; - typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; - - static const bool _TakesProgress = true; - static const bool _TakesToken = _TakesToken; - -#if _MSC_VER >= 1800 - template - static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - return _TaskGenerator::_GenerateTaskNoRet<_Function, _ClassPtr, _ProgressType_abi>(_Func, _Ptr, _Cts, _callstack); - } - template - static task> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - { - return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType_abi>(_Func, _Ptr, _Cts, _pRet, _callstack); - } -#else - template - static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts) - { - return _TaskGenerator::_GenerateTaskNoRet<_Function, _ClassPtr, _ProgressType_abi>(_Func, _Ptr, _Cts); - } - template - static task> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - { - return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType_abi>(_Func, _Ptr, _Cts, _pRet); - } -#endif - }; - - template - struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, false> - { - typedef typename ABI::Windows::Foundation::IAsyncAction _AsyncBaseType; - typedef _Zip _ProgressDelegateType; - typedef typename ABI::Windows::Foundation::IAsyncActionCompletedHandler _CompletionDelegateType; - typedef void _ReturnType; - typedef void _ReturnType_abi; - typedef typename _TaskTraits::_AsyncKind _AsyncKind; - typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; - typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; - - static const bool _TakesProgress = false; - static const bool _TakesToken = _TakesToken; - -#if _MSC_VER >= 1800 - template - static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - return _TaskGenerator::_GenerateTaskNoRet<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); - } - template - static task> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) - { - return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _pRet, _callstack); - } -#else - template - static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts) - { - return _TaskGenerator::_GenerateTaskNoRet<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts); - } - template - static task> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) - { - return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _pRet); - } -#endif - }; - - template - struct _AsyncLambdaTypeTraits - { - typedef typename _Unhat::_ReturnType>::_Value _ReturnType; - typedef typename _FunctorTypeTraits<_Function>::_Argument1Type _Argument1Type; - typedef typename _CAFunctorOptions<_Function>::_ProgressType _ProgressType; - - static const bool _TakesProgress = _CAFunctorOptions<_Function>::_TakesProgress; - static const bool _TakesToken = _CAFunctorOptions<_Function>::_TakesToken; - - typedef typename _TaskTypeTraits<_ReturnType> _TaskTraits; - typedef typename _AsyncAttributes<_Function, _ProgressType, typename _TaskTraits::_TaskRetType, _TaskTraits, _TakesToken, _TakesProgress> _AsyncAttributes; - }; - // *************************************************************************** - // AsyncInfo (and completion) Layer: - // -#ifndef RUNTIMECLASS_Concurrency_winrt_details__AsyncInfoBase_DEFINED -#define RUNTIMECLASS_Concurrency_winrt_details__AsyncInfoBase_DEFINED - extern const __declspec(selectany) WCHAR RuntimeClass_Concurrency_winrt_details__AsyncInfoBase[] = L"Concurrency_winrt.details._AsyncInfoBase"; -#endif - - // - // Internal base class implementation for async operations (based on internal Windows representation for ABI level async operations) - // - template < typename _Attributes, _AsyncResultType resultType = SingleResult > - class _AsyncInfoBase abstract : public Microsoft::WRL::RuntimeClass< - Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::WinRt>, Microsoft::WRL::Implements> - { - InspectableClass(RuntimeClass_Concurrency_winrt_details__AsyncInfoBase, BaseTrust) - public: - _AsyncInfoBase() : - _M_currentStatus(_AsyncStatusInternal::_AsyncCreated), - _M_errorCode(S_OK), - _M_completeDelegate(nullptr), - _M_CompleteDelegateAssigned(0), - _M_CallbackMade(0) - { -#if _MSC_VER < 1800 - _M_id = Concurrency::details::_GetNextAsyncId(); -#else - _M_id = Concurrency::details::platform::GetNextAsyncId(); -#endif - } - public: - virtual STDMETHODIMP GetResults(typename _Attributes::_ReturnType_abi* results) - { - (void)results; - return E_UNEXPECTED; - } - - virtual STDMETHODIMP get_Id(unsigned int* id) - { - HRESULT hr = _CheckValidStateForAsyncInfoCall(); - if (FAILED(hr)) return hr; - if (!id) return E_POINTER; - *id = _M_id; - return S_OK; - } - - virtual STDMETHODIMP put_Id(unsigned int id) - { - HRESULT hr = _CheckValidStateForAsyncInfoCall(); - if (FAILED(hr)) return hr; - - if (id == 0) - { - return E_INVALIDARG; - } - else if (_M_currentStatus != _AsyncStatusInternal::_AsyncCreated) - { - return E_ILLEGAL_METHOD_CALL; - } - - _M_id = id; - return S_OK; - } - virtual STDMETHODIMP get_Status(ABI::Windows::Foundation::AsyncStatus* status) - { - HRESULT hr = _CheckValidStateForAsyncInfoCall(); - if (FAILED(hr)) return hr; - if (!status) return E_POINTER; - - _AsyncStatusInternal _Current = _M_currentStatus; - // - // Map our internal cancel pending to cancelled. This way "pending cancelled" looks to the outside as "cancelled" but - // can still transition to "completed" if the operation completes without acknowledging the cancellation request - // - switch (_Current) - { - case _AsyncCancelPending: - _Current = _AsyncCanceled; - break; - case _AsyncCreated: - _Current = _AsyncStarted; - break; - default: - break; - } - - *status = static_cast(_Current); - return S_OK; - } - - virtual STDMETHODIMP get_ErrorCode(HRESULT* errorCode) - { - HRESULT hr = _CheckValidStateForAsyncInfoCall(); - if (FAILED(hr)) return hr; - if (!hr) return hr; - *errorCode = _M_errorCode; - return S_OK; - } - - virtual STDMETHODIMP get_Progress(typename _Attributes::_ProgressDelegateType** _ProgressHandler) - { - return _GetOnProgress(_ProgressHandler); - } - - virtual STDMETHODIMP put_Progress(typename _Attributes::_ProgressDelegateType* _ProgressHandler) - { - return _PutOnProgress(_ProgressHandler); - } - - virtual STDMETHODIMP Cancel() - { - if (_TransitionToState(_AsyncCancelPending)) - { - _OnCancel(); - } - return S_OK; - } - - virtual STDMETHODIMP Close() - { - if (_TransitionToState(_AsyncClosed)) - { - _OnClose(); - } - else - { - if (_M_currentStatus != _AsyncClosed) // Closed => Closed transition is just ignored - { - return E_ILLEGAL_STATE_CHANGE; - } - } - return S_OK; - } - - virtual STDMETHODIMP get_Completed(typename _Attributes::_CompletionDelegateType** _CompleteHandler) - { - _CheckValidStateForDelegateCall(); - if (!_CompleteHandler) return E_POINTER; - *_CompleteHandler = _M_completeDelegate.Get(); - return S_OK; - } - - virtual STDMETHODIMP put_Completed(typename _Attributes::_CompletionDelegateType* _CompleteHandler) - { - _CheckValidStateForDelegateCall(); - // this delegate property is "write once" - if (InterlockedIncrement(&_M_CompleteDelegateAssigned) == 1) - { - _M_completeDelegateContext = _ContextCallback::_CaptureCurrent(); - _M_completeDelegate = _CompleteHandler; - // Guarantee that the write of _M_completeDelegate is ordered with respect to the read of state below - // as perceived from _FireCompletion on another thread. - MemoryBarrier(); - if (_IsTerminalState()) - { - _FireCompletion(); - } - } - else - { - return E_ILLEGAL_DELEGATE_ASSIGNMENT; - } - return S_OK; - } - - protected: - // _Start - this is not externally visible since async operations "hot start" before returning to the caller - STDMETHODIMP _Start() - { - if (_TransitionToState(_AsyncStarted)) - { - _OnStart(); - } - else - { - return E_ILLEGAL_STATE_CHANGE; - } - return S_OK; - } - - HRESULT _FireCompletion() - { - HRESULT hr = S_OK; - _TryTransitionToCompleted(); - - // we guarantee that completion can only ever be fired once - if (_M_completeDelegate != nullptr && InterlockedIncrement(&_M_CallbackMade) == 1) - { - hr = _M_completeDelegateContext._CallInContext([=]() -> HRESULT { - ABI::Windows::Foundation::AsyncStatus status; - HRESULT hr; - if (SUCCEEDED(hr = this->get_Status(&status))) - _M_completeDelegate->Invoke((_Attributes::_AsyncBaseType*)this, status); - _M_completeDelegate = nullptr; - return hr; - }); - } - return hr; - } - - virtual STDMETHODIMP _GetOnProgress(typename _Attributes::_ProgressDelegateType** _ProgressHandler) - { - (void)_ProgressHandler; - return E_UNEXPECTED; - } - - virtual STDMETHODIMP _PutOnProgress(typename _Attributes::_ProgressDelegateType* _ProgressHandler) - { - (void)_ProgressHandler; - return E_UNEXPECTED; - } - - - bool _TryTransitionToCompleted() - { - return _TransitionToState(_AsyncStatusInternal::_AsyncCompleted); - } - - bool _TryTransitionToCancelled() - { - return _TransitionToState(_AsyncStatusInternal::_AsyncCanceled); - } - - bool _TryTransitionToError(const HRESULT error) - { - _InterlockedCompareExchange(reinterpret_cast(&_M_errorCode), error, S_OK); - return _TransitionToState(_AsyncStatusInternal::_AsyncError); - } - - // This method checks to see if the delegate properties can be - // modified in the current state and generates the appropriate - // error hr in the case of violation. - inline HRESULT _CheckValidStateForDelegateCall() - { - if (_M_currentStatus == _AsyncClosed) - { - return E_ILLEGAL_METHOD_CALL; - } - return S_OK; - } - - // This method checks to see if results can be collected in the - // current state and generates the appropriate error hr in - // the case of a violation. - inline HRESULT _CheckValidStateForResultsCall() - { - _AsyncStatusInternal _Current = _M_currentStatus; - - if (_Current == _AsyncError) - { - return _M_errorCode; - } -#pragma warning(push) -#pragma warning(disable: 4127) // Conditional expression is constant - // single result illegal before transition to Completed or Cancelled state - if (resultType == SingleResult) -#pragma warning(pop) - { - if (_Current != _AsyncCompleted) - { - return E_ILLEGAL_METHOD_CALL; - } - } - // multiple results can be called after Start has been called and before/after Completed - else if (_Current != _AsyncStarted && - _Current != _AsyncCancelPending && - _Current != _AsyncCanceled && - _Current != _AsyncCompleted) - { - return E_ILLEGAL_METHOD_CALL; - } - return S_OK; - } - - // This method can be called by derived classes periodically to determine - // whether the asynchronous operation should continue processing or should - // be halted. - inline bool _ContinueAsyncOperation() - { - return _M_currentStatus == _AsyncStarted; - } - - // These two methods are used to allow the async worker implementation do work on - // state transitions. No real "work" should be done in these methods. In other words - // they should not block for a long time on UI timescales. - virtual void _OnStart() = 0; - virtual void _OnClose() = 0; - virtual void _OnCancel() = 0; - - private: - - // This method is used to check if calls to the AsyncInfo properties - // (id, status, errorcode) are legal in the current state. It also - // generates the appropriate error hr to return in the case of an - // illegal call. - inline HRESULT _CheckValidStateForAsyncInfoCall() - { - _AsyncStatusInternal _Current = _M_currentStatus; - if (_Current == _AsyncClosed) - { - return E_ILLEGAL_METHOD_CALL; - } - else if (_Current == _AsyncCreated) - { - return E_ASYNC_OPERATION_NOT_STARTED; - } - return S_OK; - } - - inline bool _TransitionToState(const _AsyncStatusInternal _NewState) - { - _AsyncStatusInternal _Current = _M_currentStatus; - - // This enforces the valid state transitions of the asynchronous worker object - // state machine. - switch (_NewState) - { - case _AsyncStatusInternal::_AsyncStarted: - if (_Current != _AsyncCreated) - { - return false; - } - break; - case _AsyncStatusInternal::_AsyncCompleted: - if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) - { - return false; - } - break; - case _AsyncStatusInternal::_AsyncCancelPending: - if (_Current != _AsyncStarted) - { - return false; - } - break; - case _AsyncStatusInternal::_AsyncCanceled: - if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) - { - return false; - } - break; - case _AsyncStatusInternal::_AsyncError: - if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) - { - return false; - } - break; - case _AsyncStatusInternal::_AsyncClosed: - if (!_IsTerminalState(_Current)) - { - return false; - } - break; - default: - return false; - break; - } - - // attempt the transition to the new state - // Note: if currentStatus_ == _Current, then there was no intervening write - // by the async work object and the swap succeeded. - _AsyncStatusInternal _RetState = static_cast<_AsyncStatusInternal>( - _InterlockedCompareExchange(reinterpret_cast(&_M_currentStatus), - _NewState, - static_cast(_Current))); - - // ICE returns the former state, if the returned state and the - // state we captured at the beginning of this method are the same, - // the swap succeeded. - return (_RetState == _Current); - } - - inline bool _IsTerminalState() - { - return _IsTerminalState(_M_currentStatus); - } - - inline bool _IsTerminalState(_AsyncStatusInternal status) - { - return (status == _AsyncError || - status == _AsyncCanceled || - status == _AsyncCompleted || - status == _AsyncClosed); - } - - private: - - _ContextCallback _M_completeDelegateContext; - Microsoft::WRL::ComPtr _M_completeDelegate; //ComPtr cannot be volatile as it does not have volatile accessors - _AsyncStatusInternal volatile _M_currentStatus; - HRESULT volatile _M_errorCode; - unsigned int _M_id; - long volatile _M_CompleteDelegateAssigned; - long volatile _M_CallbackMade; - }; - - // *************************************************************************** - // Progress Layer (optional): - // - - template< typename _Attributes, bool _HasProgress, _AsyncResultType _ResultType = SingleResult > - class _AsyncProgressBase abstract : public _AsyncInfoBase<_Attributes, _ResultType> - { - }; - - template< typename _Attributes, _AsyncResultType _ResultType> - class _AsyncProgressBase<_Attributes, true, _ResultType> abstract : public _AsyncInfoBase<_Attributes, _ResultType> - { - public: - - _AsyncProgressBase() : _AsyncInfoBase<_Attributes, _ResultType>(), - _M_progressDelegate(nullptr) - { - } - - virtual STDMETHODIMP _GetOnProgress(typename _Attributes::_ProgressDelegateType** _ProgressHandler) override - { - HRESULT hr = _CheckValidStateForDelegateCall(); - if (FAILED(hr)) return hr; - *_ProgressHandler = _M_progressDelegate; - return S_OK; - } - - virtual STDMETHODIMP _PutOnProgress(typename _Attributes::_ProgressDelegateType* _ProgressHandler) override - { - HRESULT hr = _CheckValidStateForDelegateCall(); - if (FAILED(hr)) return hr; - _M_progressDelegate = _ProgressHandler; - _M_progressDelegateContext = _ContextCallback::_CaptureCurrent(); - return S_OK; - } - - public: - - void _FireProgress(const typename _Attributes::_ProgressType_abi& _ProgressValue) - { - if (_M_progressDelegate != nullptr) - { - _M_progressDelegateContext._CallInContext([=]() -> HRESULT { - _M_progressDelegate->Invoke((_Attributes::_AsyncBaseType*)this, _ProgressValue); - return S_OK; - }); - } - } - - private: - - _ContextCallback _M_progressDelegateContext; - typename _Attributes::_ProgressDelegateType* _M_progressDelegate; - }; - - template - class _AsyncBaseProgressLayer abstract : public _AsyncProgressBase<_Attributes, _Attributes::_TakesProgress, _ResultType> - { - }; - - // *************************************************************************** - // Task Adaptation Layer: - // - - // - // _AsyncTaskThunkBase provides a bridge between IAsync and task. - // - template - class _AsyncTaskThunkBase abstract : public _AsyncBaseProgressLayer<_Attributes> - { - public: - - //AsyncAction* - virtual STDMETHODIMP GetResults() - { - HRESULT hr = _CheckValidStateForResultsCall(); - if (FAILED(hr)) return hr; - _M_task.get(); - return S_OK; - } - public: - typedef task<_ReturnType> _TaskType; - - _AsyncTaskThunkBase(const _TaskType& _Task) - : _M_task(_Task) - { - } - - _AsyncTaskThunkBase() - { - } -#if _MSC_VER < 1800 - void _SetTaskCreationAddressHint(void* _SourceAddressHint) - { - if (!(std::is_same<_Attributes::_AsyncKind, _TypeSelectorAsyncTask>::value)) - { - // Overwrite the creation address with the return address of create_async unless the - // lambda returned a task. If the create async lambda returns a task, that task is reused and - // we want to preserve its creation address hint. - _M_task._SetTaskCreationAddressHint(_SourceAddressHint); - } - } -#endif - protected: - virtual void _OnStart() override - { - _M_task.then([=](_TaskType _Antecedent) -> HRESULT { - try - { - _Antecedent.get(); - } - catch (Concurrency::task_canceled&) - { - _TryTransitionToCancelled(); - } - catch (IRestrictedErrorInfo*& _Ex) - { - HRESULT hr; - HRESULT _hr; - hr = _Ex->GetErrorDetails(NULL, &_hr, NULL, NULL); - if (SUCCEEDED(hr)) hr = _hr; - _TryTransitionToError(hr); - } - catch (...) - { - _TryTransitionToError(E_FAIL); - } - return _FireCompletion(); - }); - } - - protected: - _TaskType _M_task; - Concurrency::cancellation_token_source _M_cts; - }; - - template - class _AsyncTaskReturn abstract : public _AsyncTaskThunkBase<_Attributes, _Return> - { - public: - //AsyncOperation* - virtual STDMETHODIMP GetResults(_ReturnType* results) - { - HRESULT hr = _CheckValidStateForResultsCall(); - if (FAILED(hr)) return hr; - _M_task.get(); - *results = _M_results; - return S_OK; - } - template -#if _MSC_VER >= 1800 - void DoCreateTask(_Function _func, const _TaskCreationCallstack & _callstack) - { - _M_task = _Attributes::_Generate_Task(_func, this, _M_cts, &_M_results, _callstack); - } -#else - void DoCreateTask(_Function _func) - { - _M_task = _Attributes::_Generate_Task(_func, this, _M_cts, &_M_results); - } -#endif - protected: - _ReturnType _M_results; - }; - - template - class _AsyncTaskReturn<_Attributes, _ReturnType, void> abstract : public _AsyncTaskThunkBase<_Attributes, void> - { - public: - template -#if _MSC_VER >= 1800 - void DoCreateTask(_Function _func, const _TaskCreationCallstack & _callstack) - { - _M_task = _Attributes::_Generate_Task(_func, this, _M_cts, _callstack); - } -#else - void DoCreateTask(_Function _func) - { - _M_task = _Attributes::_Generate_Task(_func, this, _M_cts); - } -#endif - }; - - template - class _AsyncTaskReturn<_Attributes, void, task> abstract : public _AsyncTaskThunkBase<_Attributes, task> - { - public: - template -#if _MSC_VER >= 1800 - void DoCreateTask(_Function _func, const _TaskCreationCallstack & _callstack) - { - _M_task = _Attributes::_Generate_Task(_func, this, _M_cts, &_M_results, _callstack); - } -#else - void DoCreateTask(_Function _func) - { - _M_task = _Attributes::_Generate_Task(_func, this, _M_cts, &_M_results); - } -#endif - protected: - task _M_results; - }; - - template - class _AsyncTaskThunk : public _AsyncTaskReturn<_Attributes, typename _Attributes::_ReturnType_abi, typename _Attributes::_ReturnType> - { - public: - - _AsyncTaskThunk(const _TaskType& _Task) : - _AsyncTaskThunkBase(_Task) - { - } - - _AsyncTaskThunk() - { - } - - protected: - - virtual void _OnClose() override - { - } - - virtual void _OnCancel() override - { - _M_cts.cancel(); - } - }; - - // *************************************************************************** - // Async Creation Layer: - // - template - class _AsyncTaskGeneratorThunk : public _AsyncTaskThunk::_AsyncAttributes> - { - public: - - typedef typename _AsyncLambdaTypeTraits<_Function>::_AsyncAttributes _Attributes; - typedef typename _AsyncTaskThunk<_Attributes> _Base; - typedef typename _Attributes::_AsyncBaseType _AsyncBaseType; - -#if _MSC_VER >= 1800 - _AsyncTaskGeneratorThunk(const _Function& _Func, const _TaskCreationCallstack &_callstack) : _M_func(_Func), _M_creationCallstack(_callstack) -#else - _AsyncTaskGeneratorThunk(const _Function& _Func) : _M_func(_Func) -#endif - { - // Virtual call here is safe as the class is declared 'sealed' - _Start(); - } - - protected: - - // - // The only thing we must do different from the base class is we must spin the hot task on transition from Created->Started. Otherwise, - // let the base thunk handle everything. - // - - virtual void _OnStart() override - { - // - // Call the appropriate task generator to actually produce a task of the expected type. This might adapt the user lambda for progress reports, - // wrap the return result in a task, or allow for direct return of a task depending on the form of the lambda. - // -#if _MSC_VER >= 1800 - DoCreateTask<_Function>(_M_func, _M_creationCallstack); -#else - DoCreateTask<_Function>(_M_func); -#endif - _Base::_OnStart(); - } - - virtual void _OnCancel() override - { - _Base::_OnCancel(); - } - - private: -#if _MSC_VER >= 1800 - _TaskCreationCallstack _M_creationCallstack; -#endif - _Function _M_func; - }; -} // namespace details - -/// -/// Creates a Windows Runtime asynchronous construct based on a user supplied lambda or function object. The return type of create_async is -/// one of either IAsyncAction^, IAsyncActionWithProgress<TProgress>^, IAsyncOperation<TResult>^, or -/// IAsyncOperationWithProgress<TResult, TProgress>^ based on the signature of the lambda passed to the method. -/// -/// -/// The lambda or function object from which to create a Windows Runtime asynchronous construct. -/// -/// -/// An asynchronous construct represented by an IAsyncAction^, IAsyncActionWithProgress<TProgress>^, IAsyncOperation<TResult>^, or an -/// IAsyncOperationWithProgress<TResult, TProgress>^. The interface returned depends on the signature of the lambda passed into the function. -/// -/// -/// The return type of the lambda determines whether the construct is an action or an operation. -/// Lambdas that return void cause the creation of actions. Lambdas that return a result of type TResult cause the creation of -/// operations of TResult. -/// The lambda may also return a task<TResult> which encapsulates the aysnchronous work within itself or is the continuation of -/// a chain of tasks that represent the asynchronous work. In this case, the lambda itself is executed inline, since the tasks are the ones that -/// execute asynchronously, and the return type of the lambda is unwrapped to produce the asynchronous construct returned by create_async. -/// This implies that a lambda that returns a task<void> will cause the creation of actions, and a lambda that returns a task<TResult> will -/// cause the creation of operations of TResult. -/// The lambda may take either zero, one or two arguments. The valid arguments are progress_reporter<TProgress> and -/// cancellation_token, in that order if both are used. A lambda without arguments causes the creation of an asynchronous construct without -/// the capability for progress reporting. A lambda that takes a progress_reporter<TProgress> will cause create_async to return an asynchronous -/// construct which reports progress of type TProgress each time the report method of the progress_reporter object is called. A lambda that -/// takes a cancellation_token may use that token to check for cancellation, or pass it to tasks that it creates so that cancellation of the -/// asynchronous construct causes cancellation of those tasks. -/// If the body of the lambda or function object returns a result (and not a task<TResult>), the lamdba will be executed -/// asynchronously within the process MTA in the context of a task the Runtime implicitly creates for it. The IAsyncInfo::Cancel method will -/// cause cancellation of the implicit task. -/// If the body of the lambda returns a task, the lamba executes inline, and by declaring the lambda to take an argument of type -/// cancellation_token you can trigger cancellation of any tasks you create within the lambda by passing that token in when you create them. -/// You may also use the register_callback method on the token to cause the Runtime to invoke a callback when you call IAsyncInfo::Cancel on -/// the async operation or action produced.. -/// This function is only available to Windows Store apps. -/// -/// -/// -/// -/**/ -template -__declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result -details::_AsyncTaskGeneratorThunk<_Function>* create_async(const _Function& _Func) -{ - static_assert(std::is_same(_Func, 0, 0, 0, 0, 0, 0, 0, 0)), std::true_type>::value, - "argument to create_async must be a callable object taking zero, one, two or three arguments"); -#if _MSC_VER >= 1800 - Microsoft::WRL::ComPtr> _AsyncInfo = Microsoft::WRL::Make>(_Func, _CAPTURE_CALLSTACK()); -#else - Microsoft::WRL::ComPtr> _AsyncInfo = Microsoft::WRL::Make>(_Func); - _AsyncInfo->_SetTaskCreationAddressHint(_ReturnAddress()); -#endif - return _AsyncInfo.Detach(); -} - -namespace details -{ -#if _MSC_VER < 1800 - // Internal API which retrieves the next async id. - _CRTIMP2 unsigned int __cdecl _GetNextAsyncId(); -#endif - // Helper struct for when_all operators to know when tasks have completed - template - struct _RunAllParam - { - _RunAllParam() : _M_completeCount(0), _M_numTasks(0) - { - } - - void _Resize(size_t _Len, bool _SkipVector = false) - { - _M_numTasks = _Len; - if (!_SkipVector) -#if _MSC_VER >= 1800 - { - _M_vector._Result.resize(_Len); - } -#else - _M_vector.resize(_Len); - _M_contexts.resize(_Len); -#endif - } - - task_completion_event<_Unit_type> _M_completed; - atomic_size_t _M_completeCount; -#if _MSC_VER >= 1800 - _ResultHolder > _M_vector; - _ResultHolder<_Type> _M_mergeVal; -#else - std::vector<_Type> _M_vector; - std::vector<_ContextCallback> _M_contexts; - _Type _M_mergeVal; -#endif - size_t _M_numTasks; - }; - -#if _MSC_VER >= 1800 - template - struct _RunAllParam > - { - _RunAllParam() : _M_completeCount(0), _M_numTasks(0) - { - } - - void _Resize(size_t _Len, bool _SkipVector = false) - { - _M_numTasks = _Len; - - if (!_SkipVector) - { - _M_vector.resize(_Len); - } - } - - task_completion_event<_Unit_type> _M_completed; - std::vector<_ResultHolder > > _M_vector; - atomic_size_t _M_completeCount; - size_t _M_numTasks; - }; -#endif - - // Helper struct specialization for void - template<> -#if _MSC_VER >= 1800 - struct _RunAllParam<_Unit_type> -#else - struct _RunAllParam -#endif - { - _RunAllParam() : _M_completeCount(0), _M_numTasks(0) - { - } - - void _Resize(size_t _Len) - { - _M_numTasks = _Len; - } - - task_completion_event<_Unit_type> _M_completed; - atomic_size_t _M_completeCount; - size_t _M_numTasks; - }; - - inline void _JoinAllTokens_Add(const Concurrency::cancellation_token_source& _MergedSrc, Concurrency::details::_CancellationTokenState *_PJoinedTokenState) - { - if (_PJoinedTokenState != nullptr && _PJoinedTokenState != Concurrency::details::_CancellationTokenState::_None()) - { - Concurrency::cancellation_token _T = Concurrency::cancellation_token::_FromImpl(_PJoinedTokenState); - _T.register_callback([=](){ - _MergedSrc.cancel(); - }); - } - } - - template - void _WhenAllContinuationWrapper(_RunAllParam<_ElementType>* _PParam, _Function _Func, task<_TaskType>& _Task) - { - if (_Task._GetImpl()->_IsCompleted()) - { - _Func(); -#if _MSC_VER >= 1800 - if (Concurrency::details::atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) -#else - if (_InterlockedIncrementSizeT(&_PParam->_M_completeCount) == _PParam->_M_numTasks) -#endif - { - // Inline execute its direct continuation, the _ReturnTask - _PParam->_M_completed.set(_Unit_type()); - // It's safe to delete it since all usage of _PParam in _ReturnTask has been finished. - delete _PParam; - } - } - else - { - _CONCRT_ASSERT(_Task._GetImpl()->_IsCanceled()); - if (_Task._GetImpl()->_HasUserException()) - { - // _Cancel will return false if the TCE is already canceled with or without exception - _PParam->_M_completed._Cancel(_Task._GetImpl()->_GetExceptionHolder()); - } - else - { - _PParam->_M_completed._Cancel(); - } -#if _MSC_VER >= 1800 - if (Concurrency::details::atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) -#else - if (_InterlockedIncrementSizeT(&_PParam->_M_completeCount) == _PParam->_M_numTasks) -#endif - { - delete _PParam; - } - } - } - - template - struct _WhenAllImpl - { -#if _MSC_VER >= 1800 - static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) -#else - static task> _Perform(Concurrency::details::_CancellationTokenState *_PTokenState, _Iterator _Begin, _Iterator _End) -#endif - { -#if _MSC_VER >= 1800 - Concurrency::details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; -#endif - auto _PParam = new _RunAllParam<_ElementType>(); - Concurrency::cancellation_token_source _MergedSource; - - // Step1: Create task completion event. -#if _MSC_VER >= 1800 - task_options _Options(_TaskOptions); - _Options.set_cancellation_token(_MergedSource.get_token()); - task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); -#else - task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token()); -#endif - // The return task must be created before step 3 to enforce inline execution. - auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type, std::vector<_ElementType>* retVal) -> HRESULT { -#if _MSC_VER >= 1800 - * retVal = _PParam->_M_vector.Get(); -#else - auto _Result = _PParam->_M_vector; // copy by value - - size_t _Index = 0; - for (auto _It = _Result.begin(); _It != _Result.end(); ++_It) - { - *_It = _ResultContext<_ElementType>::_GetValue(*_It, _PParam->_M_contexts[_Index++], false); - } - *retVal = _Result; -#endif - return S_OK; -#if _MSC_VER >= 1800 - }, nullptr); -#else - }, nullptr, true); -#endif - // Step2: Combine and check tokens, and count elements in range. - if (_PTokenState) - { - details::_JoinAllTokens_Add(_MergedSource, _PTokenState); - _PParam->_Resize(static_cast(std::distance(_Begin, _End))); - } - else - { - size_t _TaskNum = 0; - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - _TaskNum++; - details::_JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); - } - _PParam->_Resize(_TaskNum); - } - - // Step3: Check states of previous tasks. - if (_Begin == _End) - { - _PParam->_M_completed.set(_Unit_type()); - delete _PParam; - } - else - { - size_t _Index = 0; - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - if (_PTask->is_apartment_aware()) - { - _ReturnTask._SetAsync(); - } - - _PTask->_Then([_PParam, _Index](task<_ElementType> _ResultTask) -> HRESULT { - -#if _MSC_VER >= 1800 - // Dev10 compiler bug - typedef _ElementType _ElementTypeDev10; - auto _PParamCopy = _PParam; - auto _IndexCopy = _Index; - auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask](){ - _PParamCopy->_M_vector._Result[_IndexCopy] = _ResultTask._GetImpl()->_GetResult(); - }; -#else - auto _Func = [_PParam, _Index, &_ResultTask](){ - _PParam->_M_vector[_Index] = _ResultTask._GetImpl()->_GetResult(); - _PParam->_M_contexts[_Index] = _ResultContext<_ElementType>::_GetContext(false); - }; -#endif - _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); - return S_OK; -#if _MSC_VER >= 1800 - }, Concurrency::details::_CancellationTokenState::_None()); -#else - }, Concurrency::details::_CancellationTokenState::_None(), false); -#endif - - _Index++; - } - } - - return _ReturnTask; - } - }; - - template - struct _WhenAllImpl, _Iterator> - { -#if _MSC_VER >= 1800 - static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) -#else - static task> _Perform(Concurrency::details::_CancellationTokenState *_PTokenState, _Iterator _Begin, _Iterator _End) -#endif - { -#if _MSC_VER >= 1800 - Concurrency::details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; -#endif - auto _PParam = new _RunAllParam>(); - Concurrency::cancellation_token_source _MergedSource; - - // Step1: Create task completion event. -#if _MSC_VER >= 1800 - task_options _Options(_TaskOptions); - _Options.set_cancellation_token(_MergedSource.get_token()); - task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); -#else - task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token()); -#endif - // The return task must be created before step 3 to enforce inline execution. - auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type, std::vector<_ElementType>* retVal) -> HRESULT { - _CONCRT_ASSERT(_PParam->_M_completeCount == _PParam->_M_numTasks); - std::vector<_ElementType> _Result; - for (size_t _I = 0; _I < _PParam->_M_numTasks; _I++) - { -#if _MSC_VER >= 1800 - const std::vector<_ElementType>& _Vec = _PParam->_M_vector[_I].Get(); -#else - std::vector<_ElementType>& _Vec = _PParam->_M_vector[_I]; - - for (auto _It = _Vec.begin(); _It != _Vec.end(); ++_It) - { - *_It = _ResultContext<_ElementType>::_GetValue(*_It, _PParam->_M_contexts[_I], false); - } -#endif - _Result.insert(_Result.end(), _Vec.begin(), _Vec.end()); - } - *retVal = _Result; - return S_OK; -#if _MSC_VER >= 1800 - }, nullptr); -#else - }, nullptr, true); -#endif - - // Step2: Combine and check tokens, and count elements in range. - if (_PTokenState) - { - details::_JoinAllTokens_Add(_MergedSource, _PTokenState); - _PParam->_Resize(static_cast(std::distance(_Begin, _End))); - } - else - { - size_t _TaskNum = 0; - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - _TaskNum++; - details::_JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); - } - _PParam->_Resize(_TaskNum); - } - - // Step3: Check states of previous tasks. - if (_Begin == _End) - { - _PParam->_M_completed.set(_Unit_type()); - delete _PParam; - } - else - { - size_t _Index = 0; - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - if (_PTask->is_apartment_aware()) - { - _ReturnTask._SetAsync(); - } - - _PTask->_Then([_PParam, _Index](task> _ResultTask) -> HRESULT { -#if _MSC_VER >= 1800 - // Dev10 compiler bug - typedef _ElementType _ElementTypeDev10; - auto _PParamCopy = _PParam; - auto _IndexCopy = _Index; - auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask]() { - _PParamCopy->_M_vector[_IndexCopy].Set(_ResultTask._GetImpl()->_GetResult()); - }; -#else - auto _Func = [_PParam, _Index, &_ResultTask]() { - _PParam->_M_vector[_Index] = _ResultTask._GetImpl()->_GetResult(); - _PParam->_M_contexts[_Index] = _ResultContext<_ElementType>::_GetContext(false); - }; -#endif - _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); - return S_OK; -#if _MSC_VER >= 1800 - }, Concurrency::details::_CancellationTokenState::_None()); -#else - }, Concurrency::details::_CancellationTokenState::_None(), false); -#endif - - _Index++; - } - } - - return _ReturnTask; - } - }; - - template - struct _WhenAllImpl - { -#if _MSC_VER >= 1800 - static task _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) -#else - static task _Perform(Concurrency::details::_CancellationTokenState *_PTokenState, _Iterator _Begin, _Iterator _End) -#endif - { -#if _MSC_VER >= 1800 - Concurrency::details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; -#endif - auto _PParam = new _RunAllParam<_Unit_type>(); - Concurrency::cancellation_token_source _MergedSource; - - // Step1: Create task completion event. -#if _MSC_VER >= 1800 - task_options _Options(_TaskOptions); - _Options.set_cancellation_token(_MergedSource.get_token()); - task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); -#else - task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token()); -#endif - // The return task must be created before step 3 to enforce inline execution. - auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> HRESULT { return S_OK; -#if _MSC_VER >= 1800 - }, nullptr); -#else - }, nullptr, false); -#endif - - // Step2: Combine and check tokens, and count elements in range. - if (_PTokenState) - { - details::_JoinAllTokens_Add(_MergedSource, _PTokenState); - _PParam->_Resize(static_cast(std::distance(_Begin, _End))); - } - else - { - size_t _TaskNum = 0; - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - _TaskNum++; - details::_JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); - } - _PParam->_Resize(_TaskNum); - } - - // Step3: Check states of previous tasks. - if (_Begin == _End) - { - _PParam->_M_completed.set(_Unit_type()); - delete _PParam; - } - else - { - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - if (_PTask->is_apartment_aware()) - { - _ReturnTask._SetAsync(); - } - - _PTask->_Then([_PParam](task _ResultTask) -> HRESULT { - - auto _Func = []() -> HRESULT { return S_OK; }; - _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); - return S_OK; -#if _MSC_VER >= 1800 - }, Concurrency::details::_CancellationTokenState::_None()); -#else - }, Concurrency::details::_CancellationTokenState::_None(), false); -#endif - } - } - - return _ReturnTask; - } - }; - - template - task> _WhenAllVectorAndValue(const task>& _VectorTask, const task<_ReturnType>& _ValueTask, - bool _OutputVectorFirst) - { - auto _PParam = new _RunAllParam<_ReturnType>(); - Concurrency::cancellation_token_source _MergedSource; - - // Step1: Create task completion event. - task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token()); - // The return task must be created before step 3 to enforce inline execution. - auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type, std::vector<_ReturnType>* retVal) -> HRESULT { - _CONCRT_ASSERT(_PParam->_M_completeCount == 2); -#if _MSC_VER >= 1800 - auto _Result = _PParam->_M_vector.Get(); // copy by value - auto _mergeVal = _PParam->_M_mergeVal.Get(); -#else - auto _Result = _PParam->_M_vector; // copy by value - for (auto _It = _Result.begin(); _It != _Result.end(); ++_It) - { - *_It = _ResultContext<_ReturnType>::_GetValue(*_It, _PParam->_M_contexts[0], false); - } -#endif - - if (_OutputVectorFirst == true) - { -#if _MSC_VER >= 1800 - _Result.push_back(_mergeVal); -#else - _Result.push_back(_ResultContext<_ReturnType>::_GetValue(_PParam->_M_mergeVal, _PParam->_M_contexts[1], false)); -#endif - } - else - { -#if _MSC_VER >= 1800 - _Result.insert(_Result.begin(), _mergeVal); -#else - _Result.insert(_Result.begin(), _ResultContext<_ReturnType>::_GetValue(_PParam->_M_mergeVal, _PParam->_M_contexts[1], false)); -#endif - } - *retVal = _Result; - return S_OK; - }, nullptr, true); - - // Step2: Combine and check tokens. - _JoinAllTokens_Add(_MergedSource, _VectorTask._GetImpl()->_M_pTokenState); - _JoinAllTokens_Add(_MergedSource, _ValueTask._GetImpl()->_M_pTokenState); - - // Step3: Check states of previous tasks. - _PParam->_Resize(2, true); - - if (_VectorTask.is_apartment_aware() || _ValueTask.is_apartment_aware()) - { - _ReturnTask._SetAsync(); - } - _VectorTask._Then([_PParam](task> _ResultTask) -> HRESULT { -#if _MSC_VER >= 1800 - // Dev10 compiler bug - typedef _ReturnType _ReturnTypeDev10; - auto _PParamCopy = _PParam; - auto _Func = [_PParamCopy, &_ResultTask]() { - auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); - _PParamCopy->_M_vector.Set(_ResultLocal); - }; -#else - auto _Func = [_PParam, &_ResultTask]() { - _PParam->_M_vector = _ResultTask._GetImpl()->_GetResult(); - _PParam->_M_contexts[0] = _ResultContext<_ReturnType>::_GetContext(false); - }; -#endif - - _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); - return S_OK; -#if _MSC_VER >= 1800 - }, _CancellationTokenState::_None()); -#else - }, _CancellationTokenState::_None(), false); -#endif - _ValueTask._Then([_PParam](task<_ReturnType> _ResultTask) -> HRESULT { -#if _MSC_VER >= 1800 - // Dev10 compiler bug - typedef _ReturnType _ReturnTypeDev10; - auto _PParamCopy = _PParam; - auto _Func = [_PParamCopy, &_ResultTask]() { - auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); - _PParamCopy->_M_mergeVal.Set(_ResultLocal); - }; -#else - auto _Func = [_PParam, &_ResultTask]() { - _PParam->_M_mergeVal = _ResultTask._GetImpl()->_GetResult(); - _PParam->_M_contexts[1] = _ResultContext<_ReturnType>::_GetContext(false); - }; -#endif - _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); - return S_OK; -#if _MSC_VER >= 1800 - }, _CancellationTokenState::_None()); -#else - }, _CancellationTokenState::_None(), false); -#endif - - return _ReturnTask; - } -} // namespace details - -#if _MSC_VER < 1800 -/// -/// Creates a task that will complete successfully when all of the tasks supplied as arguments complete successfully. -/// -/// -/// The type of the input iterator. -/// -/// -/// The position of the first element in the range of elements to be combined into the resulting task. -/// -/// -/// The position of the first element beyond the range of elements to be combined into the resulting task. -/// -/// -/// A task that completes sucessfully when all of the input tasks have completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output -/// task will also be a task<void>. -/// -/// -/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, -/// if one is encoutered, will be thrown if you call get() or wait() on that task. -/// -/// -/**/ -template -auto when_all(_Iterator _Begin, _Iterator _End) --> decltype (details::_WhenAllImpl::value_type::result_type, _Iterator>::_Perform(nullptr, _Begin, _End)) -{ - typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; - return details::_WhenAllImpl<_ElementType, _Iterator>::_Perform(nullptr, _Begin, _End); -} -#endif - -/// -/// Creates a task that will complete successfully when all of the tasks supplied as arguments complete successfully. -/// -/// -/// The type of the input iterator. -/// -/// -/// The position of the first element in the range of elements to be combined into the resulting task. -/// -/// -/// The position of the first element beyond the range of elements to be combined into the resulting task. -/// -/// -/// The cancellation token which controls cancellation of the returned task. If you do not provide a cancellation token, the resulting -/// task will be created with a token that is a combination of all the cancelable tokens (tokens created by methods other than -/// cancellation_token::none()of the tasks supplied. -/// -/// -/// A task that completes sucessfully when all of the input tasks have completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output -/// task will also be a task<void>. -/// -/// -/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, -/// if one is encoutered, will be thrown if you call get() or wait() on that task. -/// -/// -/**/ -template -#if _MSC_VER >= 1800 -auto when_all(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) --> decltype (details::_WhenAllImpl::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) -{ - typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; - return details::_WhenAllImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); -} -#else -auto when_all(_Iterator _Begin, _Iterator _End, Concurrency::cancellation_token _CancellationToken) --> decltype (details::_WhenAllImpl::value_type::result_type, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End)) -{ - typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; - return details::_WhenAllImpl<_ElementType, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End); -} -#endif - -/// -/// Creates a task that will complete successfully when both of the tasks supplied as arguments complete successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output -/// task will also be a task<void>. -/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator -/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. -/// -/// -/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, -/// if one is encoutered, will be thrown if you call get() or wait() on that task. -/// -/// -/**/ -template -task> operator&&(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) -{ - task<_ReturnType> _PTasks[2] = { _Lhs, _Rhs }; - return when_all(_PTasks, _PTasks + 2); -} - -/// -/// Creates a task that will complete successfully when both of the tasks supplied as arguments complete successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output -/// task will also be a task<void>. -/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator -/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. -/// -/// -/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, -/// if one is encoutered, will be thrown if you call get() or wait() on that task. -/// -/// -/**/ -template -task> operator&&(const task> & _Lhs, const task<_ReturnType> & _Rhs) -{ - return details::_WhenAllVectorAndValue(_Lhs, _Rhs, true); -} - -/// -/// Creates a task that will complete successfully when both of the tasks supplied as arguments complete successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output -/// task will also be a task<void>. -/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator -/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. -/// -/// -/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, -/// if one is encoutered, will be thrown if you call get() or wait() on that task. -/// -/// -/**/ -template -task> operator&&(const task<_ReturnType> & _Lhs, const task> & _Rhs) -{ - return details::_WhenAllVectorAndValue(_Rhs, _Lhs, false); -} - -/// -/// Creates a task that will complete successfully when both of the tasks supplied as arguments complete successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output -/// task will also be a task<void>. -/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator -/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. -/// -/// -/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, -/// if one is encoutered, will be thrown if you call get() or wait() on that task. -/// -/// -/**/ -template -task> operator&&(const task> & _Lhs, const task> & _Rhs) -{ - task> _PTasks[2] = { _Lhs, _Rhs }; - return when_all(_PTasks, _PTasks + 2); -} - -/// -/// Creates a task that will complete successfully when both of the tasks supplied as arguments complete successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output -/// task will also be a task<void>. -/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator -/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. -/// -/// -/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, -/// if one is encoutered, will be thrown if you call get() or wait() on that task. -/// -/// -/**/ -inline task operator&&(const task & _Lhs, const task & _Rhs) -{ - task _PTasks[2] = { _Lhs, _Rhs }; - return when_all(_PTasks, _PTasks + 2); -} - -namespace details -{ - // Helper struct for when_any operators to know when tasks have completed - template - struct _RunAnyParam - { - _RunAnyParam() : _M_completeCount(0), _M_numTasks(0), _M_exceptionRelatedToken(nullptr), _M_fHasExplicitToken(false) - { - } - ~_RunAnyParam() - { - if (Concurrency::details::_CancellationTokenState::_IsValid(_M_exceptionRelatedToken)) - _M_exceptionRelatedToken->_Release(); - } - task_completion_event<_CompletionType> _M_Completed; - Concurrency::cancellation_token_source _M_cancellationSource; - Concurrency::details::_CancellationTokenState* _M_exceptionRelatedToken; - atomic_size_t _M_completeCount; - size_t _M_numTasks; - bool _M_fHasExplicitToken; - }; - - template - void _WhenAnyContinuationWrapper(_RunAnyParam<_CompletionType> * _PParam, const _Function & _Func, task<_TaskType>& _Task) - { - bool _IsTokenCancled = !_PParam->_M_fHasExplicitToken && _Task._GetImpl()->_M_pTokenState != Concurrency::details::_CancellationTokenState::_None() && _Task._GetImpl()->_M_pTokenState->_IsCanceled(); - if (_Task._GetImpl()->_IsCompleted() && !_IsTokenCancled) - { - _Func(); -#if _MSC_VER >= 1800 - if (Concurrency::details::atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) -#else - if (_InterlockedIncrementSizeT(&_PParam->_M_completeCount) == _PParam->_M_numTasks) -#endif - { - delete _PParam; - } - } - else - { - _CONCRT_ASSERT(_Task._GetImpl()->_IsCanceled() || _IsTokenCancled); - if (_Task._GetImpl()->_HasUserException() && !_IsTokenCancled) - { - if (_PParam->_M_Completed._StoreException(_Task._GetImpl()->_GetExceptionHolder())) - { - // This can only enter once. - _PParam->_M_exceptionRelatedToken = _Task._GetImpl()->_M_pTokenState; - _CONCRT_ASSERT(_PParam->_M_exceptionRelatedToken); - // Deref token will be done in the _PParam destructor. - if (_PParam->_M_exceptionRelatedToken != Concurrency::details::_CancellationTokenState::_None()) - { - _PParam->_M_exceptionRelatedToken->_Reference(); - } - } - } - -#if _MSC_VER >= 1800 - if (Concurrency::details::atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) -#else - if (_InterlockedIncrementSizeT(&_PParam->_M_completeCount) == _PParam->_M_numTasks) -#endif - { - // If no one has be completed so far, we need to make some final cancellation decision. - if (!_PParam->_M_Completed._IsTriggered()) - { - // If we already explicit token, we can skip the token join part. - if (!_PParam->_M_fHasExplicitToken) - { - if (_PParam->_M_exceptionRelatedToken) - { - details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _PParam->_M_exceptionRelatedToken); - } - else - { - // If haven't captured any exception token yet, there was no exception for all those tasks, - // so just pick a random token (current one) for normal cancellation. - details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Task._GetImpl()->_M_pTokenState); - } - } - // Do exception cancellation or normal cancellation based on whether it has stored exception. - _PParam->_M_Completed._Cancel(); - } - delete _PParam; - } - } - } - - template - struct _WhenAnyImpl - { -#if _MSC_VER >= 1800 - static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) -#else - static task> _Perform(Concurrency::details::_CancellationTokenState *_PTokenState, _Iterator _Begin, _Iterator _End) -#endif - { - if (_Begin == _End) - { - throw Concurrency::invalid_operation("when_any(begin, end) cannot be called on an empty container."); - } -#if _MSC_VER >= 1800 - Concurrency::details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; -#endif - auto _PParam = new _RunAnyParam, Concurrency::details::_CancellationTokenState *>>(); - - if (_PTokenState) - { - details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); - _PParam->_M_fHasExplicitToken = true; - } -#if _MSC_VER >= 1800 - task_options _Options(_TaskOptions); - _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); - task, Concurrency::details::_CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _Options); -#else - task, Concurrency::details::_CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); - _Any_tasks_completed._GetImpl()->_M_fRuntimeAggregate = true; -#endif - // Keep a copy ref to the token source - auto _CancellationSource = _PParam->_M_cancellationSource; - - _PParam->_M_numTasks = static_cast(std::distance(_Begin, _End)); - size_t index = 0; - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - if (_PTask->is_apartment_aware()) - { - _Any_tasks_completed._SetAsync(); - } - - _PTask->_Then([_PParam, index](task<_ElementType> _ResultTask) -> HRESULT { -#if _MSC_VER >= 1800 - auto _PParamCopy = _PParam; // Dev10 - auto _IndexCopy = index; // Dev10 - auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { - _PParamCopy->_M_Completed.set(std::make_pair(std::make_pair(_ResultTask._GetImpl()->_GetResult(), _IndexCopy), _ResultTask._GetImpl()->_M_pTokenState)); - }; -#else - auto _Func = [&_ResultTask, _PParam, index]() { - _PParam->_M_Completed.set(std::make_pair(std::make_pair(_ResultTask._GetImpl()->_GetResult(), index), _ResultTask._GetImpl()->_M_pTokenState)); - }; -#endif - _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); - return S_OK; -#if _MSC_VER >= 1800 - }, Concurrency::details::_CancellationTokenState::_None()); -#else - }, Concurrency::details::_CancellationTokenState::_None(), false); -#endif - index++; - } - - // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. - return _Any_tasks_completed._Then([=](std::pair, Concurrency::details::_CancellationTokenState *> _Result, std::pair<_ElementType, size_t>* retVal) -> HRESULT { - _CONCRT_ASSERT(_Result.second); - if (!_PTokenState) - { - details::_JoinAllTokens_Add(_CancellationSource, _Result.second); - } - *retVal = _Result.first; - return S_OK; -#if _MSC_VER >= 1800 - }, nullptr); -#else - }, nullptr, true); -#endif - } - }; - - template - struct _WhenAnyImpl - { -#if _MSC_VER >= 1800 - static task _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) -#else - static task _Perform(Concurrency::details::_CancellationTokenState *_PTokenState, _Iterator _Begin, _Iterator _End) -#endif - { - if (_Begin == _End) - { - throw Concurrency::invalid_operation("when_any(begin, end) cannot be called on an empty container."); - } -#if _MSC_VER >= 1800 - Concurrency::details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; -#endif - auto _PParam = new _RunAnyParam>(); - - if (_PTokenState) - { - details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); - _PParam->_M_fHasExplicitToken = true; - } - -#if _MSC_VER >= 1800 - task_options _Options(_TaskOptions); - _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); - task> _Any_tasks_completed(_PParam->_M_Completed, _Options); -#else - task> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); -#endif - // Keep a copy ref to the token source - auto _CancellationSource = _PParam->_M_cancellationSource; - - _PParam->_M_numTasks = static_cast(std::distance(_Begin, _End)); - size_t index = 0; - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - if (_PTask->is_apartment_aware()) - { - _Any_tasks_completed._SetAsync(); - } - - _PTask->_Then([_PParam, index](task _ResultTask) -> HRESULT { -#if _MSC_VER >= 1800 - auto _PParamCopy = _PParam; // Dev10 - auto _IndexCopy = index; // Dev10 - auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { - _PParamCopy->_M_Completed.set(std::make_pair(_IndexCopy, _ResultTask._GetImpl()->_M_pTokenState)); - }; -#else - auto _Func = [&_ResultTask, _PParam, index]() { - _PParam->_M_Completed.set(std::make_pair(index, _ResultTask._GetImpl()->_M_pTokenState)); - }; -#endif - _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); - return S_OK; -#if _MSC_VER >= 1800 - }, Concurrency::details::_CancellationTokenState::_None()); -#else - }, Concurrency::details::_CancellationTokenState::_None(), false); -#endif - - index++; - } - - // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. - return _Any_tasks_completed._Then([=](std::pair _Result, size_t* retVal) -> HRESULT { - _CONCRT_ASSERT(_Result.second); - if (!_PTokenState) - { - details::_JoinAllTokens_Add(_CancellationSource, _Result.second); - } - *retVal = _Result.first; - return S_OK; -#if _MSC_VER >= 1800 - }, nullptr); -#else - }, nullptr, false); -#endif - } - }; -} // namespace details - -/// -/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. -/// -/// -/// The type of the input iterator. -/// -/// -/// The position of the first element in the range of elements to be combined into the resulting task. -/// -/// -/// The position of the first element beyond the range of elements to be combined into the resulting task. -/// -/// -/// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::pair<T, size_t>>>, where the first element of the pair is the result -/// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type void -/// the output is a task<size_t>, where the result is the index of the completing task. -/// -/// -/**/ -template -#if _MSC_VER >= 1800 -auto when_any(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) --> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) -{ - typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; - return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); -} -#else -auto when_any(_Iterator _Begin, _Iterator _End) --> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(nullptr, _Begin, _End)) -{ - typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; - return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(nullptr, _Begin, _End); -} -#endif - -/// -/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. -/// -/// -/// The type of the input iterator. -/// -/// -/// The position of the first element in the range of elements to be combined into the resulting task. -/// -/// -/// The position of the first element beyond the range of elements to be combined into the resulting task. -/// -/// -/// The cancellation token which controls cancellation of the returned task. If you do not provide a cancellation token, the resulting -/// task will receive the cancellation token of the task that causes it to complete. -/// -/// -/// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::pair<T, size_t>>>, where the first element of the pair is the result -/// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type void -/// the output is a task<size_t>, where the result is the index of the completing task. -/// -/// -/**/ -template -auto when_any(_Iterator _Begin, _Iterator _End, Concurrency::cancellation_token _CancellationToken) --> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End)) -{ - typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; - return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End); -} - -/// -/// Creates a task that will complete successfully when either of the tasks supplied as arguments completes successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task -/// will also be a task<void>. -/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence -/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> -/// and the other one is of type task<T>. -/// -/// -/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, -/// if any are encountered, will be thrown when you call get() or wait() on that task. -/// -/// -/**/ -template -task<_ReturnType> operator||(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) -{ -#if _MSC_VER >= 1800 - auto _PParam = new details::_RunAnyParam>(); - - task> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); - // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, - // So that _PParam can be used before it getting deleted. - auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair<_ReturnType, size_t> _Ret, _ReturnType* retVal) -> HRESULT { - _CONCRT_ASSERT(_Ret.second); - details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, reinterpret_cast(_Ret.second)); - *retVal = _Ret.first; - return S_OK; - }, nullptr); -#else - auto _PParam = new details::_RunAnyParam>(); - - task> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); - // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, - // So that _PParam can be used before it getting deleted. - auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair<_ReturnType, Concurrency::details::_CancellationTokenState *> _Ret, _ReturnType* retVal) -> HRESULT { - _CONCRT_ASSERT(_Ret.second); - details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); - *retVal = _Ret.first; - return S_OK; - }, nullptr, false); -#endif - if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) - { - _ReturnTask._SetAsync(); - } - - _PParam->_M_numTasks = 2; - auto _Continuation = [_PParam](task<_ReturnType> _ResultTask) -> HRESULT { -#if _MSC_VER >= 1800 - // Dev10 compiler bug - auto _PParamCopy = _PParam; - auto _Func = [&_ResultTask, _PParamCopy]() { - _PParamCopy->_M_Completed.set(std::make_pair(_ResultTask._GetImpl()->_GetResult(), reinterpret_cast(_ResultTask._GetImpl()->_M_pTokenState))); - }; -#else - auto _Func = [&_ResultTask, _PParam]() { - _PParam->_M_Completed.set(std::make_pair(_ResultTask._GetImpl()->_GetResult(), _ResultTask._GetImpl()->_M_pTokenState)); - }; -#endif - _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); - return S_OK; - }; - -#if _MSC_VER >= 1800 - _Lhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None()); - _Rhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None()); -#else - _Lhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None(), false); - _Rhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None(), false); -#endif - return _ReturnTask; -} - -/// -/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task -/// will also be a task<void>. -/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence -/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> -/// and the other one is of type task<T>. -/// -/// -/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, -/// if any are encountered, will be thrown when you call get() or wait() on that task. -/// -/// -/**/ -template -task> operator||(const task> & _Lhs, const task<_ReturnType> & _Rhs) -{ - auto _PParam = new details::_RunAnyParam, Concurrency::details::_CancellationTokenState *>>(); - - task, Concurrency::details::_CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); -#if _MSC_VER < 1800 - _Any_tasks_completed._GetImpl()->_M_fRuntimeAggregate = true; -#endif - // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, - // So that _PParam can be used before it getting deleted. - auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair, Concurrency::details::_CancellationTokenState *> _Ret, std::vector<_ReturnType>* retVal) -> HRESULT { - _CONCRT_ASSERT(_Ret.second); - details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); - *retVal = _Ret.first; - return S_OK; - }, nullptr, true); - - if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) - { - _ReturnTask._SetAsync(); - } - - _PParam->_M_numTasks = 2; - _Lhs._Then([_PParam](task> _ResultTask) -> HRESULT { -#if _MSC_VER >= 1800 - // Dev10 compiler bug - auto _PParamCopy = _PParam; - auto _Func = [&_ResultTask, _PParamCopy]() { - auto _Result = _ResultTask._GetImpl()->_GetResult(); - _PParamCopy->_M_Completed.set(std::make_pair(_Result, _ResultTask._GetImpl()->_M_pTokenState)); - }; -#else - auto _Func = [&_ResultTask, _PParam]() { - std::vector<_ReturnType> _Result = _ResultTask._GetImpl()->_GetResult(); - _PParam->_M_Completed.set(std::make_pair(_Result, _ResultTask._GetImpl()->_M_pTokenState)); - }; -#endif - _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); - return S_OK; -#if _MSC_VER >= 1800 - }, Concurrency::details::_CancellationTokenState::_None()); -#else - }, Concurrency::details::_CancellationTokenState::_None(), false); -#endif - _Rhs._Then([_PParam](task<_ReturnType> _ResultTask) -> HRESULT { -#if _MSC_VER >= 1800 - // Dev10 compiler bug - typedef _ReturnType _ReturnTypeDev10; - auto _PParamCopy = _PParam; - auto _Func = [&_ResultTask, _PParamCopy]() { - auto _Result = _ResultTask._GetImpl()->_GetResult(); - - std::vector<_ReturnTypeDev10> _Vec; - _Vec.push_back(_Result); - _PParamCopy->_M_Completed.set(std::make_pair(_Vec, _ResultTask._GetImpl()->_M_pTokenState)); - }; -#else - auto _Func = [&_ResultTask, _PParam]() { - _ReturnType _Result = _ResultTask._GetImpl()->_GetResult(); - - std::vector<_ReturnType> _Vec; - _Vec.push_back(_Result); - _PParam->_M_Completed.set(std::make_pair(_Vec, _ResultTask._GetImpl()->_M_pTokenState)); - }; -#endif - _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); - return S_OK; -#if _MSC_VER >= 1800 - }, Concurrency::details::_CancellationTokenState::_None()); -#else - }, Concurrency::details::_CancellationTokenState::_None(), false); -#endif - return _ReturnTask; -} - -/// -/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task -/// will also be a task<void>. -/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence -/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> -/// and the other one is of type task<T>. -/// -/// -/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, -/// if any are encountered, will be thrown when you call get() or wait() on that task. -/// -/// -/**/ -template -task> operator||(const task<_ReturnType> & _Lhs, const task> & _Rhs) -{ - return _Rhs || _Lhs; -} - -/// -/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task -/// will also be a task<void>. -/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence -/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> -/// and the other one is of type task<T>. -/// -/// -/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, -/// if any are encountered, will be thrown when you call get() or wait() on that task. -/// -/// -/**/ -inline task operator||(const task & _Lhs, const task & _Rhs) -{ - auto _PParam = new details::_RunAnyParam>(); - - task> _Any_task_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); - // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, - // So that _PParam can be used before it getting deleted. - auto _ReturnTask = _Any_task_completed._Then([=](std::pair _Ret) -> HRESULT { - _CONCRT_ASSERT(_Ret.second); - details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); - return S_OK; -#if _MSC_VER >= 1800 - }, nullptr); -#else - }, nullptr, false); -#endif - - if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) - { - _ReturnTask._SetAsync(); - } - - _PParam->_M_numTasks = 2; - auto _Continuation = [_PParam](task _ResultTask) mutable -> HRESULT { - // Dev10 compiler needs this. - auto _PParam1 = _PParam; - auto _Func = [&_ResultTask, _PParam1]() { - _PParam1->_M_Completed.set(std::make_pair(details::_Unit_type(), _ResultTask._GetImpl()->_M_pTokenState)); - }; - _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); - return S_OK; - }; - -#if _MSC_VER >= 1800 - _Lhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None()); - _Rhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None()); -#else - _Lhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None(), false); - _Rhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None(), false); -#endif - - return _ReturnTask; -} - -#if _MSC_VER >= 1800 -template -task<_Ty> task_from_result(_Ty _Param, const task_options& _TaskOptions = task_options()) -{ - task_completion_event<_Ty> _Tce; - _Tce.set(_Param); - return create_task<_Ty>(_Tce, _TaskOptions); -} - -// Work around VS 2010 compiler bug -#if _MSC_VER == 1600 -inline task task_from_result(bool _Param) -{ - task_completion_event _Tce; - _Tce.set(_Param); - return create_task(_Tce, task_options()); -} -#endif -inline task task_from_result(const task_options& _TaskOptions = task_options()) -{ - task_completion_event _Tce; - _Tce.set(); - return create_task(_Tce, _TaskOptions); -} - -template -task<_TaskType> task_from_exception(_ExType _Exception, const task_options& _TaskOptions = task_options()) -{ - task_completion_event<_TaskType> _Tce; - _Tce.set_exception(_Exception); - return create_task<_TaskType>(_Tce, _TaskOptions); -} - -namespace details -{ - /// - /// A convenient extension to Concurrency: loop until a condition is no longer met - /// - /// - /// A function representing the body of the loop. It will be invoked at least once and - /// then repetitively as long as it returns true. - /// - inline - task do_while(std::function(void)> func) - { - task first = func(); - return first.then([=](bool guard, task* retVal) -> HRESULT { - if (guard) - *retVal = do_while(func); - else - *retVal = first; - return S_OK; - }); - } - -} // namespace details -#endif - -} // namespace Concurrency_winrt - -namespace concurrency_winrt = Concurrency_winrt; - -#pragma pop_macro("new") -#pragma warning(pop) -#pragma pack(pop) -#endif - -#endif diff --git a/modules/highgui/src/window.cpp b/modules/highgui/src/window.cpp index cda019102c..d6f35af1cb 100644 --- a/modules/highgui/src/window.cpp +++ b/modules/highgui/src/window.cpp @@ -481,11 +481,12 @@ int cv::createButton(const String&, ButtonCallback, void*, int , bool ) #endif -#if defined(HAVE_WIN32UI) // see window_w32.cpp +#if defined (HAVE_WIN32UI) // see window_w32.cpp #elif defined (HAVE_GTK) // see window_gtk.cpp #elif defined (HAVE_COCOA) // see window_carbon.cpp #elif defined (HAVE_CARBON) -#elif defined (HAVE_QT) //YV see window_QT.cpp +#elif defined (HAVE_QT) // see window_QT.cpp +#elif defined (WINRT) && !defined (WINRT_8_0) // see window_winrt.cpp #else diff --git a/modules/highgui/src/window_winrt.cpp b/modules/highgui/src/window_winrt.cpp new file mode 100644 index 0000000000..b87c9611db --- /dev/null +++ b/modules/highgui/src/window_winrt.cpp @@ -0,0 +1,438 @@ +#include "precomp.hpp" + +#if defined WINRT && !defined WINRT_8_0 + +#include +#include +#include +#include + +struct CvWindow; + +typedef struct CvTrackbar +{ + int signature; + void* hwnd; // TODO: use proper handle type + char* name; + CvTrackbar* next; + CvWindow* parent; + int* data; + int pos; + int maxval; + void (*notify)(int); + void (*notify2)(int, void*); + void* userdata; + int id; +} +CvTrackbar; + + +typedef struct CvWindow +{ + int signature; + void* hwnd; // TODO: use proper handle type + char* name; + CvWindow* prev; + CvWindow* next; + + HGDIOBJ image; + int flags; + + CvMouseCallback on_mouse; + void* on_mouse_param; + + struct + { + void* toolbar; // TODO: use proper handle type + int pos; + int rows; + CvTrackbar* first; + } + toolbar; + + int width; + int height; +} +CvWindow; + +static CvWindow* hg_windows = 0; + +// typedef int (CV_CDECL * CvWin32WindowCallback)(HWND, UINT, WPARAM, LPARAM, int*); + +static CvWindow* icvFindWindowByName(const char* name) { + CvWindow* window = hg_windows; + + for (; window != 0 && strcmp(name, window->name) != 0; window = window->next) + ; + + return window; +} + +static CvTrackbar* +icvFindTrackbarByName(const CvWindow* window, const char* name) { + CvTrackbar* trackbar = window->toolbar.first; + + for (; trackbar != 0 && strcmp(trackbar->name, name) != 0; trackbar = trackbar->next) + ; + + return trackbar; +} + +CV_IMPL int cvInitSystem( int, char** ) +{ + static int wasInitialized = 0; + + if (!wasInitialized) + { + hg_windows = 0; + } + + return 0; +} + +CV_IMPL int cvStartWindowThread(){ + return 0; +} + +CV_IMPL int cvNamedWindow( const char* name, int flags ) +{ + int result = 0; + CV_FUNCNAME( "cvNamedWindow" ); + + __BEGIN__; + __END__; + + return result; +} + +CV_IMPL void cvDestroyWindow( const char* name ) +{ + CV_FUNCNAME( "cvDestroyWindow" ); + + __BEGIN__; + + CvWindow* window; + + if(!name) + CV_ERROR( CV_StsNullPtr, "NULL name string" ); + + window = icvFindWindowByName(name); + if( !window ) + EXIT; + + __END__; +} + +CV_IMPL void cvShowImage( const char* name, const CvArr* arr ) +{ + CV_FUNCNAME( "cvShowImage" ); + + __BEGIN__; + + CvWindow* window; + SIZE size = { 0, 0 }; + int channels = 0; + void* dst_ptr = 0; + const int channels_def = 3; + int origin = 0; + CvMat stub, dst, *image; + bool changed_size = false; + + if( !name ) + CV_ERROR( CV_StsNullPtr, "NULL name" ); + + window = icvFindWindowByName(name); + if(!window) + { + cvNamedWindow(name, CV_WINDOW_AUTOSIZE); + window = icvFindWindowByName(name); + } + + if( !window || !arr ) + EXIT; + + if( CV_IS_IMAGE_HDR( arr )) + origin = ((IplImage*)arr)->origin; + + CV_CALL( image = cvGetMat( arr, &stub )); + +#ifdef HAVE_OPENGL + if (window->useGl) + { + cv::imshow(name, cv::cvarrToMat(image)); + return; + } +#endif + + if (window->image) + { + //TODO: validate image + } + + if (size.cx != image->width || size.cy != image->height || channels != channels_def) + { + changed_size = true; + + //TODO: handle image resize + } + + cvInitMatHeader( &dst, size.cy, size.cx, CV_8UC3, + dst_ptr, (size.cx * channels + 3) & -4 ); + cvConvertImage( image, &dst, origin == 0 ? CV_CVTIMG_FLIP : 0 ); + + if (changed_size) + //TODO: handle consequent image resize + + __END__; +} + +CV_IMPL void cvResizeWindow(const char* name, int width, int height ) +{ + CV_FUNCNAME( "cvResizeWindow" ); + + __BEGIN__; + + CvWindow* window; + + if( !name ) + CV_ERROR( CV_StsNullPtr, "NULL name" ); + + window = icvFindWindowByName(name); + if(!window) + EXIT; + + // TODO: implement appropriate logic here + + __END__; +} + + +CV_IMPL void cvMoveWindow( const char* name, int x, int y ) +{ + CV_FUNCNAME( "cvMoveWindow" ); + + __BEGIN__; + + CvWindow* window; + RECT rect; + + if( !name ) + CV_ERROR( CV_StsNullPtr, "NULL name" ); + + window = icvFindWindowByName(name); + if(!window) + EXIT; + + // TODO: implement appropriate logic here + + __END__; +} + + + +CV_IMPL void cvDestroyAllWindows(void) +{ + // TODO: implement appropriate logic here +} + +CV_IMPL int cvWaitKey( int delay ) +{ + // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms724411(v=vs.85).aspx + int time0 = GetTickCount64(); + + for(;;) + { + CvWindow* window; + + if ((delay > 0 && abs((int)(GetTickCount64() - time0)) >= delay) || hg_windows == 0) + return -1; + + if (delay <= 0) + { + // TODO: implement appropriate logic here + } + + for( window = hg_windows; window != 0; window = window->next ) + { + } + } +} + + + +CV_IMPL int +cvCreateTrackbar( const char* trackbar_name, const char* window_name, + int* val, int count, CvTrackbarCallback on_notify ) +{ + // TODO: implement appropriate logic here + return 0; +} + +CV_IMPL int +cvCreateTrackbar2( const char* trackbar_name, const char* window_name, + int* val, int count, CvTrackbarCallback2 on_notify2, + void* userdata ) +{ + // TODO: implement appropriate logic here + return 0; +} + +CV_IMPL void +cvSetMouseCallback( const char* window_name, CvMouseCallback on_mouse, void* param ) +{ + CV_FUNCNAME( "cvSetMouseCallback" ); + + __BEGIN__; + + CvWindow* window = 0; + + if( !window_name ) + CV_ERROR( CV_StsNullPtr, "NULL window name" ); + + window = icvFindWindowByName(window_name); + if( !window ) + EXIT; + + // TODO: implement appropriate logic here + + __END__; +} + + +CV_IMPL int cvGetTrackbarPos( const char* trackbar_name, const char* window_name ) +{ + int pos = -1; + + CV_FUNCNAME( "cvGetTrackbarPos" ); + + __BEGIN__; + + CvWindow* window; + CvTrackbar* trackbar = 0; + + if( trackbar_name == 0 || window_name == 0 ) + CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" ); + + window = icvFindWindowByName( window_name ); + if( window ) + trackbar = icvFindTrackbarByName( window, trackbar_name ); + + if( trackbar ) + pos = trackbar->pos; + + __END__; + + return pos; +} + + +CV_IMPL void cvSetTrackbarPos( const char* trackbar_name, const char* window_name, int pos ) +{ + CV_FUNCNAME( "cvSetTrackbarPos" ); + + __BEGIN__; + + CvWindow* window; + CvTrackbar* trackbar = 0; + + if( trackbar_name == 0 || window_name == 0 ) + CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" ); + + window = icvFindWindowByName( window_name ); + if( window ) + trackbar = icvFindTrackbarByName( window, trackbar_name ); + + if( trackbar ) + { + if( pos < 0 ) + pos = 0; + + if( pos > trackbar->maxval ) + pos = trackbar->maxval; + + //TODO: update trackbar + } + + __END__; +} + + +CV_IMPL void cvSetTrackbarMax(const char* trackbar_name, const char* window_name, int maxval) +{ + CV_FUNCNAME( "cvSetTrackbarMax" ); + + __BEGIN__; + + if (maxval >= 0) + { + CvWindow* window = 0; + CvTrackbar* trackbar = 0; + if (trackbar_name == 0 || window_name == 0) + { + CV_ERROR(CV_StsNullPtr, "NULL trackbar or window name"); + } + + window = icvFindWindowByName(window_name); + if (window) + { + trackbar = icvFindTrackbarByName(window, trackbar_name); + if (trackbar) + { + // The position will be min(pos, maxval). + trackbar->maxval = maxval; + + //TODO: update trackbar + } + } + } + + __END__; +} + + +CV_IMPL void* cvGetWindowHandle( const char* window_name ) +{ + void* hwnd = 0; + + CV_FUNCNAME( "cvGetWindowHandle" ); + + __BEGIN__; + + CvWindow* window; + + if( window_name == 0 ) + CV_ERROR( CV_StsNullPtr, "NULL window name" ); + + window = icvFindWindowByName( window_name ); + if( window ) + hwnd = (void*)window->hwnd; + + __END__; + + return hwnd; +} + + +CV_IMPL const char* cvGetWindowName( void* window_handle ) +{ + const char* window_name = ""; + + CV_FUNCNAME( "cvGetWindowName" ); + + __BEGIN__; + + CvWindow* window = 0; + + if( window_handle == 0 ) + CV_ERROR( CV_StsNullPtr, "NULL window" ); + + // window = TODO: find window by handle + if( window ) + window_name = window->name; + + __END__; + + return 0; +} + +#endif //defined WINRT && !defined WINRT_8_0 \ No newline at end of file From 9394486147b1ac7f53e7cc7995377555256b02cf Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 16 Jun 2015 17:56:00 +0300 Subject: [PATCH 021/133] fix issue 3891 --- modules/core/include/opencv2/core/mat.inl.hpp | 12 ++++++++++++ modules/core/test/test_mat.cpp | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/mat.inl.hpp b/modules/core/include/opencv2/core/mat.inl.hpp index 3779b83f82..2b648718c2 100644 --- a/modules/core/include/opencv2/core/mat.inl.hpp +++ b/modules/core/include/opencv2/core/mat.inl.hpp @@ -1586,7 +1586,13 @@ template template inline Mat_<_Tp>::operator Vec::channel_type, n>() const { CV_Assert(n % DataType<_Tp>::channels == 0); + +#if defined _MSC_VER + const Mat* pMat = (const Mat*)this; // workaround for MSVS <= 2012 compiler bugs (but GCC 4.6 dislikes this workaround) + return pMat->operator Vec::channel_type, n>(); +#else return this->Mat::operator Vec::channel_type, n>(); +#endif } template template inline @@ -1594,8 +1600,14 @@ Mat_<_Tp>::operator Matx::channel_type, m, n>() const { CV_Assert(n % DataType<_Tp>::channels == 0); +#if defined _MSC_VER + const Mat* pMat = (const Mat*)this; // workaround for MSVS <= 2012 compiler bugs (but GCC 4.6 dislikes this workaround) + Matx::channel_type, m, n> res = pMat->operator Matx::channel_type, m, n>(); + return res; +#else Matx::channel_type, m, n> res = this->Mat::operator Matx::channel_type, m, n>(); return res; +#endif } template inline diff --git a/modules/core/test/test_mat.cpp b/modules/core/test/test_mat.cpp index 897ac40a43..19992433b2 100644 --- a/modules/core/test/test_mat.cpp +++ b/modules/core/test/test_mat.cpp @@ -1214,7 +1214,7 @@ TEST(Core_Matx, fromMat_) { Mat_ a = (Mat_(2,2) << 10, 11, 12, 13); Matx22d b(a); - ASSERT_EQ( norm(a, b, NORM_INF), 0.); + ASSERT_EQ( cvtest::norm(a, b, NORM_INF), 0.); } TEST(Core_InputArray, empty) From 510dec4927991eab7207fa6012af869ca6085468 Mon Sep 17 00:00:00 2001 From: rajithr Date: Wed, 17 Jun 2015 11:07:49 +0530 Subject: [PATCH 022/133] Fixing resource leaks --- modules/calib3d/src/upnp.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/calib3d/src/upnp.cpp b/modules/calib3d/src/upnp.cpp index 378f5a11b4..1054e0bffe 100644 --- a/modules/calib3d/src/upnp.cpp +++ b/modules/calib3d/src/upnp.cpp @@ -114,6 +114,7 @@ double upnp::compute_pose(Mat& R, Mat& t) SVD::compute(MtM, D, Ut, Vt, SVD::MODIFY_A | SVD::FULL_UV); Mat(Ut.t()).copyTo(Ut); M->release(); + delete M; double l_6x12[6 * 12], rho[6]; Mat L_6x12 = Mat(6, 12, CV_64F, l_6x12); @@ -589,7 +590,16 @@ void upnp::gauss_newton(const Mat * L_6x12, const Mat * Rho, double betas[4], do } if (f[0] < 0) f[0] = -f[0]; - fu = fv = f[0]; + fu = fv = f[0]; + + A->release(); + delete A; + + B->release(); + delete B; + + X->release(); + delete X; } From fef7509eed59567c3e3ac52d5b1efd42ddb3af3e Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Wed, 17 Jun 2015 12:07:57 +0300 Subject: [PATCH 023/133] NumpyAllocator: check reference count before actual release, revert flann changes --- modules/features2d/src/matchers.cpp | 25 +++++-------------------- modules/python/src2/cv2.cpp | 8 ++++++-- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/modules/features2d/src/matchers.cpp b/modules/features2d/src/matchers.cpp index a37ef0693f..8d2f69ec83 100644 --- a/modules/features2d/src/matchers.cpp +++ b/modules/features2d/src/matchers.cpp @@ -1022,27 +1022,12 @@ FlannBasedMatcher::FlannBasedMatcher( const Ptr& _indexParam void FlannBasedMatcher::add( InputArrayOfArrays _descriptors ) { DescriptorMatcher::add( _descriptors ); - if(_descriptors.isUMatVector() || _descriptors.isUMat()) + std::vector descriptors; + _descriptors.getUMatVector(descriptors); + + for( size_t i = 0; i < descriptors.size(); i++ ) { - std::vector descriptors; - _descriptors.getUMatVector(descriptors); - for( size_t i = 0; i < descriptors.size(); i++ ) - { - addedDescCount += descriptors[i].rows; - } - } - else if(_descriptors.isMatVector() || _descriptors.isMat()) - { - std::vector descriptors; - _descriptors.getMatVector(descriptors); - for( size_t i = 0; i < descriptors.size(); i++ ) - { - addedDescCount += descriptors[i].rows; - } - } - else - { - CV_Assert( _descriptors.isUMat() || _descriptors.isUMatVector() || _descriptors.isMat() || _descriptors.isMatVector()); + addedDescCount += descriptors[i].rows; } } diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 974545994b..2ffb609caf 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -190,9 +190,13 @@ public: void deallocate(UMatData* u) const { - if(u) + if(!u) + return; + PyEnsureGIL gil; + CV_Assert(u->urefcount >= 0); + CV_Assert(u->refcount >= 0); + if(u->refcount == 0) { - PyEnsureGIL gil; PyObject* o = (PyObject*)u->userdata; Py_XDECREF(o); delete u; From 125782c061b0b0d6164200c95730ff8f6cf1a0a7 Mon Sep 17 00:00:00 2001 From: themightyoarfish Date: Wed, 17 Jun 2015 12:08:34 +0200 Subject: [PATCH 024/133] fixed copy-paste errors --- modules/imgcodecs/src/ios_conversions.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/imgcodecs/src/ios_conversions.mm b/modules/imgcodecs/src/ios_conversions.mm index 0b771f1cbc..8f2b4e84fc 100644 --- a/modules/imgcodecs/src/ios_conversions.mm +++ b/modules/imgcodecs/src/ios_conversions.mm @@ -67,8 +67,8 @@ UIImage* MatToUIImage(const cv::Mat& image) { CGDataProviderCreateWithCFData((__bridge CFDataRef)data); // Preserve alpha transparency, if exists - bool alpha = cvMat.channels() == 4; - CGBitmapInfo bitMapInfo = (alpha ? kCGImageAlphaLast : kCGImageAlphaNone) | kCGBitmapByteOrderDefault; + bool alpha = image.channels() == 4; + CGBitmapInfo bitmapInfo = (alpha ? kCGImageAlphaLast : kCGImageAlphaNone) | kCGBitmapByteOrderDefault; // Creating CGImage from cv::Mat CGImageRef imageRef = CGImageCreate(image.cols, From 0fcc5face36c360b894ff85be8ee96563182a208 Mon Sep 17 00:00:00 2001 From: Ruslan Baratov Date: Wed, 17 Jun 2015 12:11:11 +0200 Subject: [PATCH 025/133] Fix `cmake -E touch classes.jar' step `-E touch` command doesn't create intermediate directories. We have to do it manually using `file(MAKE_DIRECTORY ...)` command. --- modules/java/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/java/CMakeLists.txt b/modules/java/CMakeLists.txt index 0528fa486e..a653d31aa4 100644 --- a/modules/java/CMakeLists.txt +++ b/modules/java/CMakeLists.txt @@ -282,6 +282,8 @@ else() set(LIB_NAME_SUFIX "${OPENCV_VERSION_MAJOR}${OPENCV_VERSION_MINOR}${OPENCV_VERSION_PATCH}") endif() +file(MAKE_DIRECTORY "${OpenCV_BINARY_DIR}/bin") + # step 4: build jar if(ANDROID) set(JAR_FILE "${OpenCV_BINARY_DIR}/bin/classes.jar") From f30bf39bd4c9695baf73ed6245b6994cc1cf5e96 Mon Sep 17 00:00:00 2001 From: Ruslan Baratov Date: Wed, 17 Jun 2015 13:44:11 +0200 Subject: [PATCH 026/133] Add OpenCV_INCLUDE_DIRS to INTERFACE_INCLUDE_DIRECTORIES target property With this fix there is no need to add includes by using `include_directories(${OpenCV_INCLUDE_DIRS})`. Directory will be added by command `target_link_libraries(... ${OpenCV_LIBS})` automatically. --- cmake/templates/OpenCVConfig.cmake.in | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/cmake/templates/OpenCVConfig.cmake.in b/cmake/templates/OpenCVConfig.cmake.in index e5904aba37..ee57ecda63 100644 --- a/cmake/templates/OpenCVConfig.cmake.in +++ b/cmake/templates/OpenCVConfig.cmake.in @@ -7,7 +7,7 @@ # In your CMakeLists.txt, add these lines: # # find_package(OpenCV REQUIRED) -# include_directories(${OpenCV_INCLUDE_DIRS}) +# include_directories(${OpenCV_INCLUDE_DIRS}) # Not needed for CMake >= 2.8.11 # target_link_libraries(MY_TARGET_NAME ${OpenCV_LIBS}) # # Or you can search for specific OpenCV modules: @@ -177,6 +177,20 @@ if(OpenCV2_INCLUDE_DIRS) endif() endif() +if(NOT CMAKE_VERSION VERSION_LESS "2.8.11") + # Target property INTERFACE_INCLUDE_DIRECTORIES available since 2.8.11: + # * http://www.cmake.org/cmake/help/v2.8.11/cmake.html#prop_tgt:INTERFACE_INCLUDE_DIRECTORIES + foreach(__component ${OpenCV_LIB_COMPONENTS}) + if(TARGET ${__component}) + set_target_properties( + ${__component} + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${OpenCV_INCLUDE_DIRS}" + ) + endif() + endforeach() +endif() + # ============================================================== # Check OpenCV availability # ============================================================== From 1ce0ef9d4119810ecf3615d103b6e5fe2a2f72a0 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 17 Jun 2015 17:23:56 +0300 Subject: [PATCH 027/133] fix build without IPP (and empty 3rdparty) --- cmake/OpenCVUtils.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/OpenCVUtils.cmake b/cmake/OpenCVUtils.cmake index 3e2ea8a7a4..2eaaab8d08 100644 --- a/cmake/OpenCVUtils.cmake +++ b/cmake/OpenCVUtils.cmake @@ -824,7 +824,7 @@ macro(ocv_get_all_libs _modules _extra _3rdparty) endif() # split 3rdparty libs and modules - list(REMOVE_ITEM ${_modules} ${${_3rdparty}} ${${_extra}}) + list(REMOVE_ITEM ${_modules} ${${_3rdparty}} ${${_extra}} non_empty_list) # convert CMake lists to makefile literals foreach(lst ${_modules} ${_3rdparty} ${_extra}) From f63dde2f4387741d5f2bae3cc54332e31c0c7ad2 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Mon, 15 Jun 2015 16:00:49 +0300 Subject: [PATCH 028/133] Separate OpenCV.mk files for different ABIs --- cmake/OpenCVGenAndroidMK.cmake | 10 +++------- cmake/templates/OpenCV-abi.mk.in | 2 ++ cmake/templates/OpenCV.mk.in | 18 ++---------------- 3 files changed, 7 insertions(+), 23 deletions(-) create mode 100644 cmake/templates/OpenCV-abi.mk.in diff --git a/cmake/OpenCVGenAndroidMK.cmake b/cmake/OpenCVGenAndroidMK.cmake index 627d860169..9cc52147af 100644 --- a/cmake/OpenCVGenAndroidMK.cmake +++ b/cmake/OpenCVGenAndroidMK.cmake @@ -30,13 +30,6 @@ if(ANDROID) # replace 'opencv_' -> ''' string(REPLACE "opencv_" "" OPENCV_MODULES_CONFIGMAKE "${OPENCV_MODULES_CONFIGMAKE}") - - # prepare 3rd-party component list without TBB for armeabi and mips platforms. TBB is useless there. - set(OPENCV_3RDPARTY_COMPONENTS_CONFIGMAKE_NO_TBB ${OPENCV_3RDPARTY_COMPONENTS_CONFIGMAKE}) - foreach(mod ${OPENCV_3RDPARTY_COMPONENTS_CONFIGMAKE_NO_TBB}) - string(REPLACE "tbb" "" OPENCV_3RDPARTY_COMPONENTS_CONFIGMAKE_NO_TBB "${OPENCV_3RDPARTY_COMPONENTS_CONFIGMAKE_NO_TBB}") - endforeach() - if(BUILD_FAT_JAVA_LIB) set(OPENCV_LIBS_CONFIGMAKE java3) else() @@ -52,6 +45,7 @@ if(ANDROID) set(OPENCV_3RDPARTY_LIBS_DIR_CONFIGCMAKE "\$(OPENCV_THIS_DIR)/3rdparty/lib/\$(OPENCV_TARGET_ARCH_ABI)") configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCV.mk.in" "${CMAKE_BINARY_DIR}/OpenCV.mk" @ONLY) + configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCV-abi.mk.in" "${CMAKE_BINARY_DIR}/OpenCV-${ANDROID_NDK_ABI_NAME}.mk" @ONLY) # ------------------------------------------------------------------------------------------- # Part 2/2: ${BIN_DIR}/unix-install/OpenCV.mk -> For use with "make install" @@ -62,5 +56,7 @@ if(ANDROID) set(OPENCV_3RDPARTY_LIBS_DIR_CONFIGCMAKE "\$(OPENCV_THIS_DIR)/../3rdparty/libs/\$(OPENCV_TARGET_ARCH_ABI)") configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCV.mk.in" "${CMAKE_BINARY_DIR}/unix-install/OpenCV.mk" @ONLY) + configure_file("${OpenCV_SOURCE_DIR}/cmake/templates/OpenCV-abi.mk.in" "${CMAKE_BINARY_DIR}/unix-install/OpenCV-${ANDROID_NDK_ABI_NAME}.mk" @ONLY) install(FILES ${CMAKE_BINARY_DIR}/unix-install/OpenCV.mk DESTINATION ${OPENCV_CONFIG_INSTALL_PATH} COMPONENT dev) + install(FILES ${CMAKE_BINARY_DIR}/unix-install/OpenCV-${ANDROID_NDK_ABI_NAME}.mk DESTINATION ${OPENCV_CONFIG_INSTALL_PATH} COMPONENT dev) endif(ANDROID) diff --git a/cmake/templates/OpenCV-abi.mk.in b/cmake/templates/OpenCV-abi.mk.in new file mode 100644 index 0000000000..41d5054504 --- /dev/null +++ b/cmake/templates/OpenCV-abi.mk.in @@ -0,0 +1,2 @@ +OPENCV_3RDPARTY_COMPONENTS:=@OPENCV_3RDPARTY_COMPONENTS_CONFIGMAKE@ +OPENCV_EXTRA_COMPONENTS:=@OPENCV_EXTRA_COMPONENTS_CONFIGMAKE@ diff --git a/cmake/templates/OpenCV.mk.in b/cmake/templates/OpenCV.mk.in index acbb763c94..e99b1ad8bd 100644 --- a/cmake/templates/OpenCV.mk.in +++ b/cmake/templates/OpenCV.mk.in @@ -19,6 +19,7 @@ OPENCV_3RDPARTY_LIBS_DIR:=@OPENCV_3RDPARTY_LIBS_DIR_CONFIGCMAKE@ OPENCV_BASEDIR:=@OPENCV_BASE_INCLUDE_DIR_CONFIGCMAKE@ OPENCV_LOCAL_C_INCLUDES:=@OPENCV_INCLUDE_DIRS_CONFIGCMAKE@ OPENCV_MODULES:=@OPENCV_MODULES_CONFIGMAKE@ +OPENCV_SUB_MK:=$(call my-dir)/OpenCV-$(TARGET_ARCH_ABI).mk ifeq ($(OPENCV_LIB_TYPE),) OPENCV_LIB_TYPE:=@OPENCV_LIBTYPE_CONFIGMAKE@ @@ -36,22 +37,7 @@ ifeq ($(OPENCV_LIB_TYPE),SHARED) OPENCV_3RDPARTY_COMPONENTS:= OPENCV_EXTRA_COMPONENTS:= else - ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) - OPENCV_3RDPARTY_COMPONENTS:=@OPENCV_3RDPARTY_COMPONENTS_CONFIGMAKE@ - OPENCV_EXTRA_COMPONENTS:=@OPENCV_EXTRA_COMPONENTS_CONFIGMAKE@ - endif - ifeq ($(TARGET_ARCH_ABI),x86) - OPENCV_3RDPARTY_COMPONENTS:=@OPENCV_3RDPARTY_COMPONENTS_CONFIGMAKE@ - OPENCV_EXTRA_COMPONENTS:=@OPENCV_EXTRA_COMPONENTS_CONFIGMAKE@ - endif - ifeq ($(TARGET_ARCH_ABI),armeabi) - OPENCV_3RDPARTY_COMPONENTS:=@OPENCV_3RDPARTY_COMPONENTS_CONFIGMAKE_NO_TBB@ - OPENCV_EXTRA_COMPONENTS:=@OPENCV_EXTRA_COMPONENTS_CONFIGMAKE@ - endif - ifeq ($(TARGET_ARCH_ABI),mips) - OPENCV_3RDPARTY_COMPONENTS:=@OPENCV_3RDPARTY_COMPONENTS_CONFIGMAKE@ - OPENCV_EXTRA_COMPONENTS:=@OPENCV_EXTRA_COMPONENTS_CONFIGMAKE@ - endif + include $(OPENCV_SUB_MK) endif ifeq ($(OPENCV_LIB_TYPE),SHARED) From 48cc53bf2e971f3656c6e69831216017f7041348 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Mon, 15 Jun 2015 16:01:08 +0300 Subject: [PATCH 029/133] Updated android toolchain --- platforms/android/android.toolchain.cmake | 322 +++++++++------------- 1 file changed, 124 insertions(+), 198 deletions(-) diff --git a/platforms/android/android.toolchain.cmake b/platforms/android/android.toolchain.cmake index b540ea47df..ffa26126a7 100644 --- a/platforms/android/android.toolchain.cmake +++ b/platforms/android/android.toolchain.cmake @@ -30,7 +30,7 @@ # ------------------------------------------------------------------------------ # Android CMake toolchain file, for use with the Android NDK r5-r10d -# Requires cmake 2.6.3 or newer (2.8.5 or newer is recommended). +# Requires cmake 2.6.3 or newer (2.8.9 or newer is recommended). # See home page: https://github.com/taka-no-me/android-cmake # # Usage Linux: @@ -39,12 +39,6 @@ # $ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/the/android.toolchain.cmake .. # $ make -j8 # -# Usage Linux (using standalone toolchain): -# $ export ANDROID_STANDALONE_TOOLCHAIN=/absolute/path/to/android-toolchain -# $ mkdir build && cd build -# $ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/the/android.toolchain.cmake .. -# $ make -j8 -# # Usage Windows: # You need native port of make to build your project. # Android NDK r7 (and newer) already has make.exe on board. @@ -63,11 +57,6 @@ # ANDROID_NDK=/opt/android-ndk - path to the NDK root. # Can be set as environment variable. Can be set only at first cmake run. # -# ANDROID_STANDALONE_TOOLCHAIN=/opt/android-toolchain - path to the -# standalone toolchain. This option is not used if full NDK is found -# (ignored if ANDROID_NDK is set). -# Can be set as environment variable. Can be set only at first cmake run. -# # ANDROID_ABI=armeabi-v7a - specifies the target Application Binary # Interface (ABI). This option nearly matches to the APP_ABI variable # used by ndk-build tool from Android NDK. @@ -123,8 +112,8 @@ # * x86_64-clang3.5 # # ANDROID_FORCE_ARM_BUILD=OFF - set ON to generate 32-bit ARM instructions -# instead of Thumb. Is not available for "x86" (inapplicable) and -# "armeabi-v6 with VFP" (is forced to be ON) ABIs. +# instead of Thumb. Is not available for "armeabi-v6 with VFP" +# (is forced to be ON) ABI. # # ANDROID_NO_UNDEFINED=ON - set ON to show all undefined symbols as linker # errors even if they are not used. @@ -133,13 +122,6 @@ # libraries. Automatically turned for NDK r5x and r6x due to GLESv2 # problems. # -# LIBRARY_OUTPUT_PATH_ROOT=${CMAKE_SOURCE_DIR} - where to output binary -# files. See additional details below. -# -# ANDROID_SET_OBSOLETE_VARIABLES=ON - if set, then toolchain defines some -# obsolete variables which were used by previous versions of this file for -# backward compatibility. -# # ANDROID_STL=gnustl_static - specify the runtime to use. # # Possible values are: @@ -200,12 +182,6 @@ # will be set true, mutually exclusive. NEON option will be set true # if VFP is set to NEON. # -# LIBRARY_OUTPUT_PATH_ROOT should be set in cache to determine where Android -# libraries will be installed. -# Default is ${CMAKE_SOURCE_DIR}, and the android libs will always be -# under the ${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME} -# (depending on the target ABI). This is convenient for Android packaging. -# # ------------------------------------------------------------------------------ cmake_minimum_required( VERSION 2.6.3 ) @@ -235,22 +211,22 @@ endif() # this one not so much set( CMAKE_SYSTEM_VERSION 1 ) -# rpath makes low sence for Android +# rpath makes low sense for Android set( CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "" ) set( CMAKE_SKIP_RPATH TRUE CACHE BOOL "If set, runtime paths are not added when using shared libraries." ) # NDK search paths set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r10d -r10c -r10b -r10 -r9d -r9c -r9b -r9 -r8e -r8d -r8c -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" ) -if(NOT DEFINED ANDROID_NDK_SEARCH_PATHS) +if( NOT DEFINED ANDROID_NDK_SEARCH_PATHS ) if( CMAKE_HOST_WIN32 ) file( TO_CMAKE_PATH "$ENV{PROGRAMFILES}" ANDROID_NDK_SEARCH_PATHS ) - set( ANDROID_NDK_SEARCH_PATHS "${ANDROID_NDK_SEARCH_PATHS}/android-ndk" "$ENV{SystemDrive}/NVPACK/android-ndk" ) + set( ANDROID_NDK_SEARCH_PATHS "${ANDROID_NDK_SEARCH_PATHS}" "$ENV{SystemDrive}/NVPACK" ) else() file( TO_CMAKE_PATH "$ENV{HOME}" ANDROID_NDK_SEARCH_PATHS ) - set( ANDROID_NDK_SEARCH_PATHS /opt/android-ndk "${ANDROID_NDK_SEARCH_PATHS}/NVPACK/android-ndk" ) + set( ANDROID_NDK_SEARCH_PATHS /opt "${ANDROID_NDK_SEARCH_PATHS}/NVPACK" ) endif() endif() -if(NOT DEFINED ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH) +if( NOT DEFINED ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH ) set( ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH /opt/android-toolchain ) endif() @@ -272,106 +248,90 @@ set( ANDROID_DEFAULT_NDK_API_LEVEL_mips64 21 ) macro( __LIST_FILTER listvar regex ) - if( ${listvar} ) - foreach( __val ${${listvar}} ) - if( __val MATCHES "${regex}" ) - list( REMOVE_ITEM ${listvar} "${__val}" ) - endif() - endforeach() - endif() + if( ${listvar} ) + foreach( __val ${${listvar}} ) + if( __val MATCHES "${regex}" ) + list( REMOVE_ITEM ${listvar} "${__val}" ) + endif() + endforeach() + endif() endmacro() macro( __INIT_VARIABLE var_name ) - set( __test_path 0 ) - foreach( __var ${ARGN} ) - if( __var STREQUAL "PATH" ) - set( __test_path 1 ) - break() - endif() - endforeach() - if( __test_path AND NOT EXISTS "${${var_name}}" ) - unset( ${var_name} CACHE ) - endif() - if( "${${var_name}}" STREQUAL "" ) - set( __values 0 ) + set( __test_path 0 ) foreach( __var ${ARGN} ) - if( __var STREQUAL "VALUES" ) - set( __values 1 ) - elseif( NOT __var STREQUAL "PATH" ) - set( __obsolete 0 ) - if( __var MATCHES "^OBSOLETE_.*$" ) - string( REPLACE "OBSOLETE_" "" __var "${__var}" ) - set( __obsolete 1 ) - endif() - if( __var MATCHES "^ENV_.*$" ) - string( REPLACE "ENV_" "" __var "${__var}" ) - set( __value "$ENV{${__var}}" ) - elseif( DEFINED ${__var} ) - set( __value "${${__var}}" ) - else() - if( __values ) - set( __value "${__var}" ) - else() - set( __value "" ) - endif() - endif() - if( NOT "${__value}" STREQUAL "" ) - if( __test_path ) - if( EXISTS "${__value}" ) - file( TO_CMAKE_PATH "${__value}" ${var_name} ) - if( __obsolete AND NOT _CMAKE_IN_TRY_COMPILE ) - message( WARNING "Using value of obsolete variable ${__var} as initial value for ${var_name}. Please note, that ${__var} can be completely removed in future versions of the toolchain." ) - endif() - break() - endif() - else() - set( ${var_name} "${__value}" ) - if( __obsolete AND NOT _CMAKE_IN_TRY_COMPILE ) - message( WARNING "Using value of obsolete variable ${__var} as initial value for ${var_name}. Please note, that ${__var} can be completely removed in future versions of the toolchain." ) - endif() + if( __var STREQUAL "PATH" ) + set( __test_path 1 ) break() - endif() endif() - endif() endforeach() - unset( __value ) - unset( __values ) - unset( __obsolete ) - elseif( __test_path ) - file( TO_CMAKE_PATH "${${var_name}}" ${var_name} ) - endif() - unset( __test_path ) + + if( __test_path AND NOT EXISTS "${${var_name}}" ) + unset( ${var_name} CACHE ) + endif() + + if( " ${${var_name}}" STREQUAL " " ) + set( __values 0 ) + foreach( __var ${ARGN} ) + if( __var STREQUAL "VALUES" ) + set( __values 1 ) + elseif( NOT __var STREQUAL "PATH" ) + if( __var MATCHES "^ENV_.*$" ) + string( REPLACE "ENV_" "" __var "${__var}" ) + set( __value "$ENV{${__var}}" ) + elseif( DEFINED ${__var} ) + set( __value "${${__var}}" ) + elseif( __values ) + set( __value "${__var}" ) + else() + set( __value "" ) + endif() + + if( NOT " ${__value}" STREQUAL " " AND (NOT __test_path OR EXISTS "${__value}") ) + set( ${var_name} "${__value}" ) + break() + endif() + endif() + endforeach() + unset( __value ) + unset( __values ) + endif() + + if( __test_path ) + file( TO_CMAKE_PATH "${${var_name}}" ${var_name} ) + endif() + unset( __test_path ) endmacro() macro( __DETECT_NATIVE_API_LEVEL _var _path ) - SET( __ndkApiLevelRegex "^[\t ]*#define[\t ]+__ANDROID_API__[\t ]+([0-9]+)[\t ]*.*$" ) - FILE( STRINGS ${_path} __apiFileContent REGEX "${__ndkApiLevelRegex}" ) - if( NOT __apiFileContent ) - message( SEND_ERROR "Could not get Android native API level. Probably you have specified invalid level value, or your copy of NDK/toolchain is broken." ) - endif() - string( REGEX REPLACE "${__ndkApiLevelRegex}" "\\1" ${_var} "${__apiFileContent}" ) - unset( __apiFileContent ) - unset( __ndkApiLevelRegex ) + set( __ndkApiLevelRegex "^[\t ]*#define[\t ]+__ANDROID_API__[\t ]+([0-9]+)[\t ]*.*$" ) + file( STRINGS ${_path} __apiFileContent REGEX "${__ndkApiLevelRegex}" ) + if( NOT __apiFileContent ) + message( SEND_ERROR "Could not get Android native API level. Probably you have specified invalid level value, or your copy of NDK/toolchain is broken." ) + endif() + string( REGEX REPLACE "${__ndkApiLevelRegex}" "\\1" ${_var} "${__apiFileContent}" ) + unset( __apiFileContent ) + unset( __ndkApiLevelRegex ) endmacro() macro( __DETECT_TOOLCHAIN_MACHINE_NAME _var _root ) if( EXISTS "${_root}" ) - file( GLOB __gccExePath RELATIVE "${_root}/bin/" "${_root}/bin/*-gcc${TOOL_OS_SUFFIX}" ) - __LIST_FILTER( __gccExePath "^[.].*" ) - list( LENGTH __gccExePath __gccExePathsCount ) - if( NOT __gccExePathsCount EQUAL 1 AND NOT _CMAKE_IN_TRY_COMPILE ) - message( WARNING "Could not determine machine name for compiler from ${_root}" ) - set( ${_var} "" ) + file( GLOB __gccExePath RELATIVE "${_root}/bin/" "${_root}/bin/*-gcc${TOOL_OS_SUFFIX}" ) + __LIST_FILTER( __gccExePath "^[.].*" ) + list( LENGTH __gccExePath __gccExePathsCount ) + if( NOT __gccExePathsCount EQUAL 1 AND NOT _CMAKE_IN_TRY_COMPILE ) + message( WARNING "Could not determine machine name for compiler from ${_root}" ) + set( ${_var} "" ) + else() + get_filename_component( __gccExeName "${__gccExePath}" NAME_WE ) + string( REPLACE "-gcc" "" ${_var} "${__gccExeName}" ) + endif() + unset( __gccExePath ) + unset( __gccExePathsCount ) + unset( __gccExeName ) else() - get_filename_component( __gccExeName "${__gccExePath}" NAME_WE ) - string( REPLACE "-gcc" "" ${_var} "${__gccExeName}" ) + set( ${_var} "" ) endif() - unset( __gccExePath ) - unset( __gccExePathsCount ) - unset( __gccExeName ) - else() - set( ${_var} "" ) - endif() endmacro() @@ -419,17 +379,19 @@ if( NOT ANDROID_NDK_HOST_X64 ) endif() # see if we have path to Android NDK -__INIT_VARIABLE( ANDROID_NDK PATH ENV_ANDROID_NDK ) +if( NOT ANDROID_NDK AND NOT ANDROID_STANDALONE_TOOLCHAIN ) + __INIT_VARIABLE( ANDROID_NDK PATH ENV_ANDROID_NDK ) +endif() if( NOT ANDROID_NDK ) # see if we have path to Android standalone toolchain - __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ENV_ANDROID_STANDALONE_TOOLCHAIN OBSOLETE_ANDROID_NDK_TOOLCHAIN_ROOT OBSOLETE_ENV_ANDROID_NDK_TOOLCHAIN_ROOT ) + __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ENV_ANDROID_STANDALONE_TOOLCHAIN ) if( NOT ANDROID_STANDALONE_TOOLCHAIN ) #try to find Android NDK in one of the the default locations set( __ndkSearchPaths ) foreach( __ndkSearchPath ${ANDROID_NDK_SEARCH_PATHS} ) foreach( suffix ${ANDROID_SUPPORTED_NDK_VERSIONS} ) - list( APPEND __ndkSearchPaths "${__ndkSearchPath}${suffix}" ) + list( APPEND __ndkSearchPaths "${__ndkSearchPath}/android-ndk${suffix}" ) endforeach() endforeach() __INIT_VARIABLE( ANDROID_NDK PATH VALUES ${__ndkSearchPaths} ) @@ -487,7 +449,7 @@ else() or export ANDROID_STANDALONE_TOOLCHAIN=~/my-android-toolchain or put the toolchain or NDK in the default path: - sudo ln -s ~/my-android-ndk ${ANDROID_NDK_SEARCH_PATH} + sudo ln -s ~/my-android-ndk ${ANDROID_NDK_SEARCH_PATH}/android-ndk sudo ln -s ~/my-android-toolchain ${ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH}" ) endif() @@ -636,7 +598,7 @@ if( BUILD_WITH_ANDROID_NDK ) endif() if( NOT __availableToolchains ) file( GLOB __availableToolchainsLst RELATIVE "${ANDROID_NDK_TOOLCHAINS_PATH}" "${ANDROID_NDK_TOOLCHAINS_PATH}/*" ) - if( __availableToolchains ) + if( __availableToolchainsLst ) list(SORT __availableToolchainsLst) # we need clang to go after gcc endif() __LIST_FILTER( __availableToolchainsLst "^[.]" ) @@ -669,7 +631,7 @@ if( NOT ANDROID_SUPPORTED_ABIS ) endif() # choose target ABI -__INIT_VARIABLE( ANDROID_ABI OBSOLETE_ARM_TARGET OBSOLETE_ARM_TARGETS VALUES ${ANDROID_SUPPORTED_ABIS} ) +__INIT_VARIABLE( ANDROID_ABI VALUES ${ANDROID_SUPPORTED_ABIS} ) # verify that target ABI is supported list( FIND ANDROID_SUPPORTED_ABIS "${ANDROID_ABI}" __androidAbiIdx ) if( __androidAbiIdx EQUAL -1 ) @@ -760,7 +722,7 @@ if( CMAKE_BINARY_DIR AND EXISTS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMa endif() if( ANDROID_ARCH_NAME STREQUAL "arm" AND NOT ARMEABI_V6 ) - __INIT_VARIABLE( ANDROID_FORCE_ARM_BUILD OBSOLETE_FORCE_ARM VALUES OFF ) + __INIT_VARIABLE( ANDROID_FORCE_ARM_BUILD VALUES OFF ) set( ANDROID_FORCE_ARM_BUILD ${ANDROID_FORCE_ARM_BUILD} CACHE BOOL "Use 32-bit ARM instructions instead of Thumb-1" FORCE ) mark_as_advanced( ANDROID_FORCE_ARM_BUILD ) else() @@ -845,6 +807,7 @@ else() unset( __realApiLevel ) endif() set( ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" CACHE STRING "Android API level for native code" FORCE ) + set( CMAKE_ANDROID_API ${ANDROID_NATIVE_API_LEVEL} ) if( CMAKE_VERSION VERSION_GREATER "2.8" ) list( SORT ANDROID_SUPPORTED_NATIVE_API_LEVELS ) set_property( CACHE ANDROID_NATIVE_API_LEVEL PROPERTY STRINGS ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) @@ -863,16 +826,7 @@ endif() # runtime choice (STL, rtti, exceptions) if( NOT ANDROID_STL ) - # honor legacy ANDROID_USE_STLPORT - if( DEFINED ANDROID_USE_STLPORT ) - if( ANDROID_USE_STLPORT ) - set( ANDROID_STL stlport_static ) - endif() - message( WARNING "You are using an obsolete variable ANDROID_USE_STLPORT to select the STL variant. Use -DANDROID_STL=stlport_static instead." ) - endif() - if( NOT ANDROID_STL ) set( ANDROID_STL gnustl_static ) - endif() endif() set( ANDROID_STL "${ANDROID_STL}" CACHE STRING "C++ runtime" ) set( ANDROID_STL_FORCE_FEATURES ON CACHE BOOL "automatically configure rtti and exceptions support based on C++ runtime" ) @@ -1033,7 +987,7 @@ if( BUILD_WITH_ANDROID_NDK ) set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" ) elseif( ANDROID_STL MATCHES "gabi" ) if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 - message( FATAL_ERROR "gabi++ is not awailable in your NDK. You have to upgrade to NDK r7 or newer to use gabi++.") + message( FATAL_ERROR "gabi++ is not available in your NDK. You have to upgrade to NDK r7 or newer to use gabi++.") endif() set( ANDROID_RTTI ON ) set( ANDROID_EXCEPTIONS OFF ) @@ -1144,7 +1098,12 @@ if( NOT CMAKE_C_COMPILER ) endif() set( CMAKE_ASM_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "assembler" ) set( CMAKE_STRIP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-strip${TOOL_OS_SUFFIX}" CACHE PATH "strip" ) - set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) + if( EXISTS "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}" ) + # Use gcc-ar if we have it for better LTO support. + set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) + else() + set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) + endif() set( CMAKE_LINKER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ld${TOOL_OS_SUFFIX}" CACHE PATH "linker" ) set( CMAKE_NM "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-nm${TOOL_OS_SUFFIX}" CACHE PATH "nm" ) set( CMAKE_OBJCOPY "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objcopy${TOOL_OS_SUFFIX}" CACHE PATH "objcopy" ) @@ -1233,7 +1192,7 @@ endif() # NDK flags if (ARM64_V8A ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -ffunction-sections -funwind-tables" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" ) set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" ) if( NOT ANDROID_COMPILER_IS_CLANG ) @@ -1263,7 +1222,7 @@ elseif( X86 OR X86_64 ) set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" ) set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" ) elseif( MIPS OR MIPS64 ) - set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-strict-aliasing -finline-functions -ffunction-sections -funwind-tables -fmessage-length=0" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-strict-aliasing -finline-functions -funwind-tables -fmessage-length=0" ) set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer" ) set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer" ) if( NOT ANDROID_COMPILER_IS_CLANG ) @@ -1348,7 +1307,7 @@ if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7 else() __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES OFF ) endif() -__INIT_VARIABLE( ANDROID_NO_UNDEFINED OBSOLETE_NO_UNDEFINED VALUES ON ) +__INIT_VARIABLE( ANDROID_NO_UNDEFINED VALUES ON ) __INIT_VARIABLE( ANDROID_FUNCTION_LEVEL_LINKING VALUES ON ) __INIT_VARIABLE( ANDROID_GOLD_LINKER VALUES ON ) __INIT_VARIABLE( ANDROID_NOEXECSTACK VALUES ON ) @@ -1356,7 +1315,7 @@ __INIT_VARIABLE( ANDROID_RELRO VALUES ON ) set( ANDROID_NO_UNDEFINED ${ANDROID_NO_UNDEFINED} CACHE BOOL "Show all undefined symbols as linker errors" ) set( ANDROID_SO_UNDEFINED ${ANDROID_SO_UNDEFINED} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) -set( ANDROID_FUNCTION_LEVEL_LINKING ${ANDROID_FUNCTION_LEVEL_LINKING} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) +set( ANDROID_FUNCTION_LEVEL_LINKING ${ANDROID_FUNCTION_LEVEL_LINKING} CACHE BOOL "Put each function in separate section and enable garbage collection of unused input sections at link time" ) set( ANDROID_GOLD_LINKER ${ANDROID_GOLD_LINKER} CACHE BOOL "Enables gold linker" ) set( ANDROID_NOEXECSTACK ${ANDROID_NOEXECSTACK} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) set( ANDROID_RELRO ${ANDROID_RELRO} CACHE BOOL "Enables RELRO - a memory corruption mitigation technique" ) @@ -1531,27 +1490,31 @@ if( ANDROID_EXPLICIT_CRT_LINK ) endif() # setup output directories -set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_SOURCE_DIR} CACHE PATH "root for library output, set this to change where android libs are installed to" ) set( CMAKE_INSTALL_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/user" CACHE STRING "path for installing" ) -if(NOT _CMAKE_IN_TRY_COMPILE) - if( EXISTS "${CMAKE_SOURCE_DIR}/jni/CMakeLists.txt" ) - set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for applications" ) - else() - set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin" CACHE PATH "Output directory for applications" ) - endif() - set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "path for android libs" ) +if( DEFINED LIBRARY_OUTPUT_PATH_ROOT + OR EXISTS "${CMAKE_SOURCE_DIR}/AndroidManifest.xml" + OR (EXISTS "${CMAKE_SOURCE_DIR}/../AndroidManifest.xml" AND EXISTS "${CMAKE_SOURCE_DIR}/../jni/") ) + set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_SOURCE_DIR} CACHE PATH "Root for binaries output, set this to change where Android libs are installed to" ) + if( NOT _CMAKE_IN_TRY_COMPILE ) + if( EXISTS "${CMAKE_SOURCE_DIR}/jni/CMakeLists.txt" ) + set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for applications" ) + else() + set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin" CACHE PATH "Output directory for applications" ) + endif() + set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for Android libs" ) + endif() endif() # copy shaed stl library to build directory -if( NOT _CMAKE_IN_TRY_COMPILE AND __libstl MATCHES "[.]so$" ) - get_filename_component( __libstlname "${__libstl}" NAME ) - execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${__libstl}" "${LIBRARY_OUTPUT_PATH}/${__libstlname}" RESULT_VARIABLE __fileCopyProcess ) - if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${LIBRARY_OUTPUT_PATH}/${__libstlname}") - message( SEND_ERROR "Failed copying of ${__libstl} to the ${LIBRARY_OUTPUT_PATH}/${__libstlname}" ) - endif() - unset( __fileCopyProcess ) - unset( __libstlname ) +if( NOT _CMAKE_IN_TRY_COMPILE AND __libstl MATCHES "[.]so$" AND DEFINED LIBRARY_OUTPUT_PATH ) + get_filename_component( __libstlname "${__libstl}" NAME ) + execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${__libstl}" "${LIBRARY_OUTPUT_PATH}/${__libstlname}" RESULT_VARIABLE __fileCopyProcess ) + if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${LIBRARY_OUTPUT_PATH}/${__libstlname}") + message( SEND_ERROR "Failed copying of ${__libstl} to the ${LIBRARY_OUTPUT_PATH}/${__libstlname}" ) + endif() + unset( __fileCopyProcess ) + unset( __libstlname ) endif() @@ -1612,25 +1575,10 @@ macro( find_host_program ) endmacro() -macro( ANDROID_GET_ABI_RAWNAME TOOLCHAIN_FLAG VAR ) - if( "${TOOLCHAIN_FLAG}" STREQUAL "ARMEABI" ) - set( ${VAR} "armeabi" ) - elseif( "${TOOLCHAIN_FLAG}" STREQUAL "ARMEABI_V7A" ) - set( ${VAR} "armeabi-v7a" ) - elseif( "${TOOLCHAIN_FLAG}" STREQUAL "X86" ) - set( ${VAR} "x86" ) - elseif( "${TOOLCHAIN_FLAG}" STREQUAL "MIPS" ) - set( ${VAR} "mips" ) - else() - set( ${VAR} "unknown" ) - endif() -endmacro() - - # export toolchain settings for the try_compile() command -if( NOT PROJECT_NAME STREQUAL "CMAKE_TRY_COMPILE" ) +if( NOT _CMAKE_IN_TRY_COMPILE ) set( __toolchain_config "") - foreach( __var NDK_CCACHE LIBRARY_OUTPUT_PATH_ROOT ANDROID_FORBID_SYGWIN ANDROID_SET_OBSOLETE_VARIABLES + foreach( __var NDK_CCACHE LIBRARY_OUTPUT_PATH_ROOT ANDROID_FORBID_SYGWIN ANDROID_NDK_HOST_X64 ANDROID_NDK ANDROID_NDK_LAYOUT @@ -1652,7 +1600,7 @@ if( NOT PROJECT_NAME STREQUAL "CMAKE_TRY_COMPILE" ) ANDROID_APP_PIE ) if( DEFINED ${__var} ) - if( "${__var}" MATCHES " ") + if( ${__var} MATCHES " ") set( __toolchain_config "${__toolchain_config}set( ${__var} \"${${__var}}\" CACHE INTERNAL \"\" )\n" ) else() set( __toolchain_config "${__toolchain_config}set( ${__var} ${${__var}} CACHE INTERNAL \"\" )\n" ) @@ -1677,16 +1625,6 @@ if( CMAKE_GENERATOR MATCHES "Ninja" AND CMAKE_HOST_WIN32 ) endif() -# set some obsolete variables for backward compatibility -set( ANDROID_SET_OBSOLETE_VARIABLES ON CACHE BOOL "Define obsolete Andrid-specific cmake variables" ) -mark_as_advanced( ANDROID_SET_OBSOLETE_VARIABLES ) -if( ANDROID_SET_OBSOLETE_VARIABLES ) - set( ANDROID_API_LEVEL ${ANDROID_NATIVE_API_LEVEL} ) - set( ARM_TARGET "${ANDROID_ABI}" ) - set( ARMEABI_NDK_NAME "${ANDROID_NDK_ABI_NAME}" ) -endif() - - # Variables controlling behavior or set by cmake toolchain: # ANDROID_ABI : "armeabi-v7a" (default), "armeabi", "armeabi-v7a with NEON", "armeabi-v7a with VFPV3", "armeabi-v6 with VFP", "x86", "mips", "arm64-v8a", "x86_64", "mips64" # ANDROID_NATIVE_API_LEVEL : 3,4,5,8,9,14,15,16,17,18,19,21 (depends on NDK version) @@ -1700,22 +1638,15 @@ endif() # ANDROID_RELRO : ON/OFF # ANDROID_FORCE_ARM_BUILD : ON/OFF # ANDROID_STL_FORCE_FEATURES : ON/OFF -# ANDROID_SET_OBSOLETE_VARIABLES : ON/OFF +# ANDROID_LIBM_PATH : path to libm.so (set to something like $(TOP)/out/target/product//obj/lib/libm.so) to workaround unresolved `sincos` # Can be set only at the first run: -# ANDROID_NDK -# ANDROID_STANDALONE_TOOLCHAIN +# ANDROID_NDK : path to your NDK install +# NDK_CCACHE : path to your ccache executable # ANDROID_TOOLCHAIN_NAME : the NDK name of compiler toolchain # ANDROID_NDK_HOST_X64 : try to use x86_64 toolchain (default for x64 host systems) # ANDROID_NDK_LAYOUT : the inner NDK structure (RELEASE, LINARO, ANDROID) # LIBRARY_OUTPUT_PATH_ROOT : -# NDK_CCACHE : -# Obsolete: -# ANDROID_API_LEVEL : superseded by ANDROID_NATIVE_API_LEVEL -# ARM_TARGET : superseded by ANDROID_ABI -# ARM_TARGETS : superseded by ANDROID_ABI (can be set only) -# ANDROID_NDK_TOOLCHAIN_ROOT : superseded by ANDROID_STANDALONE_TOOLCHAIN (can be set only) -# ANDROID_USE_STLPORT : superseded by ANDROID_STL=stlport_static -# ANDROID_LEVEL : superseded by ANDROID_NATIVE_API_LEVEL (completely removed) +# ANDROID_STANDALONE_TOOLCHAIN # # Primary read-only variables: # ANDROID : always TRUE @@ -1729,7 +1660,6 @@ endif() # X86_64 : TRUE if configured for x86_64 # MIPS : TRUE if configured for mips # MIPS64 : TRUE if configured for mips64 -# BUILD_ANDROID : always TRUE # BUILD_WITH_ANDROID_NDK : TRUE if NDK is used # BUILD_WITH_STANDALONE_TOOLCHAIN : TRUE if standalone toolchain is used # ANDROID_NDK_HOST_SYSTEM_NAME : "windows", "linux-x86" or "darwin-x86" depending on host platform @@ -1740,8 +1670,6 @@ endif() # ANDROID_SYSROOT : path to the compiler sysroot # TOOL_OS_SUFFIX : "" or ".exe" depending on host platform # ANDROID_COMPILER_IS_CLANG : TRUE if clang compiler is used -# Obsolete: -# ARMEABI_NDK_NAME : superseded by ANDROID_NDK_ABI_NAME # # Secondary (less stable) read-only variables: # ANDROID_COMPILER_VERSION : GCC version used (not Clang version) @@ -1756,12 +1684,10 @@ endif() # ANDROID_RTTI : if rtti is enabled by the runtime # ANDROID_EXCEPTIONS : if exceptions are enabled by the runtime # ANDROID_GCC_TOOLCHAIN_NAME : read-only, differs from ANDROID_TOOLCHAIN_NAME only if clang is used -# ANDROID_LIBM_PATH : path to libm.so (set to something like $(TOP)/out/target/product//obj/lib/libm.so) to workaround unresolved `sincos` # # Defaults: # ANDROID_DEFAULT_NDK_API_LEVEL # ANDROID_DEFAULT_NDK_API_LEVEL_${ARCH} # ANDROID_NDK_SEARCH_PATHS -# ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH # ANDROID_SUPPORTED_ABIS_${ARCH} # ANDROID_SUPPORTED_NDK_VERSIONS From c79ad45fce73a1ee4861ed97c0e17eb24571dceb Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Tue, 16 Jun 2015 16:40:18 +0300 Subject: [PATCH 030/133] Added interface libraries for android opencv_java --- modules/java/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/java/CMakeLists.txt b/modules/java/CMakeLists.txt index 0528fa486e..86c25f65ad 100644 --- a/modules/java/CMakeLists.txt +++ b/modules/java/CMakeLists.txt @@ -369,6 +369,7 @@ set_target_properties(${the_module} PROPERTIES if(ANDROID) ocv_target_link_libraries(${the_module} jnigraphics) # for Mat <=> Bitmap converters + ocv_target_link_libraries(${the_module} LINK_INTERFACE_LIBRARIES ${OPENCV_LINKER_LIBS} jnigraphics) # force strip library after the build command # because samples and tests will make a copy of the library before install From bfa77ff5a408a5a4cbe2ce7a1e10db6da1a7ebc6 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Tue, 16 Jun 2015 17:07:32 +0300 Subject: [PATCH 031/133] Increase android samples versions --- samples/android/15-puzzle/AndroidManifest.xml | 7 ++++--- samples/android/camera-calibration/AndroidManifest.xml | 4 ++-- samples/android/color-blob-detection/AndroidManifest.xml | 4 ++-- samples/android/face-detection/AndroidManifest.xml | 4 ++-- samples/android/image-manipulations/AndroidManifest.xml | 4 ++-- .../android/tutorial-1-camerapreview/AndroidManifest.xml | 4 ++-- .../android/tutorial-2-mixedprocessing/AndroidManifest.xml | 4 ++-- .../android/tutorial-3-cameracontrol/AndroidManifest.xml | 4 ++-- 8 files changed, 18 insertions(+), 17 deletions(-) diff --git a/samples/android/15-puzzle/AndroidManifest.xml b/samples/android/15-puzzle/AndroidManifest.xml index 356382bf14..c783076a84 100644 --- a/samples/android/15-puzzle/AndroidManifest.xml +++ b/samples/android/15-puzzle/AndroidManifest.xml @@ -1,7 +1,8 @@ + + android:versionCode="301" + android:versionName="3.01" > @@ -31,4 +32,4 @@ - \ No newline at end of file + diff --git a/samples/android/camera-calibration/AndroidManifest.xml b/samples/android/camera-calibration/AndroidManifest.xml index 619c919eec..7c03ba0bee 100644 --- a/samples/android/camera-calibration/AndroidManifest.xml +++ b/samples/android/camera-calibration/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="301" + android:versionName="3.01" > + android:versionCode="301" + android:versionName="3.01"> + android:versionCode="301" + android:versionName="3.01"> + android:versionCode="301" + android:versionName="3.01"> + android:versionCode="301" + android:versionName="3.01"> + android:versionCode="301" + android:versionName="3.01"> + android:versionCode="301" + android:versionName="3.01"> Date: Tue, 16 Jun 2015 17:19:16 +0300 Subject: [PATCH 032/133] Updated android manager readme file --- platforms/android/service/readme.txt | 36 ++++++++++------------------ 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/platforms/android/service/readme.txt b/platforms/android/service/readme.txt index 51853c24e5..5bd773b0e6 100644 --- a/platforms/android/service/readme.txt +++ b/platforms/android/service/readme.txt @@ -2,32 +2,22 @@ How to select the proper version of OpenCV Manager -------------------------------------------------- Since version 1.7 several packages of OpenCV Manager are built. Every package is targeted for some -specific hardware platform and includes corresponding OpenCV binaries. So, in most cases OpenCV -Manager uses built-in version of OpenCV. Separate package with OpenCV binaries is currently used in -a single rare case, when an ARMv7-A processor without NEON support is detected. In this case an -additional binary package is used. The new package selection logic in most cases simplifies OpenCV -installation on end user devices. In most cases OpenCV Manager may be installed automatically from -Google Play. +specific hardware platform and includes corresponding OpenCV binaries. So, in all cases OpenCV +Manager uses built-in version of OpenCV. The new package selection logic in most cases simplifies +OpenCV installation on end user devices. In most cases OpenCV Manager may be installed automatically +from Google Play. If Google Play is not available (i.e. on emulator, developer board, etc), you can install it manually using adb tool: -.. code-block:: sh + adb install /apk/OpenCV_3.0.0_Manager_3.00_.apk - adb install OpenCV-2.4.9-android-sdk/apk/OpenCV_2.4.9_Manager_2.18_.apk +Use the list below to determine proper OpenCV Manager package for your device: -Use the table below to determine proper OpenCV Manager package for your device: - -+------------------------------+--------------+----------------------------------------------------+ -| Hardware Platform | Android ver. | Package name | -+==============================+==============+====================================================+ -| armeabi-v7a (ARMv7-A + NEON) | >= 2.3 | OpenCV_2.4.9_Manager_2.18_armv7a-neon.apk | -+------------------------------+--------------+----------------------------------------------------+ -| armeabi-v7a (ARMv7-A + NEON) | = 2.2 | OpenCV_2.4.9_Manager_2.18_armv7a-neon-android8.apk | -+------------------------------+--------------+----------------------------------------------------+ -| armeabi (ARMv5, ARMv6) | >= 2.3 | OpenCV_2.4.9_Manager_2.18_armeabi.apk | -+------------------------------+--------------+----------------------------------------------------+ -| Intel x86 | >= 2.3 | OpenCV_2.4.9_Manager_2.18_x86.apk | -+------------------------------+--------------+----------------------------------------------------+ -| MIPS | >= 2.3 | OpenCV_2.4.9_Manager_2.18_mips.apk | -+------------------------------+--------------+----------------------------------------------------+ +- OpenCV_3.0.0-dev_Manager_3.00_armeabi.apk - armeabi (ARMv5, ARMv6) +- OpenCV_3.0.0-dev_Manager_3.00_armeabi-v7a.apk - armeabi-v7a (ARMv7-A + NEON) +- OpenCV_3.0.0-dev_Manager_3.00_arm64-v8a.apk - arm64-v8a (ARM64-v8a) +- OpenCV_3.0.0-dev_Manager_3.00_mips.apk - mips (MIPS) +- OpenCV_3.0.0-dev_Manager_3.00_mips64.apk - mips64 (MIPS64) +- OpenCV_3.0.0-dev_Manager_3.00_x86.apk - x86 +- OpenCV_3.0.0-dev_Manager_3.00_x86_64.apk - x86_64 From 83b882039ebd045a6ac3affd12d7f75db7096271 Mon Sep 17 00:00:00 2001 From: Ruslan Baratov Date: Wed, 17 Jun 2015 17:53:05 +0200 Subject: [PATCH 033/133] Update samples/cpp/example_cmake: no need to use `include_directories` --- samples/cpp/example_cmake/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/samples/cpp/example_cmake/CMakeLists.txt b/samples/cpp/example_cmake/CMakeLists.txt index fe7e629812..00c1687fa0 100644 --- a/samples/cpp/example_cmake/CMakeLists.txt +++ b/samples/cpp/example_cmake/CMakeLists.txt @@ -18,8 +18,10 @@ message(STATUS " version: ${OpenCV_VERSION}") message(STATUS " libraries: ${OpenCV_LIBS}") message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}") -# Add OpenCV headers location to your include paths -include_directories(${OpenCV_INCLUDE_DIRS}) +if(CMAKE_VERSION VERSION_LESS "2.8.11") + # Add OpenCV headers location to your include paths + include_directories(${OpenCV_INCLUDE_DIRS}) +endif() # Declare the executable target built from your sources add_executable(opencv_example example.cpp) From f11ed4b91d7653dc4aba143fcb3f82b32cd38cde Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 17 Jun 2015 20:07:56 +0300 Subject: [PATCH 034/133] fix "non target" warning for "world" build --- cmake/OpenCVUtils.cmake | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cmake/OpenCVUtils.cmake b/cmake/OpenCVUtils.cmake index 3e2ea8a7a4..44672d17f5 100644 --- a/cmake/OpenCVUtils.cmake +++ b/cmake/OpenCVUtils.cmake @@ -795,8 +795,12 @@ macro(ocv_get_all_libs _modules _extra _3rdparty) set(${_extra} "") set(${_3rdparty} "") foreach(m ${OPENCV_MODULES_PUBLIC}) - get_target_property(deps ${m} INTERFACE_LINK_LIBRARIES) - if(NOT deps) + if(TARGET ${m}) + get_target_property(deps ${m} INTERFACE_LINK_LIBRARIES) + if(NOT deps) + set(deps "") + endif() + else() set(deps "") endif() list(INSERT ${_modules} 0 ${deps} ${m}) From a55a8c9aa50bbd8c1e5605db060404aff1c86fc1 Mon Sep 17 00:00:00 2001 From: manuele Date: Thu, 18 Jun 2015 11:19:46 +0200 Subject: [PATCH 035/133] Enable NEON optimization for cvRound on newer devices --- modules/hal/include/opencv2/hal/defs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hal/include/opencv2/hal/defs.h b/modules/hal/include/opencv2/hal/defs.h index 197533993b..1c30073a07 100644 --- a/modules/hal/include/opencv2/hal/defs.h +++ b/modules/hal/include/opencv2/hal/defs.h @@ -179,7 +179,7 @@ # define CV_NEON 1 #endif -#if defined __GNUC__ && defined __arm__ && (defined __ARM_PCS_VFP || defined __ARM_VFPV3__) +#if defined __GNUC__ && defined __arm__ && (defined __ARM_PCS_VFP || defined __ARM_VFPV3__ || defined __ARM_NEON__) && !defined __SOFTFP__ # define CV_VFP 1 #endif From a5a21019b2c6a40d116e0d29d6f4a6aa6816b0ed Mon Sep 17 00:00:00 2001 From: Dmitry Budnikov Date: Fri, 15 May 2015 11:15:00 +0300 Subject: [PATCH 036/133] ipp_countNonZero build fix; Removed IPP port for tiny arithm.cpp functions Additional warnings fix on various platforms. Build without OPENCL and GCC warnings fixed Fixed warnings, trailing spaces and removed unused secure_cpy. IPP code refactored. IPP code path implemented as separate static functions to simplify future work with IPP code and make it more readable. --- modules/core/include/opencv2/core/base.hpp | 1 + modules/core/include/opencv2/core/private.hpp | 58 + modules/core/src/convert.cpp | 96 +- modules/core/src/copy.cpp | 357 ++--- modules/core/src/matrix.cpp | 140 +- modules/core/src/precomp.hpp | 1 + modules/core/src/stat.cpp | 1100 +++++++------- modules/imgproc/src/accum.cpp | 504 +++--- modules/imgproc/src/color.cpp | 1352 ++++++++--------- modules/imgproc/src/corner.cpp | 81 +- modules/imgproc/src/deriv.cpp | 178 ++- modules/imgproc/src/filter.cpp | 161 +- modules/imgproc/src/histogram.cpp | 37 +- modules/imgproc/src/imgwarp.cpp | 101 +- modules/imgproc/src/morph.cpp | 13 +- modules/imgproc/src/pyramids.cpp | 304 ++-- modules/imgproc/src/smooth.cpp | 346 +++-- modules/imgproc/src/sumpixels.cpp | 104 +- modules/imgproc/src/templmatch.cpp | 147 +- modules/imgproc/src/thresh.cpp | 37 +- 20 files changed, 2723 insertions(+), 2395 deletions(-) diff --git a/modules/core/include/opencv2/core/base.hpp b/modules/core/include/opencv2/core/base.hpp index 83cc311c42..3d440dabe0 100644 --- a/modules/core/include/opencv2/core/base.hpp +++ b/modules/core/include/opencv2/core/base.hpp @@ -305,6 +305,7 @@ enum BorderTypes { #define CV_SUPPRESS_DEPRECATED_START #define CV_SUPPRESS_DEPRECATED_END #endif +#define CV_UNUSED(name) (void)name //! @endcond /*! @brief Signals an error and raises the exception. diff --git a/modules/core/include/opencv2/core/private.hpp b/modules/core/include/opencv2/core/private.hpp index 4f9f487778..5bda617b33 100644 --- a/modules/core/include/opencv2/core/private.hpp +++ b/modules/core/include/opencv2/core/private.hpp @@ -235,9 +235,67 @@ static inline IppDataType ippiGetDataType(int depth) # define IPP_VERSION_X100 0 #endif +#ifdef HAVE_IPP_ICV_ONLY +#define HAVE_ICV 1 +#else +#define HAVE_ICV 0 +#endif + + #define CV_IPP_CHECK_COND (cv::ipp::useIPP()) #define CV_IPP_CHECK() if(CV_IPP_CHECK_COND) +#ifdef HAVE_IPP + +#ifdef CV_IPP_RUN_VERBOSE +#define CV_IPP_RUN_(condition, func, ...) \ + { \ + if (cv::ipp::useIPP() && (condition) && func) \ + { \ + printf("%s: IPP implementation is running\n", CV_Func); \ + fflush(stdout); \ + CV_IMPL_ADD(CV_IMPL_IPP); \ + return __VA_ARGS__; \ + } \ + else \ + { \ + printf("%s: Plain implementation is running\n", CV_Func); \ + fflush(stdout); \ + } \ + } +#elif defined CV_IPP_RUN_ASSERT +#define CV_IPP_RUN_(condition, func, ...) \ + { \ + if (cv::ipp::useIPP() && (condition)) \ + { \ + if(func) \ + { \ + CV_IMPL_ADD(CV_IMPL_IPP); \ + } \ + else \ + { \ + setIppErrorStatus(); \ + CV_Error(cv::Error::StsAssert, #func); \ + } \ + return __VA_ARGS__; \ + } \ + } +#else +#define CV_IPP_RUN_(condition, func, ...) \ + if (cv::ipp::useIPP() && (condition) && func) \ + { \ + CV_IMPL_ADD(CV_IMPL_IPP); \ + return __VA_ARGS__; \ + } +#endif + +#else +#define CV_IPP_RUN_(condition, func, ...) +#endif + +#define CV_IPP_RUN(condition, func, ...) CV_IPP_RUN_(condition, func, __VA_ARGS__) + + #ifndef IPPI_CALL # define IPPI_CALL(func) CV_Assert((func) >= 0) #endif diff --git a/modules/core/src/convert.cpp b/modules/core/src/convert.cpp index 090acf5508..cebbb154e2 100644 --- a/modules/core/src/convert.cpp +++ b/modules/core/src/convert.cpp @@ -5194,17 +5194,9 @@ dtype* dst, size_t dstep, Size size, double* scale) \ static void cvt##suffix( const stype* src, size_t sstep, const uchar*, size_t, \ dtype* dst, size_t dstep, Size size, double*) \ { \ - CV_IPP_CHECK()\ + if (src && dst)\ {\ - if (src && dst)\ - {\ - if (ippiConvert_##ippFavor(src, (int)sstep, dst, (int)dstep, ippiSize(size.width, size.height)) >= 0) \ - {\ - CV_IMPL_ADD(CV_IMPL_IPP)\ - return; \ - }\ - setIppErrorStatus(); \ - }\ + CV_IPP_RUN(true, ippiConvert_##ippFavor(src, (int)sstep, dst, (int)dstep, ippiSize(size.width, size.height)) >= 0)\ }\ cvt_(src, sstep, dst, dstep, size); \ } @@ -5213,17 +5205,9 @@ static void cvt##suffix( const stype* src, size_t sstep, const uchar*, size_t, \ static void cvt##suffix( const stype* src, size_t sstep, const uchar*, size_t, \ dtype* dst, size_t dstep, Size size, double*) \ { \ - CV_IPP_CHECK()\ + if (src && dst)\ {\ - if (src && dst)\ - {\ - if (ippiConvert_##ippFavor(src, (int)sstep, dst, (int)dstep, ippiSize(size.width, size.height), ippRndFinancial, 0) >= 0) \ - {\ - CV_IMPL_ADD(CV_IMPL_IPP)\ - return; \ - }\ - setIppErrorStatus(); \ - }\ + CV_IPP_RUN(true, ippiConvert_##ippFavor(src, (int)sstep, dst, (int)dstep, ippiSize(size.width, size.height), ippRndFinancial, 0) >= 0)\ }\ cvt_(src, sstep, dst, dstep, size); \ } @@ -5907,6 +5891,55 @@ private: } +namespace cv +{ +#if defined(HAVE_IPP) +static bool ipp_lut(InputArray _src, InputArray _lut, OutputArray _dst) +{ + int cn = _src.channels(); + int lutcn = _lut.channels(); + + Mat src = _src.getMat(), lut = _lut.getMat(); + _dst.create(src.dims, src.size, CV_MAKETYPE(_lut.depth(), cn)); + Mat dst = _dst.getMat(); + + if (_src.dims() <= 2) + { + bool ok = false; + Ptr body; + + size_t elemSize1 = CV_ELEM_SIZE1(dst.depth()); +#if 0 // there are no performance benefits (PR #2653) + if (lutcn == 1) + { + ParallelLoopBody* p = new ipp::IppLUTParallelBody_LUTC1(src, lut, dst, &ok); + body.reset(p); + } + else +#endif + if ((lutcn == 3 || lutcn == 4) && elemSize1 == 1) + { + ParallelLoopBody* p = new ipp::IppLUTParallelBody_LUTCN(src, lut, dst, &ok); + body.reset(p); + } + + if (body != NULL && ok) + { + Range all(0, dst.rows); + if (dst.total()>>18) + parallel_for_(all, *body, (double)std::max((size_t)1, dst.total()>>16)); + else + (*body)(all); + if (ok) + return true; + } + } + return false; +} +#endif +} + + void cv::LUT( InputArray _src, InputArray _lut, OutputArray _dst ) { int cn = _src.channels(), depth = _src.depth(); @@ -5919,6 +5952,10 @@ void cv::LUT( InputArray _src, InputArray _lut, OutputArray _dst ) CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2, ocl_LUT(_src, _lut, _dst)) + CV_IPP_RUN((_src.dims() <= 2 && ((lutcn == 1 || lutcn == 3 || lutcn == 4) && CV_ELEM_SIZE1(_dst.depth()) == 1) && lutcn != 1), //lutcn == 1 ipp implementation switched off + ipp_lut(_src, _lut, _dst)); + + Mat src = _src.getMat(), lut = _lut.getMat(); _dst.create(src.dims, src.size, CV_MAKETYPE(_lut.depth(), cn)); Mat dst = _dst.getMat(); @@ -5927,25 +5964,6 @@ void cv::LUT( InputArray _src, InputArray _lut, OutputArray _dst ) { bool ok = false; Ptr body; -#if defined(HAVE_IPP) - CV_IPP_CHECK() - { - size_t elemSize1 = CV_ELEM_SIZE1(dst.depth()); -#if 0 // there are no performance benefits (PR #2653) - if (lutcn == 1) - { - ParallelLoopBody* p = new ipp::IppLUTParallelBody_LUTC1(src, lut, dst, &ok); - body.reset(p); - } - else -#endif - if ((lutcn == 3 || lutcn == 4) && elemSize1 == 1) - { - ParallelLoopBody* p = new ipp::IppLUTParallelBody_LUTCN(src, lut, dst, &ok); - body.reset(p); - } - } -#endif if (body == NULL || ok == false) { ok = false; diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index aa6eb2fb5a..ba09b54e6d 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -82,17 +82,7 @@ copyMask_(const uchar* _src, size_t sstep, const uchar* mask, size_t mstep, ucha template<> void copyMask_(const uchar* _src, size_t sstep, const uchar* mask, size_t mstep, uchar* _dst, size_t dstep, Size size) { -#if defined HAVE_IPP - CV_IPP_CHECK() - { - if (ippiCopy_8u_C1MR(_src, (int)sstep, _dst, (int)dstep, ippiSize(size), mask, (int)mstep) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } -#endif + CV_IPP_RUN(true, ippiCopy_8u_C1MR(_src, (int)sstep, _dst, (int)dstep, ippiSize(size), mask, (int)mstep) >= 0) for( ; size.height--; mask += mstep, _src += sstep, _dst += dstep ) { @@ -132,17 +122,7 @@ copyMask_(const uchar* _src, size_t sstep, const uchar* mask, size_t mste template<> void copyMask_(const uchar* _src, size_t sstep, const uchar* mask, size_t mstep, uchar* _dst, size_t dstep, Size size) { -#if defined HAVE_IPP - CV_IPP_CHECK() - { - if (ippiCopy_16u_C1MR((const Ipp16u *)_src, (int)sstep, (Ipp16u *)_dst, (int)dstep, ippiSize(size), mask, (int)mstep) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } -#endif + CV_IPP_RUN(true, ippiCopy_16u_C1MR((const Ipp16u *)_src, (int)sstep, (Ipp16u *)_dst, (int)dstep, ippiSize(size), mask, (int)mstep) >= 0) for( ; size.height--; mask += mstep, _src += sstep, _dst += dstep ) { @@ -214,15 +194,7 @@ static void copyMask##suffix(const uchar* src, size_t sstep, const uchar* mask, static void copyMask##suffix(const uchar* src, size_t sstep, const uchar* mask, size_t mstep, \ uchar* dst, size_t dstep, Size size, void*) \ { \ - CV_IPP_CHECK()\ - {\ - if (ippiCopy_##ippfavor((const ipptype *)src, (int)sstep, (ipptype *)dst, (int)dstep, ippiSize(size), (const Ipp8u *)mask, (int)mstep) >= 0) \ - {\ - CV_IMPL_ADD(CV_IMPL_IPP);\ - return;\ - }\ - setIppErrorStatus(); \ - }\ + CV_IPP_RUN(true, ippiCopy_##ippfavor((const ipptype *)src, (int)sstep, (ipptype *)dst, (int)dstep, ippiSize(size), (const Ipp8u *)mask, (int)mstep) >= 0)\ copyMask_(src, sstep, mask, mstep, dst, dstep, size); \ } #else @@ -319,17 +291,7 @@ void Mat::copyTo( OutputArray _dst ) const Size sz = getContinuousSize(*this, dst); size_t len = sz.width*elemSize(); -#if defined HAVE_IPP - CV_IPP_CHECK() - { - if (ippiCopy_8u_C1R(sptr, (int)step, dptr, (int)dst.step, ippiSize((int)len, sz.height)) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP) - return; - } - setIppErrorStatus(); - } -#endif + CV_IPP_RUN(true, ippiCopy_8u_C1R(sptr, (int)step, dptr, (int)dst.step, ippiSize((int)len, sz.height)) >= 0) for( ; sz.height--; sptr += step, dptr += dst.step ) memcpy( dptr, sptr, len ); @@ -461,6 +423,87 @@ Mat& Mat::operator = (const Scalar& s) return *this; } +#if defined HAVE_IPP +static bool ipp_Mat_setTo(Mat *src, InputArray _value, InputArray _mask) +{ + Mat value = _value.getMat(), mask = _mask.getMat(); + int cn = src->channels(), depth0 = src->depth(); + + if (!mask.empty() && (src->dims <= 2 || (src->isContinuous() && mask.isContinuous())) && + (/*depth0 == CV_8U ||*/ depth0 == CV_16U || depth0 == CV_16S || depth0 == CV_32S || depth0 == CV_32F) && + (cn == 1 || cn == 3 || cn == 4)) + { + uchar _buf[32]; + void * buf = _buf; + convertAndUnrollScalar( value, src->type(), _buf, 1 ); + + IppStatus status = (IppStatus)-1; + IppiSize roisize = { src->cols, src->rows }; + int mstep = (int)mask.step[0], dstep = (int)src->step[0]; + + if (src->isContinuous() && mask.isContinuous()) + { + roisize.width = (int)src->total(); + roisize.height = 1; + } + + if (cn == 1) + { + /*if (depth0 == CV_8U) + status = ippiSet_8u_C1MR(*(Ipp8u *)buf, (Ipp8u *)data, dstep, roisize, mask.data, mstep); + else*/ if (depth0 == CV_16U) + status = ippiSet_16u_C1MR(*(Ipp16u *)buf, (Ipp16u *)src->data, dstep, roisize, mask.data, mstep); + else if (depth0 == CV_16S) + status = ippiSet_16s_C1MR(*(Ipp16s *)buf, (Ipp16s *)src->data, dstep, roisize, mask.data, mstep); + else if (depth0 == CV_32S) + status = ippiSet_32s_C1MR(*(Ipp32s *)buf, (Ipp32s *)src->data, dstep, roisize, mask.data, mstep); + else if (depth0 == CV_32F) + status = ippiSet_32f_C1MR(*(Ipp32f *)buf, (Ipp32f *)src->data, dstep, roisize, mask.data, mstep); + } + else if (cn == 3 || cn == 4) + { + +#define IPP_SET(ippfavor, ippcn) \ + do \ + { \ + typedef Ipp##ippfavor ipptype; \ + ipptype ippvalue[4] = { ((ipptype *)buf)[0], ((ipptype *)buf)[1], ((ipptype *)buf)[2], ((ipptype *)buf)[3] }; \ + status = ippiSet_##ippfavor##_C##ippcn##MR(ippvalue, (ipptype *)src->data, dstep, roisize, mask.data, mstep); \ + } while ((void)0, 0) + +#define IPP_SET_CN(ippcn) \ + do \ + { \ + if (cn == ippcn) \ + { \ + /*if (depth0 == CV_8U) \ + IPP_SET(8u, ippcn); \ + else*/ if (depth0 == CV_16U) \ + IPP_SET(16u, ippcn); \ + else if (depth0 == CV_16S) \ + IPP_SET(16s, ippcn); \ + else if (depth0 == CV_32S) \ + IPP_SET(32s, ippcn); \ + else if (depth0 == CV_32F) \ + IPP_SET(32f, ippcn); \ + } \ + } while ((void)0, 0) + + IPP_SET_CN(3); + IPP_SET_CN(4); + +#undef IPP_SET_CN +#undef IPP_SET + } + + if (status >= 0) + return true; + } + + return false; +} +#endif + Mat& Mat::setTo(InputArray _value, InputArray _mask) { @@ -472,86 +515,8 @@ Mat& Mat::setTo(InputArray _value, InputArray _mask) CV_Assert( checkScalar(value, type(), _value.kind(), _InputArray::MAT )); CV_Assert( mask.empty() || (mask.type() == CV_8U && size == mask.size) ); -#if defined HAVE_IPP - CV_IPP_CHECK() - { - int cn = channels(), depth0 = depth(); + CV_IPP_RUN(true, ipp_Mat_setTo((cv::Mat*)this, _value, _mask), *this) - if (!mask.empty() && (dims <= 2 || (isContinuous() && mask.isContinuous())) && - (/*depth0 == CV_8U ||*/ depth0 == CV_16U || depth0 == CV_16S || depth0 == CV_32S || depth0 == CV_32F) && - (cn == 1 || cn == 3 || cn == 4)) - { - uchar _buf[32]; - void * buf = _buf; - convertAndUnrollScalar( value, type(), _buf, 1 ); - - IppStatus status = (IppStatus)-1; - IppiSize roisize = { cols, rows }; - int mstep = (int)mask.step[0], dstep = (int)step[0]; - - if (isContinuous() && mask.isContinuous()) - { - roisize.width = (int)total(); - roisize.height = 1; - } - - if (cn == 1) - { - /*if (depth0 == CV_8U) - status = ippiSet_8u_C1MR(*(Ipp8u *)buf, (Ipp8u *)data, dstep, roisize, mask.data, mstep); - else*/ if (depth0 == CV_16U) - status = ippiSet_16u_C1MR(*(Ipp16u *)buf, (Ipp16u *)data, dstep, roisize, mask.data, mstep); - else if (depth0 == CV_16S) - status = ippiSet_16s_C1MR(*(Ipp16s *)buf, (Ipp16s *)data, dstep, roisize, mask.data, mstep); - else if (depth0 == CV_32S) - status = ippiSet_32s_C1MR(*(Ipp32s *)buf, (Ipp32s *)data, dstep, roisize, mask.data, mstep); - else if (depth0 == CV_32F) - status = ippiSet_32f_C1MR(*(Ipp32f *)buf, (Ipp32f *)data, dstep, roisize, mask.data, mstep); - } - else if (cn == 3 || cn == 4) - { -#define IPP_SET(ippfavor, ippcn) \ - do \ - { \ - typedef Ipp##ippfavor ipptype; \ - ipptype ippvalue[4] = { ((ipptype *)buf)[0], ((ipptype *)buf)[1], ((ipptype *)buf)[2], ((ipptype *)buf)[3] }; \ - status = ippiSet_##ippfavor##_C##ippcn##MR(ippvalue, (ipptype *)data, dstep, roisize, mask.data, mstep); \ - } while ((void)0, 0) - -#define IPP_SET_CN(ippcn) \ - do \ - { \ - if (cn == ippcn) \ - { \ - /*if (depth0 == CV_8U) \ - IPP_SET(8u, ippcn); \ - else*/ if (depth0 == CV_16U) \ - IPP_SET(16u, ippcn); \ - else if (depth0 == CV_16S) \ - IPP_SET(16s, ippcn); \ - else if (depth0 == CV_32S) \ - IPP_SET(32s, ippcn); \ - else if (depth0 == CV_32F) \ - IPP_SET(32f, ippcn); \ - } \ - } while ((void)0, 0) - - IPP_SET_CN(3); - IPP_SET_CN(4); - -#undef IPP_SET_CN -#undef IPP_SET - } - - if (status >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return *this; - } - setIppErrorStatus(); - } - } -#endif size_t esz = elemSize(); BinaryFunc copymask = getCopyMaskFunc(esz); @@ -725,6 +690,80 @@ static bool ocl_flip(InputArray _src, OutputArray _dst, int flipCode ) #endif +#if defined HAVE_IPP +static bool ipp_flip( InputArray _src, OutputArray _dst, int flip_mode ) +{ + Size size = _src.size(); + Mat src = _src.getMat(); + int type = src.type(); + _dst.create( size, type ); + Mat dst = _dst.getMat(); + + typedef IppStatus (CV_STDCALL * ippiMirror)(const void * pSrc, int srcStep, void * pDst, int dstStep, IppiSize roiSize, IppiAxis flip); + typedef IppStatus (CV_STDCALL * ippiMirrorI)(const void * pSrcDst, int srcDstStep, IppiSize roiSize, IppiAxis flip); + ippiMirror ippFunc = 0; + ippiMirrorI ippFuncI = 0; + + if (src.data == dst.data) + { + CV_SUPPRESS_DEPRECATED_START + ippFuncI = + type == CV_8UC1 ? (ippiMirrorI)ippiMirror_8u_C1IR : + type == CV_8UC3 ? (ippiMirrorI)ippiMirror_8u_C3IR : + type == CV_8UC4 ? (ippiMirrorI)ippiMirror_8u_C4IR : + type == CV_16UC1 ? (ippiMirrorI)ippiMirror_16u_C1IR : + type == CV_16UC3 ? (ippiMirrorI)ippiMirror_16u_C3IR : + type == CV_16UC4 ? (ippiMirrorI)ippiMirror_16u_C4IR : + type == CV_16SC1 ? (ippiMirrorI)ippiMirror_16s_C1IR : + type == CV_16SC3 ? (ippiMirrorI)ippiMirror_16s_C3IR : + type == CV_16SC4 ? (ippiMirrorI)ippiMirror_16s_C4IR : + type == CV_32SC1 ? (ippiMirrorI)ippiMirror_32s_C1IR : + type == CV_32SC3 ? (ippiMirrorI)ippiMirror_32s_C3IR : + type == CV_32SC4 ? (ippiMirrorI)ippiMirror_32s_C4IR : + type == CV_32FC1 ? (ippiMirrorI)ippiMirror_32f_C1IR : + type == CV_32FC3 ? (ippiMirrorI)ippiMirror_32f_C3IR : + type == CV_32FC4 ? (ippiMirrorI)ippiMirror_32f_C4IR : 0; + CV_SUPPRESS_DEPRECATED_END + } + else + { + ippFunc = + type == CV_8UC1 ? (ippiMirror)ippiMirror_8u_C1R : + type == CV_8UC3 ? (ippiMirror)ippiMirror_8u_C3R : + type == CV_8UC4 ? (ippiMirror)ippiMirror_8u_C4R : + type == CV_16UC1 ? (ippiMirror)ippiMirror_16u_C1R : + type == CV_16UC3 ? (ippiMirror)ippiMirror_16u_C3R : + type == CV_16UC4 ? (ippiMirror)ippiMirror_16u_C4R : + type == CV_16SC1 ? (ippiMirror)ippiMirror_16s_C1R : + type == CV_16SC3 ? (ippiMirror)ippiMirror_16s_C3R : + type == CV_16SC4 ? (ippiMirror)ippiMirror_16s_C4R : + type == CV_32SC1 ? (ippiMirror)ippiMirror_32s_C1R : + type == CV_32SC3 ? (ippiMirror)ippiMirror_32s_C3R : + type == CV_32SC4 ? (ippiMirror)ippiMirror_32s_C4R : + type == CV_32FC1 ? (ippiMirror)ippiMirror_32f_C1R : + type == CV_32FC3 ? (ippiMirror)ippiMirror_32f_C3R : + type == CV_32FC4 ? (ippiMirror)ippiMirror_32f_C4R : 0; + } + IppiAxis axis = flip_mode == 0 ? ippAxsHorizontal : + flip_mode > 0 ? ippAxsVertical : ippAxsBoth; + IppiSize roisize = { dst.cols, dst.rows }; + + if (ippFunc != 0) + { + if (ippFunc(src.ptr(), (int)src.step, dst.ptr(), (int)dst.step, ippiSize(src.cols, src.rows), axis) >= 0) + return true; + } + else if (ippFuncI != 0) + { + if (ippFuncI(dst.ptr(), (int)dst.step, roisize, axis) >= 0) + return true; + } + + return false; +} +#endif + + void flip( InputArray _src, OutputArray _dst, int flip_mode ) { CV_Assert( _src.dims() <= 2 ); @@ -747,85 +786,15 @@ void flip( InputArray _src, OutputArray _dst, int flip_mode ) CV_OCL_RUN( _dst.isUMat(), ocl_flip(_src, _dst, flip_mode)) + CV_IPP_RUN(true, ipp_flip(_src, _dst, flip_mode)); + + Mat src = _src.getMat(); int type = src.type(); _dst.create( size, type ); Mat dst = _dst.getMat(); size_t esz = CV_ELEM_SIZE(type); -#if defined HAVE_IPP - CV_IPP_CHECK() - { - typedef IppStatus (CV_STDCALL * ippiMirror)(const void * pSrc, int srcStep, void * pDst, int dstStep, IppiSize roiSize, IppiAxis flip); - typedef IppStatus (CV_STDCALL * ippiMirrorI)(const void * pSrcDst, int srcDstStep, IppiSize roiSize, IppiAxis flip); - ippiMirror ippFunc = 0; - ippiMirrorI ippFuncI = 0; - - if (src.data == dst.data) - { - CV_SUPPRESS_DEPRECATED_START - ippFuncI = - type == CV_8UC1 ? (ippiMirrorI)ippiMirror_8u_C1IR : - type == CV_8UC3 ? (ippiMirrorI)ippiMirror_8u_C3IR : - type == CV_8UC4 ? (ippiMirrorI)ippiMirror_8u_C4IR : - type == CV_16UC1 ? (ippiMirrorI)ippiMirror_16u_C1IR : - type == CV_16UC3 ? (ippiMirrorI)ippiMirror_16u_C3IR : - type == CV_16UC4 ? (ippiMirrorI)ippiMirror_16u_C4IR : - type == CV_16SC1 ? (ippiMirrorI)ippiMirror_16s_C1IR : - type == CV_16SC3 ? (ippiMirrorI)ippiMirror_16s_C3IR : - type == CV_16SC4 ? (ippiMirrorI)ippiMirror_16s_C4IR : - type == CV_32SC1 ? (ippiMirrorI)ippiMirror_32s_C1IR : - type == CV_32SC3 ? (ippiMirrorI)ippiMirror_32s_C3IR : - type == CV_32SC4 ? (ippiMirrorI)ippiMirror_32s_C4IR : - type == CV_32FC1 ? (ippiMirrorI)ippiMirror_32f_C1IR : - type == CV_32FC3 ? (ippiMirrorI)ippiMirror_32f_C3IR : - type == CV_32FC4 ? (ippiMirrorI)ippiMirror_32f_C4IR : 0; - CV_SUPPRESS_DEPRECATED_END - } - else - { - ippFunc = - type == CV_8UC1 ? (ippiMirror)ippiMirror_8u_C1R : - type == CV_8UC3 ? (ippiMirror)ippiMirror_8u_C3R : - type == CV_8UC4 ? (ippiMirror)ippiMirror_8u_C4R : - type == CV_16UC1 ? (ippiMirror)ippiMirror_16u_C1R : - type == CV_16UC3 ? (ippiMirror)ippiMirror_16u_C3R : - type == CV_16UC4 ? (ippiMirror)ippiMirror_16u_C4R : - type == CV_16SC1 ? (ippiMirror)ippiMirror_16s_C1R : - type == CV_16SC3 ? (ippiMirror)ippiMirror_16s_C3R : - type == CV_16SC4 ? (ippiMirror)ippiMirror_16s_C4R : - type == CV_32SC1 ? (ippiMirror)ippiMirror_32s_C1R : - type == CV_32SC3 ? (ippiMirror)ippiMirror_32s_C3R : - type == CV_32SC4 ? (ippiMirror)ippiMirror_32s_C4R : - type == CV_32FC1 ? (ippiMirror)ippiMirror_32f_C1R : - type == CV_32FC3 ? (ippiMirror)ippiMirror_32f_C3R : - type == CV_32FC4 ? (ippiMirror)ippiMirror_32f_C4R : 0; - } - IppiAxis axis = flip_mode == 0 ? ippAxsHorizontal : - flip_mode > 0 ? ippAxsVertical : ippAxsBoth; - IppiSize roisize = { dst.cols, dst.rows }; - - if (ippFunc != 0) - { - if (ippFunc(src.ptr(), (int)src.step, dst.ptr(), (int)dst.step, ippiSize(src.cols, src.rows), axis) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - else if (ippFuncI != 0) - { - if (ippFuncI(dst.ptr(), (int)dst.step, roisize, axis) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - } -#endif - if( flip_mode <= 0 ) flipVert( src.ptr(), src.step, dst.ptr(), dst.step, src.size(), esz ); else diff --git a/modules/core/src/matrix.cpp b/modules/core/src/matrix.cpp index b273c8a7d8..ea82d68f1c 100644 --- a/modules/core/src/matrix.cpp +++ b/modules/core/src/matrix.cpp @@ -3088,7 +3088,77 @@ static bool ocl_transpose( InputArray _src, OutputArray _dst ) #endif + +#ifdef HAVE_IPP +static bool ipp_transpose( InputArray _src, OutputArray _dst ) +{ + int type = _src.type(); + typedef IppStatus (CV_STDCALL * ippiTranspose)(const void * pSrc, int srcStep, void * pDst, int dstStep, IppiSize roiSize); + typedef IppStatus (CV_STDCALL * ippiTransposeI)(const void * pSrcDst, int srcDstStep, IppiSize roiSize); + ippiTranspose ippFunc = 0; + ippiTransposeI ippFuncI = 0; + + Mat dst = _dst.getMat(); + Mat src = _src.getMat(); + + if (dst.data == src.data && dst.cols == dst.rows) + { + CV_SUPPRESS_DEPRECATED_START + ippFuncI = + type == CV_8UC1 ? (ippiTransposeI)ippiTranspose_8u_C1IR : + type == CV_8UC3 ? (ippiTransposeI)ippiTranspose_8u_C3IR : + type == CV_8UC4 ? (ippiTransposeI)ippiTranspose_8u_C4IR : + type == CV_16UC1 ? (ippiTransposeI)ippiTranspose_16u_C1IR : + type == CV_16UC3 ? (ippiTransposeI)ippiTranspose_16u_C3IR : + type == CV_16UC4 ? (ippiTransposeI)ippiTranspose_16u_C4IR : + type == CV_16SC1 ? (ippiTransposeI)ippiTranspose_16s_C1IR : + type == CV_16SC3 ? (ippiTransposeI)ippiTranspose_16s_C3IR : + type == CV_16SC4 ? (ippiTransposeI)ippiTranspose_16s_C4IR : + type == CV_32SC1 ? (ippiTransposeI)ippiTranspose_32s_C1IR : + type == CV_32SC3 ? (ippiTransposeI)ippiTranspose_32s_C3IR : + type == CV_32SC4 ? (ippiTransposeI)ippiTranspose_32s_C4IR : + type == CV_32FC1 ? (ippiTransposeI)ippiTranspose_32f_C1IR : + type == CV_32FC3 ? (ippiTransposeI)ippiTranspose_32f_C3IR : + type == CV_32FC4 ? (ippiTransposeI)ippiTranspose_32f_C4IR : 0; + CV_SUPPRESS_DEPRECATED_END + } + else + { + ippFunc = + type == CV_8UC1 ? (ippiTranspose)ippiTranspose_8u_C1R : + type == CV_8UC3 ? (ippiTranspose)ippiTranspose_8u_C3R : + type == CV_8UC4 ? (ippiTranspose)ippiTranspose_8u_C4R : + type == CV_16UC1 ? (ippiTranspose)ippiTranspose_16u_C1R : + type == CV_16UC3 ? (ippiTranspose)ippiTranspose_16u_C3R : + type == CV_16UC4 ? (ippiTranspose)ippiTranspose_16u_C4R : + type == CV_16SC1 ? (ippiTranspose)ippiTranspose_16s_C1R : + type == CV_16SC3 ? (ippiTranspose)ippiTranspose_16s_C3R : + type == CV_16SC4 ? (ippiTranspose)ippiTranspose_16s_C4R : + type == CV_32SC1 ? (ippiTranspose)ippiTranspose_32s_C1R : + type == CV_32SC3 ? (ippiTranspose)ippiTranspose_32s_C3R : + type == CV_32SC4 ? (ippiTranspose)ippiTranspose_32s_C4R : + type == CV_32FC1 ? (ippiTranspose)ippiTranspose_32f_C1R : + type == CV_32FC3 ? (ippiTranspose)ippiTranspose_32f_C3R : + type == CV_32FC4 ? (ippiTranspose)ippiTranspose_32f_C4R : 0; + } + + IppiSize roiSize = { src.cols, src.rows }; + if (ippFunc != 0) + { + if (ippFunc(src.ptr(), (int)src.step, dst.ptr(), (int)dst.step, roiSize) >= 0) + return true; + } + else if (ippFuncI != 0) + { + if (ippFuncI(dst.ptr(), (int)dst.step, roiSize) >= 0) + return true; + } + return false; } +#endif + +} + void cv::transpose( InputArray _src, OutputArray _dst ) { @@ -3116,76 +3186,8 @@ void cv::transpose( InputArray _src, OutputArray _dst ) return; } -#if defined HAVE_IPP - CV_IPP_CHECK() - { - typedef IppStatus (CV_STDCALL * ippiTranspose)(const void * pSrc, int srcStep, void * pDst, int dstStep, IppiSize roiSize); - typedef IppStatus (CV_STDCALL * ippiTransposeI)(const void * pSrcDst, int srcDstStep, IppiSize roiSize); - ippiTranspose ippFunc = 0; - ippiTransposeI ippFuncI = 0; + CV_IPP_RUN(true, ipp_transpose(_src, _dst)) - if (dst.data == src.data && dst.cols == dst.rows) - { - CV_SUPPRESS_DEPRECATED_START - ippFuncI = - type == CV_8UC1 ? (ippiTransposeI)ippiTranspose_8u_C1IR : - type == CV_8UC3 ? (ippiTransposeI)ippiTranspose_8u_C3IR : - type == CV_8UC4 ? (ippiTransposeI)ippiTranspose_8u_C4IR : - type == CV_16UC1 ? (ippiTransposeI)ippiTranspose_16u_C1IR : - type == CV_16UC3 ? (ippiTransposeI)ippiTranspose_16u_C3IR : - type == CV_16UC4 ? (ippiTransposeI)ippiTranspose_16u_C4IR : - type == CV_16SC1 ? (ippiTransposeI)ippiTranspose_16s_C1IR : - type == CV_16SC3 ? (ippiTransposeI)ippiTranspose_16s_C3IR : - type == CV_16SC4 ? (ippiTransposeI)ippiTranspose_16s_C4IR : - type == CV_32SC1 ? (ippiTransposeI)ippiTranspose_32s_C1IR : - type == CV_32SC3 ? (ippiTransposeI)ippiTranspose_32s_C3IR : - type == CV_32SC4 ? (ippiTransposeI)ippiTranspose_32s_C4IR : - type == CV_32FC1 ? (ippiTransposeI)ippiTranspose_32f_C1IR : - type == CV_32FC3 ? (ippiTransposeI)ippiTranspose_32f_C3IR : - type == CV_32FC4 ? (ippiTransposeI)ippiTranspose_32f_C4IR : 0; - CV_SUPPRESS_DEPRECATED_END - } - else - { - ippFunc = - type == CV_8UC1 ? (ippiTranspose)ippiTranspose_8u_C1R : - type == CV_8UC3 ? (ippiTranspose)ippiTranspose_8u_C3R : - type == CV_8UC4 ? (ippiTranspose)ippiTranspose_8u_C4R : - type == CV_16UC1 ? (ippiTranspose)ippiTranspose_16u_C1R : - type == CV_16UC3 ? (ippiTranspose)ippiTranspose_16u_C3R : - type == CV_16UC4 ? (ippiTranspose)ippiTranspose_16u_C4R : - type == CV_16SC1 ? (ippiTranspose)ippiTranspose_16s_C1R : - type == CV_16SC3 ? (ippiTranspose)ippiTranspose_16s_C3R : - type == CV_16SC4 ? (ippiTranspose)ippiTranspose_16s_C4R : - type == CV_32SC1 ? (ippiTranspose)ippiTranspose_32s_C1R : - type == CV_32SC3 ? (ippiTranspose)ippiTranspose_32s_C3R : - type == CV_32SC4 ? (ippiTranspose)ippiTranspose_32s_C4R : - type == CV_32FC1 ? (ippiTranspose)ippiTranspose_32f_C1R : - type == CV_32FC3 ? (ippiTranspose)ippiTranspose_32f_C3R : - type == CV_32FC4 ? (ippiTranspose)ippiTranspose_32f_C4R : 0; - } - - IppiSize roiSize = { src.cols, src.rows }; - if (ippFunc != 0) - { - if (ippFunc(src.ptr(), (int)src.step, dst.ptr(), (int)dst.step, roiSize) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - else if (ippFuncI != 0) - { - if (ippFuncI(dst.ptr(), (int)dst.step, roiSize) >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - } -#endif if( dst.data == src.data ) { diff --git a/modules/core/src/precomp.hpp b/modules/core/src/precomp.hpp index 88b60e4713..81b9674c75 100644 --- a/modules/core/src/precomp.hpp +++ b/modules/core/src/precomp.hpp @@ -72,6 +72,7 @@ #define GET_OPTIMIZED(func) (func) #endif + namespace cv { diff --git a/modules/core/src/stat.cpp b/modules/core/src/stat.cpp index 81f9a2484e..811fa2cca6 100644 --- a/modules/core/src/stat.cpp +++ b/modules/core/src/stat.cpp @@ -1140,64 +1140,79 @@ static bool ocl_sum( InputArray _src, Scalar & res, int sum_op, InputArray _mask } +#if defined (HAVE_IPP) +namespace cv +{ + static bool ipp_sum(Mat src, Scalar & _res) +{ +#if (IPP_VERSION_MAJOR >= 7) + int cn = src.channels(); + size_t total_size = src.total(); + int rows = src.size[0], cols = rows ? (int)(total_size / rows) : 0; + if (src.dims == 2 || (src.isContinuous() && cols > 0 && (size_t)rows*cols == total_size)) + { + IppiSize sz = { cols, rows }; + int type = src.type(); + typedef IppStatus(CV_STDCALL* ippiSumFuncHint)(const void*, int, IppiSize, double *, IppHintAlgorithm); + typedef IppStatus(CV_STDCALL* ippiSumFuncNoHint)(const void*, int, IppiSize, double *); + ippiSumFuncHint ippFuncHint = + type == CV_32FC1 ? (ippiSumFuncHint)ippiSum_32f_C1R : + type == CV_32FC3 ? (ippiSumFuncHint)ippiSum_32f_C3R : + type == CV_32FC4 ? (ippiSumFuncHint)ippiSum_32f_C4R : + 0; + ippiSumFuncNoHint ippFuncNoHint = + type == CV_8UC1 ? (ippiSumFuncNoHint)ippiSum_8u_C1R : + type == CV_8UC3 ? (ippiSumFuncNoHint)ippiSum_8u_C3R : + type == CV_8UC4 ? (ippiSumFuncNoHint)ippiSum_8u_C4R : + type == CV_16UC1 ? (ippiSumFuncNoHint)ippiSum_16u_C1R : + type == CV_16UC3 ? (ippiSumFuncNoHint)ippiSum_16u_C3R : + type == CV_16UC4 ? (ippiSumFuncNoHint)ippiSum_16u_C4R : + type == CV_16SC1 ? (ippiSumFuncNoHint)ippiSum_16s_C1R : + type == CV_16SC3 ? (ippiSumFuncNoHint)ippiSum_16s_C3R : + type == CV_16SC4 ? (ippiSumFuncNoHint)ippiSum_16s_C4R : + 0; + CV_Assert(!ippFuncHint || !ippFuncNoHint); + if (ippFuncHint || ippFuncNoHint) + { + Ipp64f res[4]; + IppStatus ret = ippFuncHint ? ippFuncHint(src.ptr(), (int)src.step[0], sz, res, ippAlgHintAccurate) : + ippFuncNoHint(src.ptr(), (int)src.step[0], sz, res); + CV_Assert(cn <= 4); + cn = min(cn, 4); + if (ret >= 0) + { + for( int i = 0; i < cn; i++ ) + _res[i] = res[i]; + return true; + } + return false; + } + } +#endif + return false; +} +} +#endif + cv::Scalar cv::sum( InputArray _src ) { -#ifdef HAVE_OPENCL Scalar _res; +#ifdef HAVE_OPENCL CV_OCL_RUN_(OCL_PERFORMANCE_CHECK(_src.isUMat()) && _src.dims() <= 2, ocl_sum(_src, _res, OCL_OP_SUM), _res) #endif +#ifdef HAVE_IPP + size_t total_size = _src.total(); + int rows = _src.rows(); + int cols = rows ? (int)(total_size / rows) : 0; +#endif Mat src = _src.getMat(); + CV_IPP_RUN((_src.dims() == 2 || (_src.isContinuous() && cols > 0 && (size_t)rows*cols == total_size)) && IPP_VERSION_MAJOR >= 7, + ipp_sum(src, _res), _res); int k, cn = src.channels(), depth = src.depth(); -#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - CV_IPP_CHECK() - { - size_t total_size = src.total(); - int rows = src.size[0], cols = rows ? (int)(total_size/rows) : 0; - if( src.dims == 2 || (src.isContinuous() && cols > 0 && (size_t)rows*cols == total_size) ) - { - IppiSize sz = { cols, rows }; - int type = src.type(); - typedef IppStatus (CV_STDCALL* ippiSumFuncHint)(const void*, int, IppiSize, double *, IppHintAlgorithm); - typedef IppStatus (CV_STDCALL* ippiSumFuncNoHint)(const void*, int, IppiSize, double *); - ippiSumFuncHint ippFuncHint = - type == CV_32FC1 ? (ippiSumFuncHint)ippiSum_32f_C1R : - type == CV_32FC3 ? (ippiSumFuncHint)ippiSum_32f_C3R : - type == CV_32FC4 ? (ippiSumFuncHint)ippiSum_32f_C4R : - 0; - ippiSumFuncNoHint ippFuncNoHint = - type == CV_8UC1 ? (ippiSumFuncNoHint)ippiSum_8u_C1R : - type == CV_8UC3 ? (ippiSumFuncNoHint)ippiSum_8u_C3R : - type == CV_8UC4 ? (ippiSumFuncNoHint)ippiSum_8u_C4R : - type == CV_16UC1 ? (ippiSumFuncNoHint)ippiSum_16u_C1R : - type == CV_16UC3 ? (ippiSumFuncNoHint)ippiSum_16u_C3R : - type == CV_16UC4 ? (ippiSumFuncNoHint)ippiSum_16u_C4R : - type == CV_16SC1 ? (ippiSumFuncNoHint)ippiSum_16s_C1R : - type == CV_16SC3 ? (ippiSumFuncNoHint)ippiSum_16s_C3R : - type == CV_16SC4 ? (ippiSumFuncNoHint)ippiSum_16s_C4R : - 0; - CV_Assert(!ippFuncHint || !ippFuncNoHint); - if( ippFuncHint || ippFuncNoHint ) - { - Ipp64f res[4]; - IppStatus ret = ippFuncHint ? ippFuncHint(src.ptr(), (int)src.step[0], sz, res, ippAlgHintAccurate) : - ippFuncNoHint(src.ptr(), (int)src.step[0], sz, res); - if( ret >= 0 ) - { - Scalar sc; - for( int i = 0; i < cn; i++ ) - sc[i] = res[i]; - CV_IMPL_ADD(CV_IMPL_IPP); - return sc; - } - setIppErrorStatus(); - } - } - } -#endif SumFunc func = getSumFunc(depth); CV_Assert( cn <= 4 && func != 0 ); @@ -1247,6 +1262,7 @@ cv::Scalar cv::sum( InputArray _src ) return s; } + #ifdef HAVE_OPENCL namespace cv { @@ -1291,51 +1307,61 @@ static bool ocl_countNonZero( InputArray _src, int & res ) #endif +#if defined HAVE_IPP +namespace cv { + +static bool ipp_countNonZero( Mat src, int & res ) +{ +#if !defined HAVE_IPP_ICV_ONLY + Ipp32s count = 0; + IppStatus status = ippStsNoErr; + + int type = src.type(), depth = CV_MAT_DEPTH(type); + IppiSize roiSize = { src.cols, src.rows }; + Ipp32s srcstep = (Ipp32s)src.step; + if (src.isContinuous()) + { + roiSize.width = (Ipp32s)src.total(); + roiSize.height = 1; + srcstep = (Ipp32s)src.total() * CV_ELEM_SIZE(type); + } + + if (depth == CV_8U) + status = ippiCountInRange_8u_C1R((const Ipp8u *)src.data, srcstep, roiSize, &count, 0, 0); + else if (depth == CV_32F) + status = ippiCountInRange_32f_C1R((const Ipp32f *)src.data, srcstep, roiSize, &count, 0, 0); + + if (status >= 0) + { + res = ((Ipp32s)src.total() - count); + return true; + } +#else + CV_UNUSED(src); CV_UNUSED(res); +#endif + return false; +} +} +#endif + + int cv::countNonZero( InputArray _src ) { int type = _src.type(), cn = CV_MAT_CN(type); CV_Assert( cn == 1 ); -#ifdef HAVE_OPENCL +#if defined HAVE_OPENCL || defined HAVE_IPP int res = -1; +#endif + +#ifdef HAVE_OPENCL CV_OCL_RUN_(OCL_PERFORMANCE_CHECK(_src.isUMat()) && _src.dims() <= 2, ocl_countNonZero(_src, res), res) #endif Mat src = _src.getMat(); - -#if defined HAVE_IPP && !defined HAVE_IPP_ICV_ONLY && 0 - CV_IPP_CHECK() - { - if (src.dims <= 2 || src.isContinuous()) - { - IppiSize roiSize = { src.cols, src.rows }; - Ipp32s count = 0, srcstep = (Ipp32s)src.step; - IppStatus status = (IppStatus)-1; - - if (src.isContinuous()) - { - roiSize.width = (Ipp32s)src.total(); - roiSize.height = 1; - srcstep = (Ipp32s)src.total() * CV_ELEM_SIZE(type); - } - - int depth = CV_MAT_DEPTH(type); - if (depth == CV_8U) - status = ippiCountInRange_8u_C1R((const Ipp8u *)src.data, srcstep, roiSize, &count, 0, 0); - else if (depth == CV_32F) - status = ippiCountInRange_32f_C1R((const Ipp32f *)src.data, srcstep, roiSize, &count, 0, 0); - - if (status >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return (Ipp32s)src.total() - count; - } - setIppErrorStatus(); - } - } -#endif + CV_IPP_RUN(0 && (_src.dims() <= 2 || _src.isContinuous()), ipp_countNonZero(src, res), res); CountNonZeroFunc func = getCountNonZeroTab(src.depth()); CV_Assert( func != 0 ); @@ -1618,6 +1644,116 @@ static bool ocl_meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv #endif +#if defined (HAVE_IPP) +namespace cv +{ +static bool ipp_meanStdDev(Mat& src, OutputArray _mean, OutputArray _sdv, Mat& mask) +{ +#if (IPP_VERSION_MAJOR >= 7) + int cn = src.channels(); + size_t total_size = src.total(); + int rows = src.size[0], cols = rows ? (int)(total_size / rows) : 0; + + Ipp64f mean_temp[3]; + Ipp64f stddev_temp[3]; + Ipp64f *pmean = &mean_temp[0]; + Ipp64f *pstddev = &stddev_temp[0]; + Mat mean, stddev; + int dcn_mean = -1; + if (_mean.needed()) + { + if (!_mean.fixedSize()) + _mean.create(cn, 1, CV_64F, -1, true); + mean = _mean.getMat(); + dcn_mean = (int)mean.total(); + pmean = mean.ptr(); + } + int dcn_stddev = -1; + if (_sdv.needed()) + { + if (!_sdv.fixedSize()) + _sdv.create(cn, 1, CV_64F, -1, true); + stddev = _sdv.getMat(); + dcn_stddev = (int)stddev.total(); + pstddev = stddev.ptr(); + } + for (int c = cn; c < dcn_mean; c++) + pmean[c] = 0; + for (int c = cn; c < dcn_stddev; c++) + pstddev[c] = 0; + IppiSize sz = { cols, rows }; + int type = src.type(); + + if (!mask.empty()) + { + typedef IppStatus(CV_STDCALL* ippiMaskMeanStdDevFuncC1)(const void *, int, const void *, int, IppiSize, Ipp64f *, Ipp64f *); + ippiMaskMeanStdDevFuncC1 ippFuncC1 = + type == CV_8UC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_8u_C1MR : + type == CV_16UC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_16u_C1MR : + type == CV_32FC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_32f_C1MR : + 0; + if (ippFuncC1) + { + if (ippFuncC1(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, pmean, pstddev) >= 0) + { + return true; + } + } + typedef IppStatus(CV_STDCALL* ippiMaskMeanStdDevFuncC3)(const void *, int, const void *, int, IppiSize, int, Ipp64f *, Ipp64f *); + ippiMaskMeanStdDevFuncC3 ippFuncC3 = + type == CV_8UC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_8u_C3CMR : + type == CV_16UC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_16u_C3CMR : + type == CV_32FC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_32f_C3CMR : + 0; + if (ippFuncC3) + { + if (ippFuncC3(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 1, &pmean[0], &pstddev[0]) >= 0 && + ippFuncC3(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 2, &pmean[1], &pstddev[1]) >= 0 && + ippFuncC3(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 3, &pmean[2], &pstddev[2]) >= 0) + { + return true; + } + } + } + else + { + typedef IppStatus(CV_STDCALL* ippiMeanStdDevFuncC1)(const void *, int, IppiSize, Ipp64f *, Ipp64f *); + ippiMeanStdDevFuncC1 ippFuncC1 = + type == CV_8UC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_8u_C1R : + type == CV_16UC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_16u_C1R : +#if (IPP_VERSION_X100 >= 801) + type == CV_32FC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_32f_C1R ://Aug 2013: bug in IPP 7.1, 8.0 +#endif + 0; + if (ippFuncC1) + { + if (ippFuncC1(src.ptr(), (int)src.step[0], sz, pmean, pstddev) >= 0) + { + return true; + } + } + typedef IppStatus(CV_STDCALL* ippiMeanStdDevFuncC3)(const void *, int, IppiSize, int, Ipp64f *, Ipp64f *); + ippiMeanStdDevFuncC3 ippFuncC3 = + type == CV_8UC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_8u_C3CR : + type == CV_16UC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_16u_C3CR : + type == CV_32FC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_32f_C3CR : + 0; + if (ippFuncC3) + { + if (ippFuncC3(src.ptr(), (int)src.step[0], sz, 1, &pmean[0], &pstddev[0]) >= 0 && + ippFuncC3(src.ptr(), (int)src.step[0], sz, 2, &pmean[1], &pstddev[1]) >= 0 && + ippFuncC3(src.ptr(), (int)src.step[0], sz, 3, &pmean[2], &pstddev[2]) >= 0) + { + return true; + } + } + } +#endif + return false; +} +} +#endif + void cv::meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv, InputArray _mask ) { CV_OCL_RUN(OCL_PERFORMANCE_CHECK(_src.isUMat()) && _src.dims() <= 2, @@ -1625,120 +1761,18 @@ void cv::meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv, Input Mat src = _src.getMat(), mask = _mask.getMat(); CV_Assert( mask.empty() || mask.type() == CV_8UC1 ); +#ifdef HAVE_IPP + Size sz = _src.dims() <= 2 ? _src.size() : Size(); + size_t total_size = _src.total(); + int rows = sz.height; + int cols = rows ? (int)(total_size / rows) : 0; +#endif + CV_IPP_RUN(IPP_VERSION_MAJOR >= 7 && (_src.dims() == 2 || (_src.isContinuous() && _mask.isContinuous() && cols > 0 && (size_t)rows*cols == total_size)), + ipp_meanStdDev(src, _mean, _sdv, mask)); + int k, cn = src.channels(), depth = src.depth(); -#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - CV_IPP_CHECK() - { - size_t total_size = src.total(); - int rows = src.size[0], cols = rows ? (int)(total_size/rows) : 0; - if( src.dims == 2 || (src.isContinuous() && mask.isContinuous() && cols > 0 && (size_t)rows*cols == total_size) ) - { - Ipp64f mean_temp[3]; - Ipp64f stddev_temp[3]; - Ipp64f *pmean = &mean_temp[0]; - Ipp64f *pstddev = &stddev_temp[0]; - Mat mean, stddev; - int dcn_mean = -1; - if( _mean.needed() ) - { - if( !_mean.fixedSize() ) - _mean.create(cn, 1, CV_64F, -1, true); - mean = _mean.getMat(); - dcn_mean = (int)mean.total(); - pmean = mean.ptr(); - } - int dcn_stddev = -1; - if( _sdv.needed() ) - { - if( !_sdv.fixedSize() ) - _sdv.create(cn, 1, CV_64F, -1, true); - stddev = _sdv.getMat(); - dcn_stddev = (int)stddev.total(); - pstddev = stddev.ptr(); - } - for( int c = cn; c < dcn_mean; c++ ) - pmean[c] = 0; - for( int c = cn; c < dcn_stddev; c++ ) - pstddev[c] = 0; - IppiSize sz = { cols, rows }; - int type = src.type(); - if( !mask.empty() ) - { - typedef IppStatus (CV_STDCALL* ippiMaskMeanStdDevFuncC1)(const void *, int, const void *, int, IppiSize, Ipp64f *, Ipp64f *); - ippiMaskMeanStdDevFuncC1 ippFuncC1 = - type == CV_8UC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_8u_C1MR : - type == CV_16UC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_16u_C1MR : - type == CV_32FC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_32f_C1MR : - 0; - if( ippFuncC1 ) - { - if( ippFuncC1(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, pmean, pstddev) >= 0 ) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - typedef IppStatus (CV_STDCALL* ippiMaskMeanStdDevFuncC3)(const void *, int, const void *, int, IppiSize, int, Ipp64f *, Ipp64f *); - ippiMaskMeanStdDevFuncC3 ippFuncC3 = - type == CV_8UC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_8u_C3CMR : - type == CV_16UC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_16u_C3CMR : - type == CV_32FC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_32f_C3CMR : - 0; - if( ippFuncC3 ) - { - if( ippFuncC3(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 1, &pmean[0], &pstddev[0]) >= 0 && - ippFuncC3(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 2, &pmean[1], &pstddev[1]) >= 0 && - ippFuncC3(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 3, &pmean[2], &pstddev[2]) >= 0 ) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - } - else - { - typedef IppStatus (CV_STDCALL* ippiMeanStdDevFuncC1)(const void *, int, IppiSize, Ipp64f *, Ipp64f *); - ippiMeanStdDevFuncC1 ippFuncC1 = - type == CV_8UC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_8u_C1R : - type == CV_16UC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_16u_C1R : -#if (IPP_VERSION_X100 >= 801) - type == CV_32FC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_32f_C1R ://Aug 2013: bug in IPP 7.1, 8.0 -#endif - 0; - if( ippFuncC1 ) - { - if( ippFuncC1(src.ptr(), (int)src.step[0], sz, pmean, pstddev) >= 0 ) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - typedef IppStatus (CV_STDCALL* ippiMeanStdDevFuncC3)(const void *, int, IppiSize, int, Ipp64f *, Ipp64f *); - ippiMeanStdDevFuncC3 ippFuncC3 = - type == CV_8UC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_8u_C3CR : - type == CV_16UC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_16u_C3CR : - type == CV_32FC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_32f_C3CR : - 0; - if( ippFuncC3 ) - { - if( ippFuncC3(src.ptr(), (int)src.step[0], sz, 1, &pmean[0], &pstddev[0]) >= 0 && - ippFuncC3(src.ptr(), (int)src.step[0], sz, 2, &pmean[1], &pstddev[1]) >= 0 && - ippFuncC3(src.ptr(), (int)src.step[0], sz, 3, &pmean[2], &pstddev[2]) >= 0 ) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - } - } - } -#endif SumSqrFunc func = getSumSqrTab(depth); @@ -1830,6 +1864,7 @@ void cv::meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv, Input } } + /****************************************************************************************\ * minMaxLoc * \****************************************************************************************/ @@ -2181,6 +2216,97 @@ static bool ocl_minMaxIdx( InputArray _src, double* minVal, double* maxVal, int* #endif +#if defined (HAVE_IPP) +static bool ipp_minMaxIdx( InputArray _src, double* minVal, double* maxVal, int* minIdx, int* maxIdx, InputArray _mask) +{ +#if (IPP_VERSION_MAJOR >= 7) + int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); + Mat src = _src.getMat(), mask = _mask.getMat(); + + size_t total_size = src.total(); + int rows = src.size[0], cols = rows ? (int)(total_size/rows) : 0; + IppiSize sz = { cols * cn, rows }; + + if( !mask.empty() ) + { + typedef IppStatus (CV_STDCALL* ippiMaskMinMaxIndxFuncC1)(const void *, int, const void *, int, + IppiSize, Ipp32f *, Ipp32f *, IppiPoint *, IppiPoint *); + + CV_SUPPRESS_DEPRECATED_START + ippiMaskMinMaxIndxFuncC1 ippFuncC1 = + type == CV_8UC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_8u_C1MR : + type == CV_8SC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_8s_C1MR : + type == CV_16UC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_16u_C1MR : + type == CV_32FC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_32f_C1MR : 0; + CV_SUPPRESS_DEPRECATED_END + + if( ippFuncC1 ) + { + Ipp32f min, max; + IppiPoint minp, maxp; + if( ippFuncC1(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, &min, &max, &minp, &maxp) >= 0 ) + { + if( minVal ) + *minVal = (double)min; + if( maxVal ) + *maxVal = (double)max; + if( !minp.x && !minp.y && !maxp.x && !maxp.y && !mask.ptr()[0] ) + minp.x = maxp.x = -1; + if( minIdx ) + { + size_t minidx = minp.y * cols + minp.x + 1; + ofs2idx(src, minidx, minIdx); + } + if( maxIdx ) + { + size_t maxidx = maxp.y * cols + maxp.x + 1; + ofs2idx(src, maxidx, maxIdx); + } + return true; + } + } + } + else + { + typedef IppStatus (CV_STDCALL* ippiMinMaxIndxFuncC1)(const void *, int, IppiSize, Ipp32f *, Ipp32f *, IppiPoint *, IppiPoint *); + + CV_SUPPRESS_DEPRECATED_START + ippiMinMaxIndxFuncC1 ippFuncC1 = + depth == CV_8U ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_8u_C1R : + depth == CV_8S ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_8s_C1R : + depth == CV_16U ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_16u_C1R : + depth == CV_32F ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_32f_C1R : 0; + CV_SUPPRESS_DEPRECATED_END + + if( ippFuncC1 ) + { + Ipp32f min, max; + IppiPoint minp, maxp; + if( ippFuncC1(src.ptr(), (int)src.step[0], sz, &min, &max, &minp, &maxp) >= 0 ) + { + if( minVal ) + *minVal = (double)min; + if( maxVal ) + *maxVal = (double)max; + if( minIdx ) + { + size_t minidx = minp.y * cols + minp.x + 1; + ofs2idx(src, minidx, minIdx); + } + if( maxIdx ) + { + size_t maxidx = maxp.y * cols + maxp.x + 1; + ofs2idx(src, maxidx, maxIdx); + } + return true; + } + } + } +#endif + return false; +} +#endif + } void cv::minMaxIdx(InputArray _src, double* minVal, @@ -2195,101 +2321,15 @@ void cv::minMaxIdx(InputArray _src, double* minVal, ocl_minMaxIdx(_src, minVal, maxVal, minIdx, maxIdx, _mask)) Mat src = _src.getMat(), mask = _mask.getMat(); - -#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - CV_IPP_CHECK() - { - size_t total_size = src.total(); - int rows = src.size[0], cols = rows ? (int)(total_size/rows) : 0; - if( src.dims == 2 || (src.isContinuous() && mask.isContinuous() && cols > 0 && (size_t)rows*cols == total_size) ) - { - IppiSize sz = { cols * cn, rows }; - - if( !mask.empty() ) - { - typedef IppStatus (CV_STDCALL* ippiMaskMinMaxIndxFuncC1)(const void *, int, const void *, int, - IppiSize, Ipp32f *, Ipp32f *, IppiPoint *, IppiPoint *); - - CV_SUPPRESS_DEPRECATED_START - ippiMaskMinMaxIndxFuncC1 ippFuncC1 = - type == CV_8UC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_8u_C1MR : - type == CV_8SC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_8s_C1MR : - type == CV_16UC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_16u_C1MR : - type == CV_32FC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_32f_C1MR : 0; - CV_SUPPRESS_DEPRECATED_END - - if( ippFuncC1 ) - { - Ipp32f min, max; - IppiPoint minp, maxp; - if( ippFuncC1(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, &min, &max, &minp, &maxp) >= 0 ) - { - if( minVal ) - *minVal = (double)min; - if( maxVal ) - *maxVal = (double)max; - if( !minp.x && !minp.y && !maxp.x && !maxp.y && !mask.ptr()[0] ) - minp.x = maxp.x = -1; - if( minIdx ) - { - size_t minidx = minp.y * cols + minp.x + 1; - ofs2idx(src, minidx, minIdx); - } - if( maxIdx ) - { - size_t maxidx = maxp.y * cols + maxp.x + 1; - ofs2idx(src, maxidx, maxIdx); - } - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - } - else - { - typedef IppStatus (CV_STDCALL* ippiMinMaxIndxFuncC1)(const void *, int, IppiSize, Ipp32f *, Ipp32f *, IppiPoint *, IppiPoint *); - - CV_SUPPRESS_DEPRECATED_START - ippiMinMaxIndxFuncC1 ippFuncC1 = - depth == CV_8U ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_8u_C1R : - depth == CV_8S ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_8s_C1R : - depth == CV_16U ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_16u_C1R : - #if !((defined _MSC_VER && defined _M_IX86) || defined __i386__) - depth == CV_32F ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_32f_C1R : - #endif - 0; - CV_SUPPRESS_DEPRECATED_END - - if( ippFuncC1 ) - { - Ipp32f min, max; - IppiPoint minp, maxp; - if( ippFuncC1(src.ptr(), (int)src.step[0], sz, &min, &max, &minp, &maxp) >= 0 ) - { - if( minVal ) - *minVal = (double)min; - if( maxVal ) - *maxVal = (double)max; - if( minIdx ) - { - size_t minidx = minp.y * cols + minp.x + 1; - ofs2idx(src, minidx, minIdx); - } - if( maxIdx ) - { - size_t maxidx = maxp.y * cols + maxp.x + 1; - ofs2idx(src, maxidx, maxIdx); - } - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - } - } - } +#ifdef HAVE_IPP + Size sz = _src.dims() <= 2 ? _src.size() : Size(); + size_t total_size = _src.total(); + int rows = sz.height; + int cols = rows ? (int)(total_size/rows) : 0; #endif + CV_IPP_RUN(IPP_VERSION_MAJOR >= 7 && (_src.dims() == 2 || (_src.isContinuous() && _mask.isContinuous() && cols > 0 && (size_t)rows*cols == total_size)), + ipp_minMaxIdx(src, minVal, maxVal, minIdx, maxIdx, mask)) + MinMaxIdxFunc func = getMinmaxTab(depth); CV_Assert( func != 0 ); @@ -2332,6 +2372,7 @@ void cv::minMaxIdx(InputArray _src, double* minVal, ofs2idx(src, maxidx, maxIdx); } + void cv::minMaxLoc( InputArray _img, double* minVal, double* maxVal, Point* minLoc, Point* maxLoc, InputArray mask ) { @@ -2613,111 +2654,96 @@ static bool ocl_norm( InputArray _src, int normType, InputArray _mask, double & } -double cv::norm( InputArray _src, int normType, InputArray _mask ) -{ - normType &= NORM_TYPE_MASK; - CV_Assert( normType == NORM_INF || normType == NORM_L1 || - normType == NORM_L2 || normType == NORM_L2SQR || - ((normType == NORM_HAMMING || normType == NORM_HAMMING2) && _src.type() == CV_8U) ); - -#ifdef HAVE_OPENCL - double _result = 0; - CV_OCL_RUN_(OCL_PERFORMANCE_CHECK(_src.isUMat()) && _src.dims() <= 2, - ocl_norm(_src, normType, _mask, _result), - _result) -#endif - - Mat src = _src.getMat(), mask = _mask.getMat(); - int depth = src.depth(), cn = src.channels(); - #if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - CV_IPP_CHECK() - { - size_t total_size = src.total(); - int rows = src.size[0], cols = rows ? (int)(total_size/rows) : 0; - if( (src.dims == 2 || (src.isContinuous() && mask.isContinuous())) - && cols > 0 && (size_t)rows*cols == total_size - && (normType == NORM_INF || normType == NORM_L1 || - normType == NORM_L2 || normType == NORM_L2SQR) ) - { - IppiSize sz = { cols, rows }; - int type = src.type(); - if( !mask.empty() ) +namespace cv { +static bool ipp_norm(InputArray _src, int normType, InputArray _mask, double & result) +{ + Mat src = _src.getMat(), mask = _mask.getMat(); + int cn = src.channels(); + size_t total_size = src.total(); + int rows = src.size[0], cols = rows ? (int)(total_size / rows) : 0; + if ((src.dims == 2 || (src.isContinuous() && mask.isContinuous())) + && cols > 0 && (size_t)rows*cols == total_size + && (normType == NORM_INF || normType == NORM_L1 || + normType == NORM_L2 || normType == NORM_L2SQR)) + { + IppiSize sz = { cols, rows }; + int type = src.type(); + if (!mask.empty()) { - typedef IppStatus (CV_STDCALL* ippiMaskNormFuncC1)(const void *, int, const void *, int, IppiSize, Ipp64f *); + typedef IppStatus(CV_STDCALL* ippiMaskNormFuncC1)(const void *, int, const void *, int, IppiSize, Ipp64f *); ippiMaskNormFuncC1 ippFuncC1 = normType == NORM_INF ? (type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_8u_C1MR : type == CV_8SC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_8s_C1MR : - // type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_16u_C1MR : + // type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_16u_C1MR : type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_32f_C1MR : 0) : - normType == NORM_L1 ? + normType == NORM_L1 ? (type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_8u_C1MR : type == CV_8SC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_8s_C1MR : type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_16u_C1MR : type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_32f_C1MR : 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? + normType == NORM_L2 || normType == NORM_L2SQR ? (type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_8u_C1MR : type == CV_8SC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_8s_C1MR : type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_16u_C1MR : type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_32f_C1MR : 0) : 0; - if( ippFuncC1 ) + if (ippFuncC1) { Ipp64f norm; - if( ippFuncC1(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, &norm) >= 0 ) + if (ippFuncC1(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, &norm) >= 0) { - CV_IMPL_ADD(CV_IMPL_IPP); - return normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm; + result = normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm; + return true; } - - setIppErrorStatus(); + return false; } /*typedef IppStatus (CV_STDCALL* ippiMaskNormFuncC3)(const void *, int, const void *, int, IppiSize, int, Ipp64f *); ippiMaskNormFuncC3 ippFuncC3 = - normType == NORM_INF ? - (type == CV_8UC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_8u_C3CMR : - type == CV_8SC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_8s_C3CMR : - type == CV_16UC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_16u_C3CMR : - type == CV_32FC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_32f_C3CMR : - 0) : + normType == NORM_INF ? + (type == CV_8UC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_8u_C3CMR : + type == CV_8SC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_8s_C3CMR : + type == CV_16UC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_16u_C3CMR : + type == CV_32FC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_32f_C3CMR : + 0) : normType == NORM_L1 ? - (type == CV_8UC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_8u_C3CMR : - type == CV_8SC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_8s_C3CMR : - type == CV_16UC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_16u_C3CMR : - type == CV_32FC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_32f_C3CMR : - 0) : + (type == CV_8UC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_8u_C3CMR : + type == CV_8SC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_8s_C3CMR : + type == CV_16UC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_16u_C3CMR : + type == CV_32FC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_32f_C3CMR : + 0) : normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_8UC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_8u_C3CMR : - type == CV_8SC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_8s_C3CMR : - type == CV_16UC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_16u_C3CMR : - type == CV_32FC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_32f_C3CMR : - 0) : 0; + (type == CV_8UC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_8u_C3CMR : + type == CV_8SC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_8s_C3CMR : + type == CV_16UC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_16u_C3CMR : + type == CV_32FC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_32f_C3CMR : + 0) : 0; if( ippFuncC3 ) { - Ipp64f norm1, norm2, norm3; - if( ippFuncC3(src.data, (int)src.step[0], mask.data, (int)mask.step[0], sz, 1, &norm1) >= 0 && - ippFuncC3(src.data, (int)src.step[0], mask.data, (int)mask.step[0], sz, 2, &norm2) >= 0 && - ippFuncC3(src.data, (int)src.step[0], mask.data, (int)mask.step[0], sz, 3, &norm3) >= 0) - { - Ipp64f norm = - normType == NORM_INF ? std::max(std::max(norm1, norm2), norm3) : - normType == NORM_L1 ? norm1 + norm2 + norm3 : - normType == NORM_L2 || normType == NORM_L2SQR ? std::sqrt(norm1 * norm1 + norm2 * norm2 + norm3 * norm3) : - 0; - CV_IMPL_ADD(CV_IMPL_IPP); - return normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm; - } - setIppErrorStatus(); + Ipp64f norm1, norm2, norm3; + if( ippFuncC3(src.data, (int)src.step[0], mask.data, (int)mask.step[0], sz, 1, &norm1) >= 0 && + ippFuncC3(src.data, (int)src.step[0], mask.data, (int)mask.step[0], sz, 2, &norm2) >= 0 && + ippFuncC3(src.data, (int)src.step[0], mask.data, (int)mask.step[0], sz, 3, &norm3) >= 0) + { + Ipp64f norm = + normType == NORM_INF ? std::max(std::max(norm1, norm2), norm3) : + normType == NORM_L1 ? norm1 + norm2 + norm3 : + normType == NORM_L2 || normType == NORM_L2SQR ? std::sqrt(norm1 * norm1 + norm2 * norm2 + norm3 * norm3) : + 0; + CV_IMPL_ADD(CV_IMPL_IPP); + return normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm; + } + setIppErrorStatus(); }*/ } else { - typedef IppStatus (CV_STDCALL* ippiNormFuncHint)(const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); - typedef IppStatus (CV_STDCALL* ippiNormFuncNoHint)(const void *, int, IppiSize, Ipp64f *); + typedef IppStatus(CV_STDCALL* ippiNormFuncHint)(const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); + typedef IppStatus(CV_STDCALL* ippiNormFuncNoHint)(const void *, int, IppiSize, Ipp64f *); ippiNormFuncHint ippFuncHint = normType == NORM_L1 ? (type == CV_32FC1 ? (ippiNormFuncHint)ippiNorm_L1_32f_C1R : @@ -2770,15 +2796,17 @@ double cv::norm( InputArray _src, int normType, InputArray _mask ) 0) : 0; // Make sure only zero or one version of the function pointer is valid CV_Assert(!ippFuncHint || !ippFuncNoHint); - if( ippFuncHint || ippFuncNoHint ) + if (ippFuncHint || ippFuncNoHint) { Ipp64f norm_array[4]; IppStatus ret = ippFuncHint ? ippFuncHint(src.ptr(), (int)src.step[0], sz, norm_array, ippAlgHintAccurate) : - ippFuncNoHint(src.ptr(), (int)src.step[0], sz, norm_array); - if( ret >= 0 ) + ippFuncNoHint(src.ptr(), (int)src.step[0], sz, norm_array); + if (ret >= 0) { Ipp64f norm = (normType == NORM_L2 || normType == NORM_L2SQR) ? norm_array[0] * norm_array[0] : norm_array[0]; - for( int i = 1; i < cn; i++ ) + CV_Assert(cn <= 4); + cn = min(cn, 4); + for (int i = 1; i < cn; i++) { norm = normType == NORM_INF ? std::max(norm, norm_array[i]) : @@ -2786,16 +2814,40 @@ double cv::norm( InputArray _src, int normType, InputArray _mask ) normType == NORM_L2 || normType == NORM_L2SQR ? norm + norm_array[i] * norm_array[i] : 0; } - CV_IMPL_ADD(CV_IMPL_IPP); - return normType == NORM_L2 ? (double)std::sqrt(norm) : (double)norm; + result = normType == NORM_L2 ? (double)std::sqrt(norm) : (double)norm; + return true; } - setIppErrorStatus(); + return false; } } } + return false; } +} #endif + +double cv::norm( InputArray _src, int normType, InputArray _mask ) +{ + normType &= NORM_TYPE_MASK; + CV_Assert( normType == NORM_INF || normType == NORM_L1 || + normType == NORM_L2 || normType == NORM_L2SQR || + ((normType == NORM_HAMMING || normType == NORM_HAMMING2) && _src.type() == CV_8U) ); + +#if defined HAVE_OPENCL || defined HAVE_IPP + double _result = 0; +#endif + +#ifdef HAVE_OPENCL + CV_OCL_RUN_(OCL_PERFORMANCE_CHECK(_src.isUMat()) && _src.dims() <= 2, + ocl_norm(_src, normType, _mask, _result), + _result) +#endif + + Mat src = _src.getMat(), mask = _mask.getMat(); + int depth = src.depth(), cn = src.channels(); + CV_IPP_RUN(true, ipp_norm(_src, normType, _mask, _result), _result); + if( src.isContinuous() && mask.empty() ) { size_t len = src.total()*cn; @@ -2992,151 +3044,129 @@ static bool ocl_norm( InputArray _src1, InputArray _src2, int normType, InputArr #endif -double cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _mask ) +namespace cv { - CV_Assert( _src1.sameSize(_src2) && _src1.type() == _src2.type() ); - -#ifdef HAVE_OPENCL - double _result = 0; - CV_OCL_RUN_(OCL_PERFORMANCE_CHECK(_src1.isUMat()), - ocl_norm(_src1, _src2, normType, _mask, _result), - _result) -#endif - - if( normType & CV_RELATIVE ) - { #if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - CV_IPP_CHECK() - { - Mat src1 = _src1.getMat(), src2 = _src2.getMat(), mask = _mask.getMat(); - - normType &= NORM_TYPE_MASK; - CV_Assert( normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 || normType == NORM_L2SQR || - ((normType == NORM_HAMMING || normType == NORM_HAMMING2) && src1.type() == CV_8U) ); - size_t total_size = src1.total(); - int rows = src1.size[0], cols = rows ? (int)(total_size/rows) : 0; - if( (src1.dims == 2 || (src1.isContinuous() && src2.isContinuous() && mask.isContinuous())) - && cols > 0 && (size_t)rows*cols == total_size - && (normType == NORM_INF || normType == NORM_L1 || - normType == NORM_L2 || normType == NORM_L2SQR) ) - { - IppiSize sz = { cols, rows }; - int type = src1.type(); - if( !mask.empty() ) - { - typedef IppStatus (CV_STDCALL* ippiMaskNormRelFuncC1)(const void *, int, const void *, int, const void *, int, IppiSize, Ipp64f *); - ippiMaskNormRelFuncC1 ippFuncC1 = - normType == NORM_INF ? - (type == CV_8UC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_Inf_8u_C1MR : -#ifndef __APPLE__ - type == CV_8SC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_Inf_8s_C1MR : -#endif - type == CV_16UC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_Inf_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_Inf_32f_C1MR : - 0) : - normType == NORM_L1 ? - (type == CV_8UC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L1_8u_C1MR : -#ifndef __APPLE__ - type == CV_8SC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L1_8s_C1MR : -#endif - type == CV_16UC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L1_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L1_32f_C1MR : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_8UC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L2_8u_C1MR : - type == CV_8SC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L2_8s_C1MR : - type == CV_16UC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L2_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L2_32f_C1MR : - 0) : 0; - if( ippFuncC1 ) - { - Ipp64f norm; - if( ippFuncC1(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], mask.ptr(), (int)mask.step[0], sz, &norm) >= 0 ) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm; - } - setIppErrorStatus(); - } - } - else - { - typedef IppStatus (CV_STDCALL* ippiNormRelFuncNoHint)(const void *, int, const void *, int, IppiSize, Ipp64f *); - typedef IppStatus (CV_STDCALL* ippiNormRelFuncHint)(const void *, int, const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); - ippiNormRelFuncNoHint ippFuncNoHint = - normType == NORM_INF ? - (type == CV_8UC1 ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_8u_C1R : - type == CV_16UC1 ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_16u_C1R : - type == CV_16SC1 ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_16s_C1R : - type == CV_32FC1 ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_32f_C1R : - 0) : - normType == NORM_L1 ? - (type == CV_8UC1 ? (ippiNormRelFuncNoHint)ippiNormRel_L1_8u_C1R : - type == CV_16UC1 ? (ippiNormRelFuncNoHint)ippiNormRel_L1_16u_C1R : - type == CV_16SC1 ? (ippiNormRelFuncNoHint)ippiNormRel_L1_16s_C1R : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_8UC1 ? (ippiNormRelFuncNoHint)ippiNormRel_L2_8u_C1R : - type == CV_16UC1 ? (ippiNormRelFuncNoHint)ippiNormRel_L2_16u_C1R : - type == CV_16SC1 ? (ippiNormRelFuncNoHint)ippiNormRel_L2_16s_C1R : - 0) : 0; - ippiNormRelFuncHint ippFuncHint = - normType == NORM_L1 ? - (type == CV_32FC1 ? (ippiNormRelFuncHint)ippiNormRel_L1_32f_C1R : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_32FC1 ? (ippiNormRelFuncHint)ippiNormRel_L2_32f_C1R : - 0) : 0; - if (ippFuncNoHint) - { - Ipp64f norm; - if( ippFuncNoHint(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, &norm) >= 0 ) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return (double)norm; - } - setIppErrorStatus(); - } - if (ippFuncHint) - { - Ipp64f norm; - if( ippFuncHint(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, &norm, ippAlgHintAccurate) >= 0 ) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return (double)norm; - } - setIppErrorStatus(); - } - } - } - } -#endif - return norm(_src1, _src2, normType & ~CV_RELATIVE, _mask)/(norm(_src2, normType, _mask) + DBL_EPSILON); - } - - Mat src1 = _src1.getMat(), src2 = _src2.getMat(), mask = _mask.getMat(); - int depth = src1.depth(), cn = src1.channels(); - - normType &= 7; - CV_Assert( normType == NORM_INF || normType == NORM_L1 || - normType == NORM_L2 || normType == NORM_L2SQR || - ((normType == NORM_HAMMING || normType == NORM_HAMMING2) && src1.type() == CV_8U) ); - -#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - CV_IPP_CHECK() + static bool ipp_norm_rel(InputArray _src1, InputArray _src2, int normType, InputArray _mask, double & result) { + Mat src1 = _src1.getMat(), src2 = _src2.getMat(), mask = _mask.getMat(); + + normType &= NORM_TYPE_MASK; + CV_Assert(normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 || normType == NORM_L2SQR || + ((normType == NORM_HAMMING || normType == NORM_HAMMING2) && src1.type() == CV_8U)); size_t total_size = src1.total(); - int rows = src1.size[0], cols = rows ? (int)(total_size/rows) : 0; - if( (src1.dims == 2 || (src1.isContinuous() && src2.isContinuous() && mask.isContinuous())) + int rows = src1.size[0], cols = rows ? (int)(total_size / rows) : 0; + if ((src1.dims == 2 || (src1.isContinuous() && src2.isContinuous() && mask.isContinuous())) && cols > 0 && (size_t)rows*cols == total_size && (normType == NORM_INF || normType == NORM_L1 || - normType == NORM_L2 || normType == NORM_L2SQR) ) + normType == NORM_L2 || normType == NORM_L2SQR)) { IppiSize sz = { cols, rows }; int type = src1.type(); - if( !mask.empty() ) + if (!mask.empty()) { - typedef IppStatus (CV_STDCALL* ippiMaskNormDiffFuncC1)(const void *, int, const void *, int, const void *, int, IppiSize, Ipp64f *); + typedef IppStatus(CV_STDCALL* ippiMaskNormRelFuncC1)(const void *, int, const void *, int, const void *, int, IppiSize, Ipp64f *); + ippiMaskNormRelFuncC1 ippFuncC1 = + normType == NORM_INF ? + (type == CV_8UC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_Inf_8u_C1MR : +#ifndef __APPLE__ + type == CV_8SC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_Inf_8s_C1MR : +#endif + type == CV_16UC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_Inf_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_Inf_32f_C1MR : + 0) : + normType == NORM_L1 ? + (type == CV_8UC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L1_8u_C1MR : +#ifndef __APPLE__ + type == CV_8SC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L1_8s_C1MR : +#endif + type == CV_16UC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L1_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L1_32f_C1MR : + 0) : + normType == NORM_L2 || normType == NORM_L2SQR ? + (type == CV_8UC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L2_8u_C1MR : + type == CV_8SC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L2_8s_C1MR : + type == CV_16UC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L2_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L2_32f_C1MR : + 0) : 0; + if (ippFuncC1) + { + Ipp64f norm; + if (ippFuncC1(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], mask.ptr(), (int)mask.step[0], sz, &norm) >= 0) + { + result = normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm; + return true; + } + return false; + } + } + else + { + typedef IppStatus(CV_STDCALL* ippiNormRelFuncNoHint)(const void *, int, const void *, int, IppiSize, Ipp64f *); + typedef IppStatus(CV_STDCALL* ippiNormRelFuncHint)(const void *, int, const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); + ippiNormRelFuncNoHint ippFuncNoHint = + normType == NORM_INF ? + (type == CV_8UC1 ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_8u_C1R : + type == CV_16UC1 ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_16u_C1R : + type == CV_16SC1 ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_16s_C1R : + type == CV_32FC1 ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_32f_C1R : + 0) : + normType == NORM_L1 ? + (type == CV_8UC1 ? (ippiNormRelFuncNoHint)ippiNormRel_L1_8u_C1R : + type == CV_16UC1 ? (ippiNormRelFuncNoHint)ippiNormRel_L1_16u_C1R : + type == CV_16SC1 ? (ippiNormRelFuncNoHint)ippiNormRel_L1_16s_C1R : + 0) : + normType == NORM_L2 || normType == NORM_L2SQR ? + (type == CV_8UC1 ? (ippiNormRelFuncNoHint)ippiNormRel_L2_8u_C1R : + type == CV_16UC1 ? (ippiNormRelFuncNoHint)ippiNormRel_L2_16u_C1R : + type == CV_16SC1 ? (ippiNormRelFuncNoHint)ippiNormRel_L2_16s_C1R : + 0) : 0; + ippiNormRelFuncHint ippFuncHint = + normType == NORM_L1 ? + (type == CV_32FC1 ? (ippiNormRelFuncHint)ippiNormRel_L1_32f_C1R : + 0) : + normType == NORM_L2 || normType == NORM_L2SQR ? + (type == CV_32FC1 ? (ippiNormRelFuncHint)ippiNormRel_L2_32f_C1R : + 0) : 0; + if (ippFuncNoHint) + { + Ipp64f norm; + if (ippFuncNoHint(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, &norm) >= 0) + { + result = (double)norm; + return true; + } + return false; + } + if (ippFuncHint) + { + Ipp64f norm; + if (ippFuncHint(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, &norm, ippAlgHintAccurate) >= 0) + { + result = (double)norm; + return true; + } + return false; + } + } + } + return false; + } + static bool ipp_norm_dif(InputArray _src1, InputArray _src2, int normType, InputArray _mask, double & result) + { + Mat src1 = _src1.getMat(), src2 = _src2.getMat(), mask = _mask.getMat(); + size_t total_size = src1.total(); + int rows = src1.size[0], cols = rows ? (int)(total_size / rows) : 0; + if ((src1.dims == 2 || (src1.isContinuous() && src2.isContinuous() && mask.isContinuous())) + && cols > 0 && (size_t)rows*cols == total_size + && (normType == NORM_INF || normType == NORM_L1 || + normType == NORM_L2 || normType == NORM_L2SQR)) + { + IppiSize sz = { cols, rows }; + int type = src1.type(); + if (!mask.empty()) + { + typedef IppStatus(CV_STDCALL* ippiMaskNormDiffFuncC1)(const void *, int, const void *, int, const void *, int, IppiSize, Ipp64f *); ippiMaskNormDiffFuncC1 ippFuncC1 = normType == NORM_INF ? (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_8u_C1MR : @@ -3158,18 +3188,18 @@ double cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _m type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_16u_C1MR : type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_32f_C1MR : 0) : 0; - if( ippFuncC1 ) + if (ippFuncC1) { Ipp64f norm; - if( ippFuncC1(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], mask.ptr(), (int)mask.step[0], sz, &norm) >= 0 ) + if (ippFuncC1(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], mask.ptr(), (int)mask.step[0], sz, &norm) >= 0) { - CV_IMPL_ADD(CV_IMPL_IPP); - return normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm; + result = normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm; + return true; } - setIppErrorStatus(); + return false; } #ifndef __APPLE__ - typedef IppStatus (CV_STDCALL* ippiMaskNormDiffFuncC3)(const void *, int, const void *, int, const void *, int, IppiSize, int, Ipp64f *); + typedef IppStatus(CV_STDCALL* ippiMaskNormDiffFuncC3)(const void *, int, const void *, int, const void *, int, IppiSize, int, Ipp64f *); ippiMaskNormDiffFuncC3 ippFuncC3 = normType == NORM_INF ? (type == CV_8UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_Inf_8u_C3CMR : @@ -3189,10 +3219,10 @@ double cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _m type == CV_16UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_16u_C3CMR : type == CV_32FC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_32f_C3CMR : 0) : 0; - if( ippFuncC3 ) + if (ippFuncC3) { Ipp64f norm1, norm2, norm3; - if( ippFuncC3(src1.data, (int)src1.step[0], src2.data, (int)src2.step[0], mask.data, (int)mask.step[0], sz, 1, &norm1) >= 0 && + if (ippFuncC3(src1.data, (int)src1.step[0], src2.data, (int)src2.step[0], mask.data, (int)mask.step[0], sz, 1, &norm1) >= 0 && ippFuncC3(src1.data, (int)src1.step[0], src2.data, (int)src2.step[0], mask.data, (int)mask.step[0], sz, 2, &norm2) >= 0 && ippFuncC3(src1.data, (int)src1.step[0], src2.data, (int)src2.step[0], mask.data, (int)mask.step[0], sz, 3, &norm3) >= 0) { @@ -3201,17 +3231,17 @@ double cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _m normType == NORM_L1 ? norm1 + norm2 + norm3 : normType == NORM_L2 || normType == NORM_L2SQR ? std::sqrt(norm1 * norm1 + norm2 * norm2 + norm3 * norm3) : 0; - CV_IMPL_ADD(CV_IMPL_IPP); - return normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm; + result = normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm; + return true; } - setIppErrorStatus(); + return false; } #endif } else { - typedef IppStatus (CV_STDCALL* ippiNormDiffFuncHint)(const void *, int, const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); - typedef IppStatus (CV_STDCALL* ippiNormDiffFuncNoHint)(const void *, int, const void *, int, IppiSize, Ipp64f *); + typedef IppStatus(CV_STDCALL* ippiNormDiffFuncHint)(const void *, int, const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); + typedef IppStatus(CV_STDCALL* ippiNormDiffFuncNoHint)(const void *, int, const void *, int, IppiSize, Ipp64f *); ippiNormDiffFuncHint ippFuncHint = normType == NORM_L1 ? (type == CV_32FC1 ? (ippiNormDiffFuncHint)ippiNormDiff_L1_32f_C1R : @@ -3266,15 +3296,17 @@ double cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _m 0) : 0; // Make sure only zero or one version of the function pointer is valid CV_Assert(!ippFuncHint || !ippFuncNoHint); - if( ippFuncHint || ippFuncNoHint ) + if (ippFuncHint || ippFuncNoHint) { Ipp64f norm_array[4]; IppStatus ret = ippFuncHint ? ippFuncHint(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, norm_array, ippAlgHintAccurate) : - ippFuncNoHint(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, norm_array); - if( ret >= 0 ) + ippFuncNoHint(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, norm_array); + if (ret >= 0) { Ipp64f norm = (normType == NORM_L2 || normType == NORM_L2SQR) ? norm_array[0] * norm_array[0] : norm_array[0]; - for( int i = 1; i < src1.channels(); i++ ) + CV_Assert(src1.channels() <= 4); + int cn = min(src1.channels(), 4); + for (int i = 1; i < cn; i++) { norm = normType == NORM_INF ? std::max(norm, norm_array[i]) : @@ -3282,15 +3314,47 @@ double cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _m normType == NORM_L2 || normType == NORM_L2SQR ? norm + norm_array[i] * norm_array[i] : 0; } - CV_IMPL_ADD(CV_IMPL_IPP); - return normType == NORM_L2 ? (double)std::sqrt(norm) : (double)norm; + result = normType == NORM_L2 ? (double)std::sqrt(norm) : (double)norm; + return true; } - setIppErrorStatus(); + return false; } } } + return false; } #endif +} + + +double cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _mask ) +{ + CV_Assert( _src1.sameSize(_src2) && _src1.type() == _src2.type() ); + +#if defined HAVE_OPENCL || defined HAVE_IPP + double _result = 0; +#endif + +#ifdef HAVE_OPENCL + CV_OCL_RUN_(OCL_PERFORMANCE_CHECK(_src1.isUMat()), + ocl_norm(_src1, _src2, normType, _mask, _result), + _result) +#endif + + if( normType & CV_RELATIVE ) + { + CV_IPP_RUN(true, ipp_norm_rel(_src1, _src2, normType, _mask, _result), _result); + return norm(_src1, _src2, normType & ~CV_RELATIVE, _mask)/(norm(_src2, normType, _mask) + DBL_EPSILON); + } + + Mat src1 = _src1.getMat(), src2 = _src2.getMat(), mask = _mask.getMat(); + int depth = src1.depth(), cn = src1.channels(); + + normType &= 7; + CV_Assert( normType == NORM_INF || normType == NORM_L1 || + normType == NORM_L2 || normType == NORM_L2SQR || + ((normType == NORM_HAMMING || normType == NORM_HAMMING2) && src1.type() == CV_8U) ); + CV_IPP_RUN(true, ipp_norm_dif(_src1, _src2, normType, _mask, _result), _result); if( src1.isContinuous() && src2.isContinuous() && mask.empty() ) { diff --git a/modules/imgproc/src/accum.cpp b/modules/imgproc/src/accum.cpp index 23dc4576ba..8792e85d04 100644 --- a/modules/imgproc/src/accum.cpp +++ b/modules/imgproc/src/accum.cpp @@ -843,6 +843,70 @@ static bool ocl_accumulate( InputArray _src, InputArray _src2, InputOutputArray } +#if defined(HAVE_IPP) +namespace cv +{ +static bool ipp_accumulate(InputArray _src, InputOutputArray _dst, InputArray _mask) +{ + int stype = _src.type(), sdepth = CV_MAT_DEPTH(stype), scn = CV_MAT_CN(stype); + int dtype = _dst.type(), ddepth = CV_MAT_DEPTH(dtype); + + Mat src = _src.getMat(), dst = _dst.getMat(), mask = _mask.getMat(); + + if (src.dims <= 2 || (src.isContinuous() && dst.isContinuous() && (mask.empty() || mask.isContinuous()))) + { + typedef IppStatus (CV_STDCALL * ippiAdd)(const void * pSrc, int srcStep, Ipp32f * pSrcDst, int srcdstStep, IppiSize roiSize); + typedef IppStatus (CV_STDCALL * ippiAddMask)(const void * pSrc, int srcStep, const Ipp8u * pMask, int maskStep, Ipp32f * pSrcDst, + int srcDstStep, IppiSize roiSize); + ippiAdd ippFunc = 0; + ippiAddMask ippFuncMask = 0; + + if (mask.empty()) + { + CV_SUPPRESS_DEPRECATED_START + ippFunc = sdepth == CV_8U && ddepth == CV_32F ? (ippiAdd)ippiAdd_8u32f_C1IR : + sdepth == CV_16U && ddepth == CV_32F ? (ippiAdd)ippiAdd_16u32f_C1IR : + sdepth == CV_32F && ddepth == CV_32F ? (ippiAdd)ippiAdd_32f_C1IR : 0; + CV_SUPPRESS_DEPRECATED_END + } + else if (scn == 1) + { + ippFuncMask = sdepth == CV_8U && ddepth == CV_32F ? (ippiAddMask)ippiAdd_8u32f_C1IMR : + sdepth == CV_16U && ddepth == CV_32F ? (ippiAddMask)ippiAdd_16u32f_C1IMR : + sdepth == CV_32F && ddepth == CV_32F ? (ippiAddMask)ippiAdd_32f_C1IMR : 0; + } + + if (ippFunc || ippFuncMask) + { + IppStatus status = ippStsErr; + + Size size = src.size(); + int srcstep = (int)src.step, dststep = (int)dst.step, maskstep = (int)mask.step; + if (src.isContinuous() && dst.isContinuous() && mask.isContinuous()) + { + srcstep = static_cast(src.total() * src.elemSize()); + dststep = static_cast(dst.total() * dst.elemSize()); + maskstep = static_cast(mask.total() * mask.elemSize()); + size.width = static_cast(src.total()); + size.height = 1; + } + size.width *= scn; + + if (ippFunc) + status = ippFunc(src.ptr(), srcstep, dst.ptr(), dststep, ippiSize(size.width, size.height)); + else if(ippFuncMask) + status = ippFuncMask(src.ptr(), srcstep, mask.ptr(), maskstep, + dst.ptr(), dststep, ippiSize(size.width, size.height)); + + if (status >= 0) + return true; + } + } + return false; +} +} +#endif + void cv::accumulate( InputArray _src, InputOutputArray _dst, InputArray _mask ) { int stype = _src.type(), sdepth = CV_MAT_DEPTH(stype), scn = CV_MAT_CN(stype); @@ -854,66 +918,11 @@ void cv::accumulate( InputArray _src, InputOutputArray _dst, InputArray _mask ) CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(), ocl_accumulate(_src, noArray(), _dst, 0.0, _mask, ACCUMULATE)) + CV_IPP_RUN((_src.dims() <= 2 || (_src.isContinuous() && _dst.isContinuous() && (_mask.empty() || _mask.isContinuous()))), + ipp_accumulate(_src, _dst, _mask)); + Mat src = _src.getMat(), dst = _dst.getMat(), mask = _mask.getMat(); -#if defined HAVE_IPP - CV_IPP_CHECK() - { - if (src.dims <= 2 || (src.isContinuous() && dst.isContinuous() && (mask.empty() || mask.isContinuous()))) - { - typedef IppStatus (CV_STDCALL * ippiAdd)(const void * pSrc, int srcStep, Ipp32f * pSrcDst, int srcdstStep, IppiSize roiSize); - typedef IppStatus (CV_STDCALL * ippiAddMask)(const void * pSrc, int srcStep, const Ipp8u * pMask, int maskStep, Ipp32f * pSrcDst, - int srcDstStep, IppiSize roiSize); - ippiAdd ippFunc = 0; - ippiAddMask ippFuncMask = 0; - - if (mask.empty()) - { - CV_SUPPRESS_DEPRECATED_START - ippFunc = sdepth == CV_8U && ddepth == CV_32F ? (ippiAdd)ippiAdd_8u32f_C1IR : - sdepth == CV_16U && ddepth == CV_32F ? (ippiAdd)ippiAdd_16u32f_C1IR : - sdepth == CV_32F && ddepth == CV_32F ? (ippiAdd)ippiAdd_32f_C1IR : 0; - CV_SUPPRESS_DEPRECATED_END - } - else if (scn == 1) - { - ippFuncMask = sdepth == CV_8U && ddepth == CV_32F ? (ippiAddMask)ippiAdd_8u32f_C1IMR : - sdepth == CV_16U && ddepth == CV_32F ? (ippiAddMask)ippiAdd_16u32f_C1IMR : - sdepth == CV_32F && ddepth == CV_32F ? (ippiAddMask)ippiAdd_32f_C1IMR : 0; - } - - if (ippFunc || ippFuncMask) - { - IppStatus status = ippStsNoErr; - - Size size = src.size(); - int srcstep = (int)src.step, dststep = (int)dst.step, maskstep = (int)mask.step; - if (src.isContinuous() && dst.isContinuous() && mask.isContinuous()) - { - srcstep = static_cast(src.total() * src.elemSize()); - dststep = static_cast(dst.total() * dst.elemSize()); - maskstep = static_cast(mask.total() * mask.elemSize()); - size.width = static_cast(src.total()); - size.height = 1; - } - size.width *= scn; - - if (mask.empty()) - status = ippFunc(src.ptr(), srcstep, dst.ptr(), dststep, ippiSize(size.width, size.height)); - else - status = ippFuncMask(src.ptr(), srcstep, mask.ptr(), maskstep, - dst.ptr(), dststep, ippiSize(size.width, size.height)); - - if (status >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - } - } -#endif int fidx = getAccTabIdx(sdepth, ddepth); AccFunc func = fidx >= 0 ? accTab[fidx] : 0; @@ -928,6 +937,68 @@ void cv::accumulate( InputArray _src, InputOutputArray _dst, InputArray _mask ) func(ptrs[0], ptrs[1], ptrs[2], len, scn); } +#if defined(HAVE_IPP) +namespace cv +{ +static bool ipp_accumulate_square(InputArray _src, InputOutputArray _dst, InputArray _mask) +{ + int stype = _src.type(), sdepth = CV_MAT_DEPTH(stype), scn = CV_MAT_CN(stype); + int dtype = _dst.type(), ddepth = CV_MAT_DEPTH(dtype); + + Mat src = _src.getMat(), dst = _dst.getMat(), mask = _mask.getMat(); + + if (src.dims <= 2 || (src.isContinuous() && dst.isContinuous() && (mask.empty() || mask.isContinuous()))) + { + typedef IppStatus (CV_STDCALL * ippiAddSquare)(const void * pSrc, int srcStep, Ipp32f * pSrcDst, int srcdstStep, IppiSize roiSize); + typedef IppStatus (CV_STDCALL * ippiAddSquareMask)(const void * pSrc, int srcStep, const Ipp8u * pMask, int maskStep, Ipp32f * pSrcDst, + int srcDstStep, IppiSize roiSize); + ippiAddSquare ippFunc = 0; + ippiAddSquareMask ippFuncMask = 0; + + if (mask.empty()) + { + ippFunc = sdepth == CV_8U && ddepth == CV_32F ? (ippiAddSquare)ippiAddSquare_8u32f_C1IR : + sdepth == CV_16U && ddepth == CV_32F ? (ippiAddSquare)ippiAddSquare_16u32f_C1IR : + sdepth == CV_32F && ddepth == CV_32F ? (ippiAddSquare)ippiAddSquare_32f_C1IR : 0; + } + else if (scn == 1) + { + ippFuncMask = sdepth == CV_8U && ddepth == CV_32F ? (ippiAddSquareMask)ippiAddSquare_8u32f_C1IMR : + sdepth == CV_16U && ddepth == CV_32F ? (ippiAddSquareMask)ippiAddSquare_16u32f_C1IMR : + sdepth == CV_32F && ddepth == CV_32F ? (ippiAddSquareMask)ippiAddSquare_32f_C1IMR : 0; + } + + if (ippFunc || ippFuncMask) + { + IppStatus status = ippStsErr; + + Size size = src.size(); + int srcstep = (int)src.step, dststep = (int)dst.step, maskstep = (int)mask.step; + if (src.isContinuous() && dst.isContinuous() && mask.isContinuous()) + { + srcstep = static_cast(src.total() * src.elemSize()); + dststep = static_cast(dst.total() * dst.elemSize()); + maskstep = static_cast(mask.total() * mask.elemSize()); + size.width = static_cast(src.total()); + size.height = 1; + } + size.width *= scn; + + if (ippFunc) + status = ippFunc(src.ptr(), srcstep, dst.ptr(), dststep, ippiSize(size.width, size.height)); + else if(ippFuncMask) + status = ippFuncMask(src.ptr(), srcstep, mask.ptr(), maskstep, + dst.ptr(), dststep, ippiSize(size.width, size.height)); + + if (status >= 0) + return true; + } + } + return false; +} +} +#endif + void cv::accumulateSquare( InputArray _src, InputOutputArray _dst, InputArray _mask ) { int stype = _src.type(), sdepth = CV_MAT_DEPTH(stype), scn = CV_MAT_CN(stype); @@ -939,65 +1010,11 @@ void cv::accumulateSquare( InputArray _src, InputOutputArray _dst, InputArray _m CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(), ocl_accumulate(_src, noArray(), _dst, 0.0, _mask, ACCUMULATE_SQUARE)) + CV_IPP_RUN((_src.dims() <= 2 || (_src.isContinuous() && _dst.isContinuous() && (_mask.empty() || _mask.isContinuous()))), + ipp_accumulate_square(_src, _dst, _mask)); + Mat src = _src.getMat(), dst = _dst.getMat(), mask = _mask.getMat(); -#if defined(HAVE_IPP) - CV_IPP_CHECK() - { - if (src.dims <= 2 || (src.isContinuous() && dst.isContinuous() && (mask.empty() || mask.isContinuous()))) - { - typedef IppStatus (CV_STDCALL * ippiAddSquare)(const void * pSrc, int srcStep, Ipp32f * pSrcDst, int srcdstStep, IppiSize roiSize); - typedef IppStatus (CV_STDCALL * ippiAddSquareMask)(const void * pSrc, int srcStep, const Ipp8u * pMask, int maskStep, Ipp32f * pSrcDst, - int srcDstStep, IppiSize roiSize); - ippiAddSquare ippFunc = 0; - ippiAddSquareMask ippFuncMask = 0; - - if (mask.empty()) - { - ippFunc = sdepth == CV_8U && ddepth == CV_32F ? (ippiAddSquare)ippiAddSquare_8u32f_C1IR : - sdepth == CV_16U && ddepth == CV_32F ? (ippiAddSquare)ippiAddSquare_16u32f_C1IR : - sdepth == CV_32F && ddepth == CV_32F ? (ippiAddSquare)ippiAddSquare_32f_C1IR : 0; - } - else if (scn == 1) - { - ippFuncMask = sdepth == CV_8U && ddepth == CV_32F ? (ippiAddSquareMask)ippiAddSquare_8u32f_C1IMR : - sdepth == CV_16U && ddepth == CV_32F ? (ippiAddSquareMask)ippiAddSquare_16u32f_C1IMR : - sdepth == CV_32F && ddepth == CV_32F ? (ippiAddSquareMask)ippiAddSquare_32f_C1IMR : 0; - } - - if (ippFunc || ippFuncMask) - { - IppStatus status = ippStsNoErr; - - Size size = src.size(); - int srcstep = (int)src.step, dststep = (int)dst.step, maskstep = (int)mask.step; - if (src.isContinuous() && dst.isContinuous() && mask.isContinuous()) - { - srcstep = static_cast(src.total() * src.elemSize()); - dststep = static_cast(dst.total() * dst.elemSize()); - maskstep = static_cast(mask.total() * mask.elemSize()); - size.width = static_cast(src.total()); - size.height = 1; - } - size.width *= scn; - - if (mask.empty()) - status = ippFunc(src.ptr(), srcstep, dst.ptr(), dststep, ippiSize(size.width, size.height)); - else - status = ippFuncMask(src.ptr(), srcstep, mask.ptr(), maskstep, - dst.ptr(), dststep, ippiSize(size.width, size.height)); - - if (status >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - } - } -#endif - int fidx = getAccTabIdx(sdepth, ddepth); AccFunc func = fidx >= 0 ? accSqrTab[fidx] : 0; CV_Assert( func != 0 ); @@ -1011,6 +1028,74 @@ void cv::accumulateSquare( InputArray _src, InputOutputArray _dst, InputArray _m func(ptrs[0], ptrs[1], ptrs[2], len, scn); } +#if defined(HAVE_IPP) +namespace cv +{ +static bool ipp_accumulate_product(InputArray _src1, InputArray _src2, + InputOutputArray _dst, InputArray _mask) +{ + int stype = _src1.type(), sdepth = CV_MAT_DEPTH(stype), scn = CV_MAT_CN(stype); + int dtype = _dst.type(), ddepth = CV_MAT_DEPTH(dtype); + + Mat src1 = _src1.getMat(), src2 = _src2.getMat(), dst = _dst.getMat(), mask = _mask.getMat(); + + if (src1.dims <= 2 || (src1.isContinuous() && src2.isContinuous() && dst.isContinuous())) + { + typedef IppStatus (CV_STDCALL * ippiAddProduct)(const void * pSrc1, int src1Step, const void * pSrc2, + int src2Step, Ipp32f * pSrcDst, int srcDstStep, IppiSize roiSize); + typedef IppStatus (CV_STDCALL * ippiAddProductMask)(const void * pSrc1, int src1Step, const void * pSrc2, int src2Step, + const Ipp8u * pMask, int maskStep, Ipp32f * pSrcDst, int srcDstStep, IppiSize roiSize); + ippiAddProduct ippFunc = 0; + ippiAddProductMask ippFuncMask = 0; + + if (mask.empty()) + { + ippFunc = sdepth == CV_8U && ddepth == CV_32F ? (ippiAddProduct)ippiAddProduct_8u32f_C1IR : + sdepth == CV_16U && ddepth == CV_32F ? (ippiAddProduct)ippiAddProduct_16u32f_C1IR : + sdepth == CV_32F && ddepth == CV_32F ? (ippiAddProduct)ippiAddProduct_32f_C1IR : 0; + } + else if (scn == 1) + { + ippFuncMask = sdepth == CV_8U && ddepth == CV_32F ? (ippiAddProductMask)ippiAddProduct_8u32f_C1IMR : + sdepth == CV_16U && ddepth == CV_32F ? (ippiAddProductMask)ippiAddProduct_16u32f_C1IMR : + sdepth == CV_32F && ddepth == CV_32F ? (ippiAddProductMask)ippiAddProduct_32f_C1IMR : 0; + } + + if (ippFunc || ippFuncMask) + { + IppStatus status = ippStsErr; + + Size size = src1.size(); + int src1step = (int)src1.step, src2step = (int)src2.step, dststep = (int)dst.step, maskstep = (int)mask.step; + if (src1.isContinuous() && src2.isContinuous() && dst.isContinuous() && mask.isContinuous()) + { + src1step = static_cast(src1.total() * src1.elemSize()); + src2step = static_cast(src2.total() * src2.elemSize()); + dststep = static_cast(dst.total() * dst.elemSize()); + maskstep = static_cast(mask.total() * mask.elemSize()); + size.width = static_cast(src1.total()); + size.height = 1; + } + size.width *= scn; + + if (ippFunc) + status = ippFunc(src1.ptr(), src1step, src2.ptr(), src2step, dst.ptr(), + dststep, ippiSize(size.width, size.height)); + else if(ippFuncMask) + status = ippFuncMask(src1.ptr(), src1step, src2.ptr(), src2step, mask.ptr(), maskstep, + dst.ptr(), dststep, ippiSize(size.width, size.height)); + + if (status >= 0) + return true; + } + } + return false; +} +} +#endif + + + void cv::accumulateProduct( InputArray _src1, InputArray _src2, InputOutputArray _dst, InputArray _mask ) { @@ -1024,68 +1109,11 @@ void cv::accumulateProduct( InputArray _src1, InputArray _src2, CV_OCL_RUN(_src1.dims() <= 2 && _dst.isUMat(), ocl_accumulate(_src1, _src2, _dst, 0.0, _mask, ACCUMULATE_PRODUCT)) + CV_IPP_RUN( (_src1.dims() <= 2 || (_src1.isContinuous() && _src2.isContinuous() && _dst.isContinuous())), + ipp_accumulate_product(_src1, _src2, _dst, _mask)); + Mat src1 = _src1.getMat(), src2 = _src2.getMat(), dst = _dst.getMat(), mask = _mask.getMat(); -#if defined(HAVE_IPP) - CV_IPP_CHECK() - { - if (src1.dims <= 2 || (src1.isContinuous() && src2.isContinuous() && dst.isContinuous())) - { - typedef IppStatus (CV_STDCALL * ippiAddProduct)(const void * pSrc1, int src1Step, const void * pSrc2, - int src2Step, Ipp32f * pSrcDst, int srcDstStep, IppiSize roiSize); - typedef IppStatus (CV_STDCALL * ippiAddProductMask)(const void * pSrc1, int src1Step, const void * pSrc2, int src2Step, - const Ipp8u * pMask, int maskStep, Ipp32f * pSrcDst, int srcDstStep, IppiSize roiSize); - ippiAddProduct ippFunc = 0; - ippiAddProductMask ippFuncMask = 0; - - if (mask.empty()) - { - ippFunc = sdepth == CV_8U && ddepth == CV_32F ? (ippiAddProduct)ippiAddProduct_8u32f_C1IR : - sdepth == CV_16U && ddepth == CV_32F ? (ippiAddProduct)ippiAddProduct_16u32f_C1IR : - sdepth == CV_32F && ddepth == CV_32F ? (ippiAddProduct)ippiAddProduct_32f_C1IR : 0; - } - else if (scn == 1) - { - ippFuncMask = sdepth == CV_8U && ddepth == CV_32F ? (ippiAddProductMask)ippiAddProduct_8u32f_C1IMR : - sdepth == CV_16U && ddepth == CV_32F ? (ippiAddProductMask)ippiAddProduct_16u32f_C1IMR : - sdepth == CV_32F && ddepth == CV_32F ? (ippiAddProductMask)ippiAddProduct_32f_C1IMR : 0; - } - - if (ippFunc || ippFuncMask) - { - IppStatus status = ippStsNoErr; - - Size size = src1.size(); - int src1step = (int)src1.step, src2step = (int)src2.step, dststep = (int)dst.step, maskstep = (int)mask.step; - if (src1.isContinuous() && src2.isContinuous() && dst.isContinuous() && mask.isContinuous()) - { - src1step = static_cast(src1.total() * src1.elemSize()); - src2step = static_cast(src2.total() * src2.elemSize()); - dststep = static_cast(dst.total() * dst.elemSize()); - maskstep = static_cast(mask.total() * mask.elemSize()); - size.width = static_cast(src1.total()); - size.height = 1; - } - size.width *= scn; - - if (mask.empty()) - status = ippFunc(src1.ptr(), src1step, src2.ptr(), src2step, dst.ptr(), - dststep, ippiSize(size.width, size.height)); - else - status = ippFuncMask(src1.ptr(), src1step, src2.ptr(), src2step, mask.ptr(), maskstep, - dst.ptr(), dststep, ippiSize(size.width, size.height)); - - if (status >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - } - } -#endif - int fidx = getAccTabIdx(sdepth, ddepth); AccProdFunc func = fidx >= 0 ? accProdTab[fidx] : 0; CV_Assert( func != 0 ); @@ -1099,6 +1127,71 @@ void cv::accumulateProduct( InputArray _src1, InputArray _src2, func(ptrs[0], ptrs[1], ptrs[2], ptrs[3], len, scn); } +#if defined(HAVE_IPP) +namespace cv +{ +static bool ipp_accumulate_weighted( InputArray _src, InputOutputArray _dst, + double alpha, InputArray _mask ) +{ + int stype = _src.type(), sdepth = CV_MAT_DEPTH(stype), scn = CV_MAT_CN(stype); + int dtype = _dst.type(), ddepth = CV_MAT_DEPTH(dtype); + + Mat src = _src.getMat(), dst = _dst.getMat(), mask = _mask.getMat(); + + if (src.dims <= 2 || (src.isContinuous() && dst.isContinuous() && mask.isContinuous())) + { + typedef IppStatus (CV_STDCALL * ippiAddWeighted)(const void * pSrc, int srcStep, Ipp32f * pSrcDst, int srcdstStep, + IppiSize roiSize, Ipp32f alpha); + typedef IppStatus (CV_STDCALL * ippiAddWeightedMask)(const void * pSrc, int srcStep, const Ipp8u * pMask, + int maskStep, Ipp32f * pSrcDst, + int srcDstStep, IppiSize roiSize, Ipp32f alpha); + ippiAddWeighted ippFunc = 0; + ippiAddWeightedMask ippFuncMask = 0; + + if (mask.empty()) + { + ippFunc = sdepth == CV_8U && ddepth == CV_32F ? (ippiAddWeighted)ippiAddWeighted_8u32f_C1IR : + sdepth == CV_16U && ddepth == CV_32F ? (ippiAddWeighted)ippiAddWeighted_16u32f_C1IR : + sdepth == CV_32F && ddepth == CV_32F ? (ippiAddWeighted)ippiAddWeighted_32f_C1IR : 0; + } + else if (scn == 1) + { + ippFuncMask = sdepth == CV_8U && ddepth == CV_32F ? (ippiAddWeightedMask)ippiAddWeighted_8u32f_C1IMR : + sdepth == CV_16U && ddepth == CV_32F ? (ippiAddWeightedMask)ippiAddWeighted_16u32f_C1IMR : + sdepth == CV_32F && ddepth == CV_32F ? (ippiAddWeightedMask)ippiAddWeighted_32f_C1IMR : 0; + } + + if (ippFunc || ippFuncMask) + { + IppStatus status = ippStsErr; + + Size size = src.size(); + int srcstep = (int)src.step, dststep = (int)dst.step, maskstep = (int)mask.step; + if (src.isContinuous() && dst.isContinuous() && mask.isContinuous()) + { + srcstep = static_cast(src.total() * src.elemSize()); + dststep = static_cast(dst.total() * dst.elemSize()); + maskstep = static_cast(mask.total() * mask.elemSize()); + size.width = static_cast((int)src.total()); + size.height = 1; + } + size.width *= scn; + + if (ippFunc) + status = ippFunc(src.ptr(), srcstep, dst.ptr(), dststep, ippiSize(size.width, size.height), (Ipp32f)alpha); + else if(ippFuncMask) + status = ippFuncMask(src.ptr(), srcstep, mask.ptr(), maskstep, + dst.ptr(), dststep, ippiSize(size.width, size.height), (Ipp32f)alpha); + + if (status >= 0) + return true; + } + } + return false; +} +} +#endif + void cv::accumulateWeighted( InputArray _src, InputOutputArray _dst, double alpha, InputArray _mask ) { @@ -1111,66 +1204,11 @@ void cv::accumulateWeighted( InputArray _src, InputOutputArray _dst, CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(), ocl_accumulate(_src, noArray(), _dst, alpha, _mask, ACCUMULATE_WEIGHTED)) + CV_IPP_RUN((_src.dims() <= 2 || (_src.isContinuous() && _dst.isContinuous() && _mask.isContinuous())), ipp_accumulate_weighted(_src, _dst, alpha, _mask)); + + Mat src = _src.getMat(), dst = _dst.getMat(), mask = _mask.getMat(); -#if defined(HAVE_IPP) - CV_IPP_CHECK() - { - if (src.dims <= 2 || (src.isContinuous() && dst.isContinuous() && mask.isContinuous())) - { - typedef IppStatus (CV_STDCALL * ippiAddWeighted)(const void * pSrc, int srcStep, Ipp32f * pSrcDst, int srcdstStep, - IppiSize roiSize, Ipp32f alpha); - typedef IppStatus (CV_STDCALL * ippiAddWeightedMask)(const void * pSrc, int srcStep, const Ipp8u * pMask, - int maskStep, Ipp32f * pSrcDst, - int srcDstStep, IppiSize roiSize, Ipp32f alpha); - ippiAddWeighted ippFunc = 0; - ippiAddWeightedMask ippFuncMask = 0; - - if (mask.empty()) - { - ippFunc = sdepth == CV_8U && ddepth == CV_32F ? (ippiAddWeighted)ippiAddWeighted_8u32f_C1IR : - sdepth == CV_16U && ddepth == CV_32F ? (ippiAddWeighted)ippiAddWeighted_16u32f_C1IR : - sdepth == CV_32F && ddepth == CV_32F ? (ippiAddWeighted)ippiAddWeighted_32f_C1IR : 0; - } - else if (scn == 1) - { - ippFuncMask = sdepth == CV_8U && ddepth == CV_32F ? (ippiAddWeightedMask)ippiAddWeighted_8u32f_C1IMR : - sdepth == CV_16U && ddepth == CV_32F ? (ippiAddWeightedMask)ippiAddWeighted_16u32f_C1IMR : - sdepth == CV_32F && ddepth == CV_32F ? (ippiAddWeightedMask)ippiAddWeighted_32f_C1IMR : 0; - } - - if (ippFunc || ippFuncMask) - { - IppStatus status = ippStsNoErr; - - Size size = src.size(); - int srcstep = (int)src.step, dststep = (int)dst.step, maskstep = (int)mask.step; - if (src.isContinuous() && dst.isContinuous() && mask.isContinuous()) - { - srcstep = static_cast(src.total() * src.elemSize()); - dststep = static_cast(dst.total() * dst.elemSize()); - maskstep = static_cast(mask.total() * mask.elemSize()); - size.width = static_cast((int)src.total()); - size.height = 1; - } - size.width *= scn; - - if (mask.empty()) - status = ippFunc(src.ptr(), srcstep, dst.ptr(), dststep, ippiSize(size.width, size.height), (Ipp32f)alpha); - else - status = ippFuncMask(src.ptr(), srcstep, mask.ptr(), maskstep, - dst.ptr(), dststep, ippiSize(size.width, size.height), (Ipp32f)alpha); - - if (status >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - } - } -#endif int fidx = getAccTabIdx(sdepth, ddepth); AccWFunc func = fidx >= 0 ? accWTab[fidx] : 0; diff --git a/modules/imgproc/src/color.cpp b/modules/imgproc/src/color.cpp index f8bc095891..6f53a120ae 100644 --- a/modules/imgproc/src/color.cpp +++ b/modules/imgproc/src/color.cpp @@ -7325,6 +7325,600 @@ static bool ocl_cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) }//namespace cv +#ifdef HAVE_IPP +namespace cv +{ +static bool ipp_cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) +{ + int stype = _src.type(); + int scn = CV_MAT_CN(stype), depth = CV_MAT_DEPTH(stype); + + Mat src = _src.getMat(), dst; + Size sz = src.size(); + + switch( code ) + { +#if IPP_VERSION_MAJOR >= 7 + case CV_BGR2BGRA: case CV_RGB2BGRA: case CV_BGRA2BGR: + case CV_RGBA2BGR: case CV_RGB2BGR: case CV_BGRA2RGBA: + CV_Assert( scn == 3 || scn == 4 ); + dcn = code == CV_BGR2BGRA || code == CV_RGB2BGRA || code == CV_BGRA2RGBA ? 4 : 3; + _dst.create( sz, CV_MAKETYPE(depth, dcn)); + dst = _dst.getMat(); + + if( code == CV_BGR2BGRA) + { + if ( CvtColorIPPLoop(src, dst, IPPReorderFunctor(ippiSwapChannelsC3C4RTab[depth], 0, 1, 2)) ) + return true; + } + else if( code == CV_BGRA2BGR ) + { + if ( CvtColorIPPLoop(src, dst, IPPGeneralFunctor(ippiCopyAC4C3RTab[depth])) ) + return true; + } + else if( code == CV_BGR2RGBA ) + { + if( CvtColorIPPLoop(src, dst, IPPReorderFunctor(ippiSwapChannelsC3C4RTab[depth], 2, 1, 0)) ) + return true; + } + else if( code == CV_RGBA2BGR ) + { + if( CvtColorIPPLoop(src, dst, IPPReorderFunctor(ippiSwapChannelsC4C3RTab[depth], 2, 1, 0)) ) + return true; + } + else if( code == CV_RGB2BGR ) + { + if( CvtColorIPPLoopCopy(src, dst, IPPReorderFunctor(ippiSwapChannelsC3RTab[depth], 2, 1, 0)) ) + return true; + } +#if IPP_VERSION_X100 >= 801 + else if( code == CV_RGBA2BGRA ) + { + if( CvtColorIPPLoopCopy(src, dst, IPPReorderFunctor(ippiSwapChannelsC4RTab[depth], 2, 1, 0)) ) + return true; + } +#endif + return false; +#endif + +#if 0 // breaks OCL accuracy tests + case CV_BGR2BGR565: case CV_BGR2BGR555: case CV_RGB2BGR565: case CV_RGB2BGR555: + case CV_BGRA2BGR565: case CV_BGRA2BGR555: case CV_RGBA2BGR565: case CV_RGBA2BGR555: + CV_Assert( (scn == 3 || scn == 4) && depth == CV_8U ); + _dst.create(sz, CV_8UC2); + dst = _dst.getMat(); + + CV_SUPPRESS_DEPRECATED_START + + if (code == CV_BGR2BGR565 && scn == 3) + { + if (CvtColorIPPLoop(src, dst, IPPGeneralFunctor((ippiGeneralFunc)ippiBGRToBGR565_8u16u_C3R))) + return true; + } + else if (code == CV_BGRA2BGR565 && scn == 4) + { + if (CvtColorIPPLoopCopy(src, dst, + IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], + (ippiGeneralFunc)ippiBGRToBGR565_8u16u_C3R, 0, 1, 2, depth))) + return true; + } + else if (code == CV_RGB2BGR565 && scn == 3) + { + if( CvtColorIPPLoopCopy(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], + (ippiGeneralFunc)ippiBGRToBGR565_8u16u_C3R, 2, 1, 0, depth)) ) + return true; + } + else if (code == CV_RGBA2BGR565 && scn == 4) + { + if( CvtColorIPPLoopCopy(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], + (ippiGeneralFunc)ippiBGRToBGR565_8u16u_C3R, 2, 1, 0, depth)) ) + return true; + } + CV_SUPPRESS_DEPRECATED_END + return false; +#endif + + case CV_BGR5652BGR: case CV_BGR5552BGR: case CV_BGR5652RGB: case CV_BGR5552RGB: + case CV_BGR5652BGRA: case CV_BGR5552BGRA: case CV_BGR5652RGBA: case CV_BGR5552RGBA: + if(dcn <= 0) dcn = (code==CV_BGR5652BGRA || code==CV_BGR5552BGRA || code==CV_BGR5652RGBA || code==CV_BGR5552RGBA) ? 4 : 3; + CV_Assert( (dcn == 3 || dcn == 4) && scn == 2 && depth == CV_8U ); + _dst.create(sz, CV_MAKETYPE(depth, dcn)); + dst = _dst.getMat(); + + CV_SUPPRESS_DEPRECATED_START + if (code == CV_BGR5652BGR && dcn == 3) + { + if (CvtColorIPPLoop(src, dst, IPPGeneralFunctor((ippiGeneralFunc)ippiBGR565ToBGR_16u8u_C3R))) + return true; + } + else if (code == CV_BGR5652RGB && dcn == 3) + { + if (CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiBGR565ToBGR_16u8u_C3R, + ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth))) + return true; + } + else if (code == CV_BGR5652BGRA && dcn == 4) + { + if (CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiBGR565ToBGR_16u8u_C3R, + ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth))) + return true; + } + else if (code == CV_BGR5652RGBA && dcn == 4) + { + if (CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiBGR565ToBGR_16u8u_C3R, + ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth))) + return true; + } + CV_SUPPRESS_DEPRECATED_END + return false; + +#if IPP_VERSION_MAJOR >= 7 + case CV_BGR2GRAY: case CV_BGRA2GRAY: case CV_RGB2GRAY: case CV_RGBA2GRAY: + CV_Assert( scn == 3 || scn == 4 ); + _dst.create(sz, CV_MAKETYPE(depth, 1)); + dst = _dst.getMat(); + + if( code == CV_BGR2GRAY && depth == CV_32F ) + { + if( CvtColorIPPLoop(src, dst, IPPColor2GrayFunctor(ippiColor2GrayC3Tab[depth])) ) + return true; + } + else if( code == CV_RGB2GRAY && depth == CV_32F ) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralFunctor(ippiRGB2GrayC3Tab[depth])) ) + return true; + } + else if( code == CV_BGRA2GRAY && depth == CV_32F ) + { + if( CvtColorIPPLoop(src, dst, IPPColor2GrayFunctor(ippiColor2GrayC4Tab[depth])) ) + return true; + } + else if( code == CV_RGBA2GRAY && depth == CV_32F ) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralFunctor(ippiRGB2GrayC4Tab[depth])) ) + return true; + } + return false; + + case CV_GRAY2BGR: case CV_GRAY2BGRA: + if( dcn <= 0 ) dcn = (code==CV_GRAY2BGRA) ? 4 : 3; + CV_Assert( scn == 1 && (dcn == 3 || dcn == 4)); + _dst.create(sz, CV_MAKETYPE(depth, dcn)); + dst = _dst.getMat(); + + if( code == CV_GRAY2BGR ) + { + if( CvtColorIPPLoop(src, dst, IPPGray2BGRFunctor(ippiCopyP3C3RTab[depth])) ) + return true; + } + else if( code == CV_GRAY2BGRA ) + { + if( CvtColorIPPLoop(src, dst, IPPGray2BGRAFunctor(ippiCopyP3C3RTab[depth], ippiSwapChannelsC3C4RTab[depth], depth)) ) + return true; + } + return false; +#endif + +#if 0 + case CV_BGR2YCrCb: case CV_RGB2YCrCb: + case CV_BGR2YUV: case CV_RGB2YUV: + { + CV_Assert( scn == 3 || scn == 4 ); + static const float yuv_f[] = { 0.114f, 0.587f, 0.299f, 0.492f, 0.877f }; + static const int yuv_i[] = { B2Y, G2Y, R2Y, 8061, 14369 }; + const float* coeffs_f = code == CV_BGR2YCrCb || code == CV_RGB2YCrCb ? 0 : yuv_f; + const int* coeffs_i = code == CV_BGR2YCrCb || code == CV_RGB2YCrCb ? 0 : yuv_i; + + _dst.create(sz, CV_MAKETYPE(depth, 3)); + dst = _dst.getMat(); + + if (code == CV_RGB2YUV && scn == 3 && depth == CV_8U) + { + if (CvtColorIPPLoop(src, dst, IPPGeneralFunctor((ippiGeneralFunc)ippiRGBToYUV_8u_C3R))) + return true; + } + else if (code == CV_BGR2YUV && scn == 3 && depth == CV_8U) + { + if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], + (ippiGeneralFunc)ippiRGBToYUV_8u_C3R, 2, 1, 0, depth))) + return true; + } + else if (code == CV_RGB2YUV && scn == 4 && depth == CV_8U) + { + if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], + (ippiGeneralFunc)ippiRGBToYUV_8u_C3R, 0, 1, 2, depth))) + return true; + } + else if (code == CV_BGR2YUV && scn == 4 && depth == CV_8U) + { + if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], + (ippiGeneralFunc)ippiRGBToYUV_8u_C3R, 2, 1, 0, depth))) + return true; + } + return false; + } +#endif + +#if 0 + case CV_YCrCb2BGR: case CV_YCrCb2RGB: + case CV_YUV2BGR: case CV_YUV2RGB: + { + if( dcn <= 0 ) dcn = 3; + CV_Assert( scn == 3 && (dcn == 3 || dcn == 4) ); + static const float yuv_f[] = { 2.032f, -0.395f, -0.581f, 1.140f }; + static const int yuv_i[] = { 33292, -6472, -9519, 18678 }; + const float* coeffs_f = code == CV_YCrCb2BGR || code == CV_YCrCb2RGB ? 0 : yuv_f; + const int* coeffs_i = code == CV_YCrCb2BGR || code == CV_YCrCb2RGB ? 0 : yuv_i; + + _dst.create(sz, CV_MAKETYPE(depth, dcn)); + dst = _dst.getMat(); + + if (code == CV_YUV2RGB && dcn == 3 && depth == CV_8U) + { + if (CvtColorIPPLoop(src, dst, IPPGeneralFunctor((ippiGeneralFunc)ippiYUVToRGB_8u_C3R))) + return true; + } + else if (code == CV_YUV2BGR && dcn == 3 && depth == CV_8U) + { + if (CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiYUVToRGB_8u_C3R, + ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth))) + return true; + } + else if (code == CV_YUV2RGB && dcn == 4 && depth == CV_8U) + { + if (CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiYUVToRGB_8u_C3R, + ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth))) + return true; + } + else if (code == CV_YUV2BGR && dcn == 4 && depth == CV_8U) + { + if (CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiYUVToRGB_8u_C3R, + ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth))) + return true; + } + return false; + } +#endif + +#if IPP_VERSION_MAJOR >= 7 + case CV_BGR2XYZ: case CV_RGB2XYZ: + CV_Assert( scn == 3 || scn == 4 ); + _dst.create(sz, CV_MAKETYPE(depth, 3)); + dst = _dst.getMat(); + + if( code == CV_BGR2XYZ && scn == 3 && depth != CV_32F ) + { + if( CvtColorIPPLoopCopy(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], ippiRGB2XYZTab[depth], 2, 1, 0, depth)) ) + return true; + } + else if( code == CV_BGR2XYZ && scn == 4 && depth != CV_32F ) + { + if( CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2XYZTab[depth], 2, 1, 0, depth)) ) + return true; + } + else if( code == CV_RGB2XYZ && scn == 3 && depth != CV_32F ) + { + if( CvtColorIPPLoopCopy(src, dst, IPPGeneralFunctor(ippiRGB2XYZTab[depth])) ) + return true; + } + else if( code == CV_RGB2XYZ && scn == 4 && depth != CV_32F ) + { + if( CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2XYZTab[depth], 0, 1, 2, depth)) ) + return true; + } + return false; +#endif + +#if IPP_VERSION_MAJOR >= 7 + case CV_XYZ2BGR: case CV_XYZ2RGB: + if( dcn <= 0 ) dcn = 3; + CV_Assert( scn == 3 && (dcn == 3 || dcn == 4) ); + + _dst.create(sz, CV_MAKETYPE(depth, dcn)); + dst = _dst.getMat(); + + if( code == CV_XYZ2BGR && dcn == 3 && depth != CV_32F ) + { + if( CvtColorIPPLoopCopy(src, dst, IPPGeneralReorderFunctor(ippiXYZ2RGBTab[depth], ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)) ) + return true; + } + else if( code == CV_XYZ2BGR && dcn == 4 && depth != CV_32F ) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiXYZ2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)) ) + return true; + } + if( code == CV_XYZ2RGB && dcn == 3 && depth != CV_32F ) + { + if( CvtColorIPPLoopCopy(src, dst, IPPGeneralFunctor(ippiXYZ2RGBTab[depth])) ) + return true; + } + else if( code == CV_XYZ2RGB && dcn == 4 && depth != CV_32F ) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiXYZ2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)) ) + return true; + } + return false; +#endif + +#if IPP_VERSION_MAJOR >= 7 + case CV_BGR2HSV: case CV_RGB2HSV: case CV_BGR2HSV_FULL: case CV_RGB2HSV_FULL: + case CV_BGR2HLS: case CV_RGB2HLS: case CV_BGR2HLS_FULL: case CV_RGB2HLS_FULL: + { + CV_Assert( (scn == 3 || scn == 4) && (depth == CV_8U || depth == CV_32F) ); + _dst.create(sz, CV_MAKETYPE(depth, 3)); + dst = _dst.getMat(); + + if( depth == CV_8U || depth == CV_16U ) + { +#if 0 // breaks OCL accuracy tests + if( code == CV_BGR2HSV_FULL && scn == 3 ) + { + if( CvtColorIPPLoopCopy(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], ippiRGB2HSVTab[depth], 2, 1, 0, depth)) ) + return true; + } + else if( code == CV_BGR2HSV_FULL && scn == 4 ) + { + if( CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2HSVTab[depth], 2, 1, 0, depth)) ) + return true; + } + else if( code == CV_RGB2HSV_FULL && scn == 4 ) + { + if( CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2HSVTab[depth], 0, 1, 2, depth)) ) + return true; + } else +#endif + if( code == CV_RGB2HSV_FULL && scn == 3 && depth == CV_16U ) + { + if( CvtColorIPPLoopCopy(src, dst, IPPGeneralFunctor(ippiRGB2HSVTab[depth])) ) + return true; + } + else if( code == CV_BGR2HLS_FULL && scn == 3 ) + { + if( CvtColorIPPLoopCopy(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], ippiRGB2HLSTab[depth], 2, 1, 0, depth)) ) + return true; + } + else if( code == CV_BGR2HLS_FULL && scn == 4 ) + { + if( CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2HLSTab[depth], 2, 1, 0, depth)) ) + return true; + } + else if( code == CV_RGB2HLS_FULL && scn == 3 ) + { + if( CvtColorIPPLoopCopy(src, dst, IPPGeneralFunctor(ippiRGB2HLSTab[depth])) ) + return true; + } + else if( code == CV_RGB2HLS_FULL && scn == 4 ) + { + if( CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2HLSTab[depth], 0, 1, 2, depth)) ) + return true; + } + } + return false; + } +#endif + +#if IPP_VERSION_MAJOR >= 7 + case CV_HSV2BGR: case CV_HSV2RGB: case CV_HSV2BGR_FULL: case CV_HSV2RGB_FULL: + case CV_HLS2BGR: case CV_HLS2RGB: case CV_HLS2BGR_FULL: case CV_HLS2RGB_FULL: + { + if( dcn <= 0 ) dcn = 3; + CV_Assert( scn == 3 && (dcn == 3 || dcn == 4) && (depth == CV_8U || depth == CV_32F) ); + _dst.create(sz, CV_MAKETYPE(depth, dcn)); + dst = _dst.getMat(); + + if( depth == CV_8U || depth == CV_16U ) + { + if( code == CV_HSV2BGR_FULL && dcn == 3 ) + { + if( CvtColorIPPLoopCopy(src, dst, IPPGeneralReorderFunctor(ippiHSV2RGBTab[depth], ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)) ) + return true; + } + else if( code == CV_HSV2BGR_FULL && dcn == 4 ) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiHSV2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)) ) + return true; + } + else if( code == CV_HSV2RGB_FULL && dcn == 3 ) + { + if( CvtColorIPPLoopCopy(src, dst, IPPGeneralFunctor(ippiHSV2RGBTab[depth])) ) + return true; + } + else if( code == CV_HSV2RGB_FULL && dcn == 4 ) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiHSV2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)) ) + return true; + } + else if( code == CV_HLS2BGR_FULL && dcn == 3 ) + { + if( CvtColorIPPLoopCopy(src, dst, IPPGeneralReorderFunctor(ippiHLS2RGBTab[depth], ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)) ) + return true; + } + else if( code == CV_HLS2BGR_FULL && dcn == 4 ) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiHLS2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)) ) + return true; + } + else if( code == CV_HLS2RGB_FULL && dcn == 3 ) + { + if( CvtColorIPPLoopCopy(src, dst, IPPGeneralFunctor(ippiHLS2RGBTab[depth])) ) + return true; + } + else if( code == CV_HLS2RGB_FULL && dcn == 4 ) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiHLS2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)) ) + return true; + } + } + return false; + } +#endif + +#if 0 + case CV_BGR2Lab: case CV_RGB2Lab: case CV_LBGR2Lab: case CV_LRGB2Lab: + case CV_BGR2Luv: case CV_RGB2Luv: case CV_LBGR2Luv: case CV_LRGB2Luv: + { + CV_Assert( (scn == 3 || scn == 4) && (depth == CV_8U || depth == CV_32F) ); + bool srgb = code == CV_BGR2Lab || code == CV_RGB2Lab || + code == CV_BGR2Luv || code == CV_RGB2Luv; + + _dst.create(sz, CV_MAKETYPE(depth, 3)); + dst = _dst.getMat(); + + if (code == CV_LBGR2Lab && scn == 3 && depth == CV_8U) + { + if (CvtColorIPPLoop(src, dst, IPPGeneralFunctor((ippiGeneralFunc)ippiBGRToLab_8u_C3R))) + return true; + } + else if (code == CV_LBGR2Lab && scn == 4 && depth == CV_8U) + { + if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], + (ippiGeneralFunc)ippiBGRToLab_8u_C3R, 0, 1, 2, depth))) + return true; + } + else + if (code == CV_LRGB2Lab && scn == 3 && depth == CV_8U) // slower than OpenCV + { + if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], + (ippiGeneralFunc)ippiBGRToLab_8u_C3R, 2, 1, 0, depth))) + return true; + } + else if (code == CV_LRGB2Lab && scn == 4 && depth == CV_8U) // slower than OpenCV + { + if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], + (ippiGeneralFunc)ippiBGRToLab_8u_C3R, 2, 1, 0, depth))) + return true; + } + else if (code == CV_LRGB2Luv && scn == 3) + { + if (CvtColorIPPLoop(src, dst, IPPGeneralFunctor(ippiRGBToLUVTab[depth]))) + return true; + } + else if (code == CV_LRGB2Luv && scn == 4) + { + if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], + ippiRGBToLUVTab[depth], 0, 1, 2, depth))) + return true; + } + else if (code == CV_LBGR2Luv && scn == 3) + { + if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], + ippiRGBToLUVTab[depth], 2, 1, 0, depth))) + return true; + } + else if (code == CV_LBGR2Luv && scn == 4) + { + if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], + ippiRGBToLUVTab[depth], 2, 1, 0, depth))) + return true; + } + return false; + } +#endif + +#if 0 + case CV_Lab2BGR: case CV_Lab2RGB: case CV_Lab2LBGR: case CV_Lab2LRGB: + case CV_Luv2BGR: case CV_Luv2RGB: case CV_Luv2LBGR: case CV_Luv2LRGB: + { + if( dcn <= 0 ) dcn = 3; + CV_Assert( scn == 3 && (dcn == 3 || dcn == 4) && (depth == CV_8U || depth == CV_32F) ); + bool srgb = code == CV_Lab2BGR || code == CV_Lab2RGB || + code == CV_Luv2BGR || code == CV_Luv2RGB; + + _dst.create(sz, CV_MAKETYPE(depth, dcn)); + dst = _dst.getMat(); + + if( code == CV_Lab2LBGR && dcn == 3 && depth == CV_8U) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralFunctor((ippiGeneralFunc)ippiLabToBGR_8u_C3R)) ) + return true; + } + else if( code == CV_Lab2LBGR && dcn == 4 && depth == CV_8U ) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiLabToBGR_8u_C3R, + ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)) ) + return true; + } + if( code == CV_Lab2LRGB && dcn == 3 && depth == CV_8U ) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiLabToBGR_8u_C3R, + ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)) ) + return true; + } + else if( code == CV_Lab2LRGB && dcn == 4 && depth == CV_8U ) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiLabToBGR_8u_C3R, + ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)) ) + return true; + } + if( code == CV_Luv2LRGB && dcn == 3 ) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralFunctor(ippiLUVToRGBTab[depth])) ) + return true; + } + else if( code == CV_Luv2LRGB && dcn == 4 ) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiLUVToRGBTab[depth], + ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)) ) + return true; + } + if( code == CV_Luv2LBGR && dcn == 3 ) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiLUVToRGBTab[depth], + ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)) ) + return true; + } + else if( code == CV_Luv2LBGR && dcn == 4 ) + { + if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiLUVToRGBTab[depth], + ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)) ) + return true; + } + return false; + } +#endif + + case CV_YUV2GRAY_420: + { + if (dcn <= 0) dcn = 1; + + CV_Assert( dcn == 1 ); + CV_Assert( sz.width % 2 == 0 && sz.height % 3 == 0 && depth == CV_8U ); + + Size dstSz(sz.width, sz.height * 2 / 3); + _dst.create(dstSz, CV_MAKETYPE(depth, dcn)); + dst = _dst.getMat(); + + if (ippStsNoErr == ippiCopy_8u_C1R(src.data, (int)src.step, dst.data, (int)dst.step, + ippiSize(dstSz.width, dstSz.height))) + return true; + return false; + } + + case CV_RGBA2mRGBA: + { + if (dcn <= 0) dcn = 4; + CV_Assert( scn == 4 && dcn == 4 ); + + _dst.create(sz, CV_MAKETYPE(depth, dcn)); + dst = _dst.getMat(); + + if( depth == CV_8U ) + { + if (CvtColorIPPLoop(src, dst, IPPGeneralFunctor((ippiGeneralFunc)ippiAlphaPremul_8u_AC4R))) + return true; + return false; + } + + return false; + } + + default: + return false; + } +} +} +#endif + ////////////////////////////////////////////////////////////////////////////////////////// // The main function // ////////////////////////////////////////////////////////////////////////////////////////// @@ -7340,7 +7934,7 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) Mat src = _src.getMat(), dst; Size sz = src.size(); - CV_Assert( depth == CV_8U || depth == CV_16U || depth == CV_32F ); + CV_IPP_RUN(true, ipp_cvtColor(_src, _dst, code, dcn)); switch( code ) { @@ -7353,75 +7947,13 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create( sz, CV_MAKETYPE(depth, dcn)); dst = _dst.getMat(); -#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - CV_IPP_CHECK() - { - if( code == CV_BGR2BGRA) - { - if ( CvtColorIPPLoop(src, dst, IPPReorderFunctor(ippiSwapChannelsC3C4RTab[depth], 0, 1, 2)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_BGRA2BGR ) - { - if ( CvtColorIPPLoop(src, dst, IPPGeneralFunctor(ippiCopyAC4C3RTab[depth])) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_BGR2RGBA ) - { - if( CvtColorIPPLoop(src, dst, IPPReorderFunctor(ippiSwapChannelsC3C4RTab[depth], 2, 1, 0)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_RGBA2BGR ) - { - if( CvtColorIPPLoop(src, dst, IPPReorderFunctor(ippiSwapChannelsC4C3RTab[depth], 2, 1, 0)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_RGB2BGR ) - { - if( CvtColorIPPLoopCopy(src, dst, IPPReorderFunctor(ippiSwapChannelsC3RTab[depth], 2, 1, 0)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } -#if IPP_VERSION_X100 >= 801 - else if( code == CV_RGBA2BGRA ) - { - if( CvtColorIPPLoopCopy(src, dst, IPPReorderFunctor(ippiSwapChannelsC4RTab[depth], 2, 1, 0)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } -#endif - } -#endif if( depth == CV_8U ) { #ifdef HAVE_TEGRA_OPTIMIZATION - if(tegra::useTegra() && tegra::cvtBGR2RGB(src, dst, bidx)) - break; + if(!tegra::cvtBGR2RGB(src, dst, bidx)) #endif - CvtColorLoop(src, dst, RGB2RGB(scn, dcn, bidx)); + CvtColorLoop(src, dst, RGB2RGB(scn, dcn, bidx)); } else if( depth == CV_16U ) CvtColorLoop(src, dst, RGB2RGB(scn, dcn, bidx)); @@ -7435,58 +7967,10 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_8UC2); dst = _dst.getMat(); -#if defined(HAVE_IPP) && 0 // breaks OCL accuracy tests - CV_IPP_CHECK() - { - CV_SUPPRESS_DEPRECATED_START - - if (code == CV_BGR2BGR565 && scn == 3) - { - if (CvtColorIPPLoop(src, dst, IPPGeneralFunctor((ippiGeneralFunc)ippiBGRToBGR565_8u16u_C3R))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_BGRA2BGR565 && scn == 4) - { - if (CvtColorIPPLoopCopy(src, dst, - IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], - (ippiGeneralFunc)ippiBGRToBGR565_8u16u_C3R, 0, 1, 2, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_RGB2BGR565 && scn == 3) - { - if( CvtColorIPPLoopCopy(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], - (ippiGeneralFunc)ippiBGRToBGR565_8u16u_C3R, 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_RGBA2BGR565 && scn == 4) - { - if( CvtColorIPPLoopCopy(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], - (ippiGeneralFunc)ippiBGRToBGR565_8u16u_C3R, 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - CV_SUPPRESS_DEPRECATED_END - } -#endif #ifdef HAVE_TEGRA_OPTIMIZATION if(code == CV_BGR2BGR565 || code == CV_BGRA2BGR565 || code == CV_RGB2BGR565 || code == CV_RGBA2BGR565) - if(tegra::useTegra() && tegra::cvtRGB2RGB565(src, dst, code == CV_RGB2BGR565 || code == CV_RGBA2BGR565 ? 0 : 2)) + if(tegra::cvtRGB2RGB565(src, dst, code == CV_RGB2BGR565 || code == CV_RGBA2BGR565 ? 0 : 2)) break; #endif @@ -7505,53 +7989,6 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, dcn)); dst = _dst.getMat(); -#ifdef HAVE_IPP - CV_IPP_CHECK() - { - CV_SUPPRESS_DEPRECATED_START - if (code == CV_BGR5652BGR && dcn == 3) - { - if (CvtColorIPPLoop(src, dst, IPPGeneralFunctor((ippiGeneralFunc)ippiBGR565ToBGR_16u8u_C3R))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_BGR5652RGB && dcn == 3) - { - if (CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiBGR565ToBGR_16u8u_C3R, - ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_BGR5652BGRA && dcn == 4) - { - if (CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiBGR565ToBGR_16u8u_C3R, - ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_BGR5652RGBA && dcn == 4) - { - if (CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiBGR565ToBGR_16u8u_C3R, - ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - CV_SUPPRESS_DEPRECATED_END - } -#endif - CvtColorLoop(src, dst, RGB5x52RGB(dcn, code == CV_BGR5652BGR || code == CV_BGR5552BGR || code == CV_BGR5652BGRA || code == CV_BGR5552BGRA ? 0 : 2, // blue idx @@ -7565,55 +8002,13 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, 1)); dst = _dst.getMat(); -#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - CV_IPP_CHECK() - { - if( code == CV_BGR2GRAY && depth == CV_32F ) - { - if( CvtColorIPPLoop(src, dst, IPPColor2GrayFunctor(ippiColor2GrayC3Tab[depth])) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_RGB2GRAY && depth == CV_32F ) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralFunctor(ippiRGB2GrayC3Tab[depth])) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_BGRA2GRAY && depth == CV_32F ) - { - if( CvtColorIPPLoop(src, dst, IPPColor2GrayFunctor(ippiColor2GrayC4Tab[depth])) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_RGBA2GRAY && depth == CV_32F ) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralFunctor(ippiRGB2GrayC4Tab[depth])) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - } -#endif bidx = code == CV_BGR2GRAY || code == CV_BGRA2GRAY ? 0 : 2; if( depth == CV_8U ) { #ifdef HAVE_TEGRA_OPTIMIZATION - if(tegra::useTegra() && tegra::cvtRGB2Gray(src, dst, bidx)) - break; + if(!tegra::cvtRGB2Gray(src, dst, bidx)) #endif CvtColorLoop(src, dst, RGB2Gray(scn, bidx, 0)); } @@ -7637,36 +8032,11 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, dcn)); dst = _dst.getMat(); -#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - CV_IPP_CHECK() - { - if( code == CV_GRAY2BGR ) - { - if( CvtColorIPPLoop(src, dst, IPPGray2BGRFunctor(ippiCopyP3C3RTab[depth])) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_GRAY2BGRA ) - { - if( CvtColorIPPLoop(src, dst, IPPGray2BGRAFunctor(ippiCopyP3C3RTab[depth], ippiSwapChannelsC3C4RTab[depth], depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - } -#endif - if( depth == CV_8U ) { #ifdef HAVE_TEGRA_OPTIMIZATION - if(tegra::useTegra() && tegra::cvtGray2RGB(src, dst)) - break; + if(!tegra::cvtGray2RGB(src, dst)) #endif CvtColorLoop(src, dst, Gray2RGB(dcn)); } @@ -7697,55 +8067,10 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, 3)); dst = _dst.getMat(); -#if defined HAVE_IPP && 0 - CV_IPP_CHECK() - { - if (code == CV_RGB2YUV && scn == 3 && depth == CV_8U) - { - if (CvtColorIPPLoop(src, dst, IPPGeneralFunctor((ippiGeneralFunc)ippiRGBToYUV_8u_C3R))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_BGR2YUV && scn == 3 && depth == CV_8U) - { - if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], - (ippiGeneralFunc)ippiRGBToYUV_8u_C3R, 2, 1, 0, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_RGB2YUV && scn == 4 && depth == CV_8U) - { - if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], - (ippiGeneralFunc)ippiRGBToYUV_8u_C3R, 0, 1, 2, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_BGR2YUV && scn == 4 && depth == CV_8U) - { - if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], - (ippiGeneralFunc)ippiRGBToYUV_8u_C3R, 2, 1, 0, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - } -#endif - if( depth == CV_8U ) { #ifdef HAVE_TEGRA_OPTIMIZATION - if((code == CV_RGB2YCrCb || code == CV_BGR2YCrCb) && tegra::useTegra() && tegra::cvtRGB2YCrCb(src, dst, bidx)) + if((code == CV_RGB2YCrCb || code == CV_BGR2YCrCb) && tegra::cvtRGB2YCrCb(src, dst, bidx)) break; #endif CvtColorLoop(src, dst, RGB2YCrCb_i(scn, bidx, coeffs_i)); @@ -7771,50 +8096,6 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, dcn)); dst = _dst.getMat(); -#if defined HAVE_IPP && 0 - CV_IPP_CHECK() - { - if (code == CV_YUV2RGB && dcn == 3 && depth == CV_8U) - { - if (CvtColorIPPLoop(src, dst, IPPGeneralFunctor((ippiGeneralFunc)ippiYUVToRGB_8u_C3R))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_YUV2BGR && dcn == 3 && depth == CV_8U) - { - if (CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiYUVToRGB_8u_C3R, - ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_YUV2RGB && dcn == 4 && depth == CV_8U) - { - if (CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiYUVToRGB_8u_C3R, - ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_YUV2BGR && dcn == 4 && depth == CV_8U) - { - if (CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiYUVToRGB_8u_C3R, - ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - } -#endif if( depth == CV_8U ) CvtColorLoop(src, dst, YCrCb2RGB_i(dcn, bidx, coeffs_i)); @@ -7832,48 +8113,6 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, 3)); dst = _dst.getMat(); -#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - CV_IPP_CHECK() - { - if( code == CV_BGR2XYZ && scn == 3 && depth != CV_32F ) - { - if( CvtColorIPPLoopCopy(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], ippiRGB2XYZTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_BGR2XYZ && scn == 4 && depth != CV_32F ) - { - if( CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2XYZTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_RGB2XYZ && scn == 3 && depth != CV_32F ) - { - if( CvtColorIPPLoopCopy(src, dst, IPPGeneralFunctor(ippiRGB2XYZTab[depth])) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_RGB2XYZ && scn == 4 && depth != CV_32F ) - { - if( CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2XYZTab[depth], 0, 1, 2, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - } -#endif - if( depth == CV_8U ) CvtColorLoop(src, dst, RGB2XYZ_i(scn, bidx, 0)); else if( depth == CV_16U ) @@ -7890,48 +8129,6 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, dcn)); dst = _dst.getMat(); -#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - CV_IPP_CHECK() - { - if( code == CV_XYZ2BGR && dcn == 3 && depth != CV_32F ) - { - if( CvtColorIPPLoopCopy(src, dst, IPPGeneralReorderFunctor(ippiXYZ2RGBTab[depth], ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_XYZ2BGR && dcn == 4 && depth != CV_32F ) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiXYZ2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - if( code == CV_XYZ2RGB && dcn == 3 && depth != CV_32F ) - { - if( CvtColorIPPLoopCopy(src, dst, IPPGeneralFunctor(ippiXYZ2RGBTab[depth])) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_XYZ2RGB && dcn == 4 && depth != CV_32F ) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiXYZ2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - } -#endif - if( depth == CV_8U ) CvtColorLoop(src, dst, XYZ2RGB_i(dcn, bidx, 0)); else if( depth == CV_16U ) @@ -7952,94 +8149,12 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, 3)); dst = _dst.getMat(); -#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - CV_IPP_CHECK() - { - if( depth == CV_8U || depth == CV_16U ) - { -#if 0 // breaks OCL accuracy tests - if( code == CV_BGR2HSV_FULL && scn == 3 ) - { - if( CvtColorIPPLoopCopy(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], ippiRGB2HSVTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_BGR2HSV_FULL && scn == 4 ) - { - if( CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2HSVTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_RGB2HSV_FULL && scn == 4 ) - { - if( CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2HSVTab[depth], 0, 1, 2, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } else -#endif - if( code == CV_RGB2HSV_FULL && scn == 3 && depth == CV_16U ) - { - if( CvtColorIPPLoopCopy(src, dst, IPPGeneralFunctor(ippiRGB2HSVTab[depth])) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_BGR2HLS_FULL && scn == 3 ) - { - if( CvtColorIPPLoopCopy(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], ippiRGB2HLSTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_BGR2HLS_FULL && scn == 4 ) - { - if( CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2HLSTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_RGB2HLS_FULL && scn == 3 ) - { - if( CvtColorIPPLoopCopy(src, dst, IPPGeneralFunctor(ippiRGB2HLSTab[depth])) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_RGB2HLS_FULL && scn == 4 ) - { - if( CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], ippiRGB2HLSTab[depth], 0, 1, 2, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - } - } -#endif if( code == CV_BGR2HSV || code == CV_RGB2HSV || code == CV_BGR2HSV_FULL || code == CV_RGB2HSV_FULL ) { #ifdef HAVE_TEGRA_OPTIMIZATION - if(tegra::useTegra() && tegra::cvtRGB2HSV(src, dst, bidx, hrange)) + if(tegra::cvtRGB2HSV(src, dst, bidx, hrange)) break; #endif if( depth == CV_8U ) @@ -8070,86 +8185,6 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, dcn)); dst = _dst.getMat(); -#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - CV_IPP_CHECK() - { - if( depth == CV_8U || depth == CV_16U ) - { - if( code == CV_HSV2BGR_FULL && dcn == 3 ) - { - if( CvtColorIPPLoopCopy(src, dst, IPPGeneralReorderFunctor(ippiHSV2RGBTab[depth], ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_HSV2BGR_FULL && dcn == 4 ) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiHSV2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_HSV2RGB_FULL && dcn == 3 ) - { - if( CvtColorIPPLoopCopy(src, dst, IPPGeneralFunctor(ippiHSV2RGBTab[depth])) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_HSV2RGB_FULL && dcn == 4 ) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiHSV2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_HLS2BGR_FULL && dcn == 3 ) - { - if( CvtColorIPPLoopCopy(src, dst, IPPGeneralReorderFunctor(ippiHLS2RGBTab[depth], ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_HLS2BGR_FULL && dcn == 4 ) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiHLS2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_HLS2RGB_FULL && dcn == 3 ) - { - if( CvtColorIPPLoopCopy(src, dst, IPPGeneralFunctor(ippiHLS2RGBTab[depth])) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_HLS2RGB_FULL && dcn == 4 ) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiHLS2RGBTab[depth], ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - } - } -#endif if( code == CV_HSV2BGR || code == CV_HSV2RGB || code == CV_HSV2BGR_FULL || code == CV_HSV2RGB_FULL ) @@ -8181,90 +8216,6 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, 3)); dst = _dst.getMat(); -#if defined HAVE_IPP && 0 - CV_IPP_CHECK() - { - if (code == CV_LBGR2Lab && scn == 3 && depth == CV_8U) - { - if (CvtColorIPPLoop(src, dst, IPPGeneralFunctor((ippiGeneralFunc)ippiBGRToLab_8u_C3R))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_LBGR2Lab && scn == 4 && depth == CV_8U) - { - if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], - (ippiGeneralFunc)ippiBGRToLab_8u_C3R, 0, 1, 2, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else - if (code == CV_LRGB2Lab && scn == 3 && depth == CV_8U) // slower than OpenCV - { - if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], - (ippiGeneralFunc)ippiBGRToLab_8u_C3R, 2, 1, 0, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_LRGB2Lab && scn == 4 && depth == CV_8U) // slower than OpenCV - { - if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], - (ippiGeneralFunc)ippiBGRToLab_8u_C3R, 2, 1, 0, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_LRGB2Luv && scn == 3) - { - if (CvtColorIPPLoop(src, dst, IPPGeneralFunctor(ippiRGBToLUVTab[depth]))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_LRGB2Luv && scn == 4) - { - if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], - ippiRGBToLUVTab[depth], 0, 1, 2, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_LBGR2Luv && scn == 3) - { - if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC3RTab[depth], - ippiRGBToLUVTab[depth], 2, 1, 0, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if (code == CV_LBGR2Luv && scn == 4) - { - if (CvtColorIPPLoop(src, dst, IPPReorderGeneralFunctor(ippiSwapChannelsC4C3RTab[depth], - ippiRGBToLUVTab[depth], 2, 1, 0, depth))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - } -#endif if( code == CV_BGR2Lab || code == CV_RGB2Lab || code == CV_LBGR2Lab || code == CV_LRGB2Lab ) @@ -8297,82 +8248,6 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, dcn)); dst = _dst.getMat(); -#if defined HAVE_IPP && 0 - CV_IPP_CHECK() - { - if( code == CV_Lab2LBGR && dcn == 3 && depth == CV_8U) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralFunctor((ippiGeneralFunc)ippiLabToBGR_8u_C3R)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_Lab2LBGR && dcn == 4 && depth == CV_8U ) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiLabToBGR_8u_C3R, - ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - if( code == CV_Lab2LRGB && dcn == 3 && depth == CV_8U ) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiLabToBGR_8u_C3R, - ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - else if( code == CV_Lab2LRGB && dcn == 4 && depth == CV_8U ) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor((ippiGeneralFunc)ippiLabToBGR_8u_C3R, - ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - if( code == CV_Luv2LRGB && dcn == 3 ) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralFunctor(ippiLUVToRGBTab[depth])) ) - return; - } - else if( code == CV_Luv2LRGB && dcn == 4 ) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiLUVToRGBTab[depth], - ippiSwapChannelsC3C4RTab[depth], 0, 1, 2, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - } - if( code == CV_Luv2LBGR && dcn == 3 ) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiLUVToRGBTab[depth], - ippiSwapChannelsC3RTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - } - else if( code == CV_Luv2LBGR && dcn == 4 ) - { - if( CvtColorIPPLoop(src, dst, IPPGeneralReorderFunctor(ippiLUVToRGBTab[depth], - ippiSwapChannelsC3C4RTab[depth], 2, 1, 0, depth)) ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - } - } -#endif if( code == CV_Lab2BGR || code == CV_Lab2RGB || code == CV_Lab2LBGR || code == CV_Lab2LRGB ) @@ -8481,18 +8356,6 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) Size dstSz(sz.width, sz.height * 2 / 3); _dst.create(dstSz, CV_MAKETYPE(depth, dcn)); dst = _dst.getMat(); -#if defined HAVE_IPP - CV_IPP_CHECK() - { - if (ippStsNoErr == ippiCopy_8u_C1R(src.data, (int)src.step, dst.data, (int)dst.step, - ippiSize(dstSz.width, dstSz.height))) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } -#endif src(Range(0, dstSz.height), Range::all()).copyTo(dst); } break; @@ -8582,17 +8445,6 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) if( depth == CV_8U ) { -#if defined(HAVE_IPP) - CV_IPP_CHECK() - { - if (CvtColorIPPLoop(src, dst, IPPGeneralFunctor((ippiGeneralFunc)ippiAlphaPremul_8u_AC4R))) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } -#endif CvtColorLoop(src, dst, RGBA2mRGBA()); } else diff --git a/modules/imgproc/src/corner.cpp b/modules/imgproc/src/corner.cpp index 38f9676007..d9b3d943eb 100644 --- a/modules/imgproc/src/corner.cpp +++ b/modules/imgproc/src/corner.cpp @@ -523,16 +523,16 @@ static bool ocl_preCornerDetect( InputArray _src, OutputArray _dst, int ksize, i } -void cv::cornerMinEigenVal( InputArray _src, OutputArray _dst, int blockSize, int ksize, int borderType ) +#if defined(HAVE_IPP) +namespace cv { - CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(), - ocl_cornerMinEigenValVecs(_src, _dst, blockSize, ksize, 0.0, borderType, MINEIGENVAL)) - +static bool ipp_cornerMinEigenVal( InputArray _src, OutputArray _dst, int blockSize, int ksize, int borderType ) +{ +#if IPP_VERSION_MAJOR >= 8 Mat src = _src.getMat(); _dst.create( src.size(), CV_32FC1 ); Mat dst = _dst.getMat(); -#if defined(HAVE_IPP) && (IPP_VERSION_MAJOR >= 8) - CV_IPP_CHECK() + { typedef IppStatus (CV_STDCALL * ippiMinEigenValGetBufferSize)(IppiSize, int, int, int*); typedef IppStatus (CV_STDCALL * ippiMinEigenVal)(const void*, int, Ipp32f*, int, IppiSize, IppiKernelType, int, int, Ipp8u*); @@ -583,28 +583,57 @@ void cv::cornerMinEigenVal( InputArray _src, OutputArray _dst, int blockSize, in if (ok >= 0) { CV_IMPL_ADD(CV_IMPL_IPP); - return; + return true; } } - setIppErrorStatus(); } } } +#else + CV_UNUSED(_src); CV_UNUSED(_dst); CV_UNUSED(blockSize); CV_UNUSED(borderType); #endif - cornerEigenValsVecs( src, dst, blockSize, ksize, MINEIGENVAL, 0, borderType ); + return false; } +} +#endif -void cv::cornerHarris( InputArray _src, OutputArray _dst, int blockSize, int ksize, double k, int borderType ) +void cv::cornerMinEigenVal( InputArray _src, OutputArray _dst, int blockSize, int ksize, int borderType ) { CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(), - ocl_cornerMinEigenValVecs(_src, _dst, blockSize, ksize, k, borderType, HARRIS)) + ocl_cornerMinEigenValVecs(_src, _dst, blockSize, ksize, 0.0, borderType, MINEIGENVAL)) + +#ifdef HAVE_IPP + int kerSize = ksize; + if (ksize < 0) + { + kerSize = 3; + } + bool isolated = (borderType & BORDER_ISOLATED) != 0; + int borderTypeNI = borderType & ~BORDER_ISOLATED; +#endif + CV_IPP_RUN(((borderTypeNI == BORDER_REPLICATE && (!_src.isSubmatrix() || isolated)) && + (kerSize == 3 || kerSize == 5) && (blockSize == 3 || blockSize == 5)) && IPP_VERSION_MAJOR >= 8, + ipp_cornerMinEigenVal( _src, _dst, blockSize, ksize, borderType )); + Mat src = _src.getMat(); _dst.create( src.size(), CV_32FC1 ); Mat dst = _dst.getMat(); + cornerEigenValsVecs( src, dst, blockSize, ksize, MINEIGENVAL, 0, borderType ); +} + + +#if defined(HAVE_IPP) +namespace cv +{ +static bool ipp_cornerHarris( InputArray _src, OutputArray _dst, int blockSize, int ksize, double k, int borderType ) +{ #if IPP_VERSION_X100 >= 801 && 0 - CV_IPP_CHECK() + Mat src = _src.getMat(); + _dst.create( src.size(), CV_32FC1 ); + Mat dst = _dst.getMat(); + { int type = src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); int borderTypeNI = borderType & ~BORDER_ISOLATED; @@ -643,13 +672,37 @@ void cv::cornerHarris( InputArray _src, OutputArray _dst, int blockSize, int ksi if (status >= 0) { CV_IMPL_ADD(CV_IMPL_IPP); - return; + return true; } } - setIppErrorStatus(); } } +#else + CV_UNUSED(_src); CV_UNUSED(_dst); CV_UNUSED(blockSize); CV_UNUSED(ksize); CV_UNUSED(k); CV_UNUSED(borderType); #endif + return false; +} +} +#endif + +void cv::cornerHarris( InputArray _src, OutputArray _dst, int blockSize, int ksize, double k, int borderType ) +{ + CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(), + ocl_cornerMinEigenValVecs(_src, _dst, blockSize, ksize, k, borderType, HARRIS)) + +#ifdef HAVE_IPP + int borderTypeNI = borderType & ~BORDER_ISOLATED; + bool isolated = (borderType & BORDER_ISOLATED) != 0; +#endif + CV_IPP_RUN(((ksize == 3 || ksize == 5) && (_src.type() == CV_8UC1 || _src.type() == CV_32FC1) && + (borderTypeNI == BORDER_CONSTANT || borderTypeNI == BORDER_REPLICATE) && CV_MAT_CN(_src.type()) == 1 && + (!_src.isSubmatrix() || isolated)) && IPP_VERSION_X100 >= 801 && 0, ipp_cornerHarris( _src, _dst, blockSize, ksize, k, borderType )); + + + Mat src = _src.getMat(); + _dst.create( src.size(), CV_32FC1 ); + Mat dst = _dst.getMat(); + cornerEigenValsVecs( src, dst, blockSize, ksize, HARRIS, k, borderType ); } diff --git a/modules/imgproc/src/deriv.cpp b/modules/imgproc/src/deriv.cpp index d1e3a0b129..482f4d365b 100644 --- a/modules/imgproc/src/deriv.cpp +++ b/modules/imgproc/src/deriv.cpp @@ -547,7 +547,39 @@ static bool IPPDerivSobel(InputArray _src, OutputArray _dst, int ddepth, int dx, } return false; } - +#ifdef HAVE_IPP +static bool ipp_sobel(InputArray _src, OutputArray _dst, int ddepth, int dx, int dy, int ksize, double scale, double delta, int borderType) +{ + if (ksize < 0) + { + if (IPPDerivScharr(_src, _dst, ddepth, dx, dy, scale, delta, borderType)) + { + CV_IMPL_ADD(CV_IMPL_IPP); + return true; + } + } + else if (0 < ksize) + { + if (IPPDerivSobel(_src, _dst, ddepth, dx, dy, ksize, scale, delta, borderType)) + { + CV_IMPL_ADD(CV_IMPL_IPP); + return true; + } + } + return false; +} +static bool ipp_scharr(InputArray _src, OutputArray _dst, int ddepth, int dx, int dy, double scale, double delta, int borderType) +{ +#if IPP_VERSION_MAJOR >= 7 + if (IPPDerivScharr(_src, _dst, ddepth, dx, dy, scale, delta, borderType)) + { + CV_IMPL_ADD(CV_IMPL_IPP); + return true; + } +#endif + return false; +} +#endif } #endif @@ -572,27 +604,10 @@ void cv::Sobel( InputArray _src, OutputArray _dst, int ddepth, int dx, int dy, } #endif -#ifdef HAVE_IPP - CV_IPP_CHECK() - { - if (ksize < 0) - { - if (IPPDerivScharr(_src, _dst, ddepth, dx, dy, scale, delta, borderType)) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - } - else if (0 < ksize) - { - if (IPPDerivSobel(_src, _dst, ddepth, dx, dy, ksize, scale, delta, borderType)) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - } - } -#endif + + CV_IPP_RUN(true, ipp_sobel(_src, _dst, ddepth, dx, dy, ksize, scale, delta, borderType)); + + int ktype = std::max(CV_32F, std::max(ddepth, sdepth)); Mat kx, ky; @@ -628,16 +643,10 @@ void cv::Scharr( InputArray _src, OutputArray _dst, int ddepth, int dx, int dy, } #endif -#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - CV_IPP_CHECK() - { - if (IPPDerivScharr(_src, _dst, ddepth, dx, dy, scale, delta, borderType)) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - } -#endif + + CV_IPP_RUN(true, ipp_scharr(_src, _dst, ddepth, dx, dy, scale, delta, borderType)); + + int ktype = std::max(CV_32F, std::max(ddepth, sdepth)); Mat kx, ky; @@ -799,33 +808,30 @@ static bool ocl_Laplacian5(InputArray _src, OutputArray _dst, #endif -void cv::Laplacian( InputArray _src, OutputArray _dst, int ddepth, int ksize, - double scale, double delta, int borderType ) +#if defined(HAVE_IPP) +namespace cv +{ +static bool ipp_Laplacian(InputArray _src, OutputArray _dst, int ddepth, int ksize, + double scale, double delta, int borderType) { int stype = _src.type(), sdepth = CV_MAT_DEPTH(stype), cn = CV_MAT_CN(stype); if (ddepth < 0) ddepth = sdepth; _dst.create( _src.size(), CV_MAKETYPE(ddepth, cn) ); -#ifdef HAVE_IPP - CV_IPP_CHECK() - { - if ((ksize == 3 || ksize == 5) && ((borderType & BORDER_ISOLATED) != 0 || !_src.isSubmatrix()) && - ((stype == CV_8UC1 && ddepth == CV_16S) || (ddepth == CV_32F && stype == CV_32FC1)) && !ocl::useOpenCL()) - { - int iscale = saturate_cast(scale), idelta = saturate_cast(delta); - bool floatScale = std::fabs(scale - iscale) > DBL_EPSILON, needScale = iscale != 1; - bool floatDelta = std::fabs(delta - idelta) > DBL_EPSILON, needDelta = delta != 0; - int borderTypeNI = borderType & ~BORDER_ISOLATED; - Mat src = _src.getMat(), dst = _dst.getMat(); + int iscale = saturate_cast(scale), idelta = saturate_cast(delta); + bool floatScale = std::fabs(scale - iscale) > DBL_EPSILON, needScale = iscale != 1; + bool floatDelta = std::fabs(delta - idelta) > DBL_EPSILON, needDelta = delta != 0; + int borderTypeNI = borderType & ~BORDER_ISOLATED; + Mat src = _src.getMat(), dst = _dst.getMat(); - if (src.data != dst.data) - { - Ipp32s bufsize; - IppStatus status = (IppStatus)-1; - IppiSize roisize = { src.cols, src.rows }; - IppiMaskSize masksize = ksize == 3 ? ippMskSize3x3 : ippMskSize5x5; - IppiBorderType borderTypeIpp = ippiGetBorderType(borderTypeNI); + if (src.data != dst.data) + { + Ipp32s bufsize; + IppStatus status = (IppStatus)-1; + IppiSize roisize = { src.cols, src.rows }; + IppiMaskSize masksize = ksize == 3 ? ippMskSize3x3 : ippMskSize5x5; + IppiBorderType borderTypeIpp = ippiGetBorderType(borderTypeNI); #define IPP_FILTER_LAPLACIAN(ippsrctype, ippdsttype, ippfavor) \ do \ @@ -839,39 +845,51 @@ void cv::Laplacian( InputArray _src, OutputArray _dst, int ddepth, int ksize, } \ } while ((void)0, 0) - CV_SUPPRESS_DEPRECATED_START - if (sdepth == CV_8U && ddepth == CV_16S && !floatScale && !floatDelta) - { - IPP_FILTER_LAPLACIAN(Ipp8u, Ipp16s, 8u16s); + CV_SUPPRESS_DEPRECATED_START + if (sdepth == CV_8U && ddepth == CV_16S && !floatScale && !floatDelta) + { + IPP_FILTER_LAPLACIAN(Ipp8u, Ipp16s, 8u16s); - if (needScale && status >= 0) - status = ippiMulC_16s_C1IRSfs((Ipp16s)iscale, dst.ptr(), (int)dst.step, roisize, 0); - if (needDelta && status >= 0) - status = ippiAddC_16s_C1IRSfs((Ipp16s)idelta, dst.ptr(), (int)dst.step, roisize, 0); - } - else if (sdepth == CV_32F && ddepth == CV_32F) - { - IPP_FILTER_LAPLACIAN(Ipp32f, Ipp32f, 32f); - - if (needScale && status >= 0) - status = ippiMulC_32f_C1IR((Ipp32f)scale, dst.ptr(), (int)dst.step, roisize); - if (needDelta && status >= 0) - status = ippiAddC_32f_C1IR((Ipp32f)delta, dst.ptr(), (int)dst.step, roisize); - } - CV_SUPPRESS_DEPRECATED_END - - if (status >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } + if (needScale && status >= 0) + status = ippiMulC_16s_C1IRSfs((Ipp16s)iscale, dst.ptr(), (int)dst.step, roisize, 0); + if (needDelta && status >= 0) + status = ippiAddC_16s_C1IRSfs((Ipp16s)idelta, dst.ptr(), (int)dst.step, roisize, 0); } -#undef IPP_FILTER_LAPLACIAN + else if (sdepth == CV_32F && ddepth == CV_32F) + { + IPP_FILTER_LAPLACIAN(Ipp32f, Ipp32f, 32f); + + if (needScale && status >= 0) + status = ippiMulC_32f_C1IR((Ipp32f)scale, dst.ptr(), (int)dst.step, roisize); + if (needDelta && status >= 0) + status = ippiAddC_32f_C1IR((Ipp32f)delta, dst.ptr(), (int)dst.step, roisize); + } + CV_SUPPRESS_DEPRECATED_END + + if (status >= 0) + return true; } + +#undef IPP_FILTER_LAPLACIAN + return false; +} +} #endif + +void cv::Laplacian( InputArray _src, OutputArray _dst, int ddepth, int ksize, + double scale, double delta, int borderType ) +{ + int stype = _src.type(), sdepth = CV_MAT_DEPTH(stype), cn = CV_MAT_CN(stype); + if (ddepth < 0) + ddepth = sdepth; + _dst.create( _src.size(), CV_MAKETYPE(ddepth, cn) ); + + CV_IPP_RUN((ksize == 3 || ksize == 5) && ((borderType & BORDER_ISOLATED) != 0 || !_src.isSubmatrix()) && + ((stype == CV_8UC1 && ddepth == CV_16S) || (ddepth == CV_32F && stype == CV_32FC1)) && (!cv::ocl::useOpenCL()), + ipp_Laplacian(_src, _dst, ddepth, ksize, scale, delta, borderType)); + + #ifdef HAVE_TEGRA_OPTIMIZATION if (tegra::useTegra() && scale == 1.0 && delta == 0) { diff --git a/modules/imgproc/src/filter.cpp b/modules/imgproc/src/filter.cpp index f0b7ee79e5..10c8240a90 100644 --- a/modules/imgproc/src/filter.cpp +++ b/modules/imgproc/src/filter.cpp @@ -4555,6 +4555,96 @@ cv::Ptr cv::createLinearFilter( int _srcType, int _dstType, _rowBorderType, _columnBorderType, _borderValue ); } +#ifdef HAVE_IPP +namespace cv +{ +static bool ipp_filter2D( InputArray _src, OutputArray _dst, int ddepth, + InputArray _kernel, Point anchor0, + double delta, int borderType ) +{ +#if !HAVE_ICV + Mat src = _src.getMat(), kernel = _kernel.getMat(); + + if( ddepth < 0 ) + ddepth = src.depth(); + + _dst.create( src.size(), CV_MAKETYPE(ddepth, src.channels()) ); + Mat dst = _dst.getMat(); + Point anchor = normalizeAnchor(anchor0, kernel.size()); + + typedef IppStatus (CV_STDCALL * ippiFilterBorder)(const void * pSrc, int srcStep, void * pDst, int dstStep, IppiSize dstRoiSize, + IppiBorderType border, const void * borderValue, + const IppiFilterBorderSpec* pSpec, Ipp8u* pBuffer); + + int stype = src.type(), sdepth = CV_MAT_DEPTH(stype), cn = CV_MAT_CN(stype), + ktype = kernel.type(), kdepth = CV_MAT_DEPTH(ktype); + bool isolated = (borderType & BORDER_ISOLATED) != 0; + Point ippAnchor(kernel.cols >> 1, kernel.rows >> 1); + int borderTypeNI = borderType & ~BORDER_ISOLATED; + IppiBorderType ippBorderType = ippiGetBorderType(borderTypeNI); + + if (borderTypeNI == BORDER_CONSTANT || borderTypeNI == BORDER_REPLICATE) + { + ippiFilterBorder ippFunc = + stype == CV_8UC1 ? (ippiFilterBorder)ippiFilterBorder_8u_C1R : + stype == CV_8UC3 ? (ippiFilterBorder)ippiFilterBorder_8u_C3R : + stype == CV_8UC4 ? (ippiFilterBorder)ippiFilterBorder_8u_C4R : + stype == CV_16UC1 ? (ippiFilterBorder)ippiFilterBorder_16u_C1R : + stype == CV_16UC3 ? (ippiFilterBorder)ippiFilterBorder_16u_C3R : + stype == CV_16UC4 ? (ippiFilterBorder)ippiFilterBorder_16u_C4R : + stype == CV_16SC1 ? (ippiFilterBorder)ippiFilterBorder_16s_C1R : + stype == CV_16SC3 ? (ippiFilterBorder)ippiFilterBorder_16s_C3R : + stype == CV_16SC4 ? (ippiFilterBorder)ippiFilterBorder_16s_C4R : + stype == CV_32FC1 ? (ippiFilterBorder)ippiFilterBorder_32f_C1R : + stype == CV_32FC3 ? (ippiFilterBorder)ippiFilterBorder_32f_C3R : + stype == CV_32FC4 ? (ippiFilterBorder)ippiFilterBorder_32f_C4R : 0; + + if (sdepth == ddepth && (ktype == CV_16SC1 || ktype == CV_32FC1) && + ippFunc && (int)ippBorderType >= 0 && (!src.isSubmatrix() || isolated) && + std::fabs(delta - 0) < DBL_EPSILON && ippAnchor == anchor && dst.data != src.data) + { + IppiSize kernelSize = { kernel.cols, kernel.rows }, dstRoiSize = { dst.cols, dst.rows }; + IppDataType dataType = ippiGetDataType(ddepth), kernelType = ippiGetDataType(kdepth); + Ipp32s specSize = 0, bufsize = 0; + IppStatus status = (IppStatus)-1; + + if ((status = ippiFilterBorderGetSize(kernelSize, dstRoiSize, dataType, kernelType, cn, &specSize, &bufsize)) >= 0) + { + IppiFilterBorderSpec * spec = (IppiFilterBorderSpec *)ippMalloc(specSize); + Ipp8u * buffer = ippsMalloc_8u(bufsize); + Ipp32f borderValue[4] = { 0, 0, 0, 0 }; + + Mat reversedKernel; + flip(kernel, reversedKernel, -1); + + if ((kdepth == CV_32F && (status = ippiFilterBorderInit_32f((const Ipp32f *)reversedKernel.data, kernelSize, + dataType, cn, ippRndFinancial, spec)) >= 0 ) || + (kdepth == CV_16S && (status = ippiFilterBorderInit_16s((const Ipp16s *)reversedKernel.data, + kernelSize, 0, dataType, cn, ippRndFinancial, spec)) >= 0)) + { + status = ippFunc(src.data, (int)src.step, dst.data, (int)dst.step, dstRoiSize, + ippBorderType, borderValue, spec, buffer); + } + + ippsFree(buffer); + ippsFree(spec); + } + + if (status >= 0) + { + CV_IMPL_ADD(CV_IMPL_IPP); + return true; + } + } + } +#else + CV_UNUSED(_src); CV_UNUSED(_dst); CV_UNUSED(ddepth); CV_UNUSED(_kernel), CV_UNUSED(anchor0), CV_UNUSED(delta), CV_UNUSED(borderType); +#endif + return false; +} +} +#endif + void cv::filter2D( InputArray _src, OutputArray _dst, int ddepth, InputArray _kernel, Point anchor0, @@ -4579,77 +4669,8 @@ void cv::filter2D( InputArray _src, OutputArray _dst, int ddepth, Mat dst = _dst.getMat(); Point anchor = normalizeAnchor(anchor0, kernel.size()); -#if IPP_VERSION_X100 > 0 && !defined HAVE_IPP_ICV_ONLY - CV_IPP_CHECK() - { - typedef IppStatus (CV_STDCALL * ippiFilterBorder)(const void * pSrc, int srcStep, void * pDst, int dstStep, IppiSize dstRoiSize, - IppiBorderType border, const void * borderValue, - const IppiFilterBorderSpec* pSpec, Ipp8u* pBuffer); + CV_IPP_RUN(true, ipp_filter2D(_src, _dst, ddepth, _kernel, anchor0, delta, borderType)); - int stype = src.type(), sdepth = CV_MAT_DEPTH(stype), cn = CV_MAT_CN(stype), - ktype = kernel.type(), kdepth = CV_MAT_DEPTH(ktype); - bool isolated = (borderType & BORDER_ISOLATED) != 0; - Point ippAnchor(kernel.cols >> 1, kernel.rows >> 1); - int borderTypeNI = borderType & ~BORDER_ISOLATED; - IppiBorderType ippBorderType = ippiGetBorderType(borderTypeNI); - - if (borderTypeNI == BORDER_CONSTANT || borderTypeNI == BORDER_REPLICATE) - { - ippiFilterBorder ippFunc = - stype == CV_8UC1 ? (ippiFilterBorder)ippiFilterBorder_8u_C1R : - stype == CV_8UC3 ? (ippiFilterBorder)ippiFilterBorder_8u_C3R : - stype == CV_8UC4 ? (ippiFilterBorder)ippiFilterBorder_8u_C4R : - stype == CV_16UC1 ? (ippiFilterBorder)ippiFilterBorder_16u_C1R : - stype == CV_16UC3 ? (ippiFilterBorder)ippiFilterBorder_16u_C3R : - stype == CV_16UC4 ? (ippiFilterBorder)ippiFilterBorder_16u_C4R : - stype == CV_16SC1 ? (ippiFilterBorder)ippiFilterBorder_16s_C1R : - stype == CV_16SC3 ? (ippiFilterBorder)ippiFilterBorder_16s_C3R : - stype == CV_16SC4 ? (ippiFilterBorder)ippiFilterBorder_16s_C4R : - stype == CV_32FC1 ? (ippiFilterBorder)ippiFilterBorder_32f_C1R : - stype == CV_32FC3 ? (ippiFilterBorder)ippiFilterBorder_32f_C3R : - stype == CV_32FC4 ? (ippiFilterBorder)ippiFilterBorder_32f_C4R : 0; - - if (sdepth == ddepth && (ktype == CV_16SC1 || ktype == CV_32FC1) && - ippFunc && (int)ippBorderType >= 0 && (!src.isSubmatrix() || isolated) && - std::fabs(delta - 0) < DBL_EPSILON && ippAnchor == anchor && dst.data != src.data) - { - IppiSize kernelSize = { kernel.cols, kernel.rows }, dstRoiSize = { dst.cols, dst.rows }; - IppDataType dataType = ippiGetDataType(ddepth), kernelType = ippiGetDataType(kdepth); - Ipp32s specSize = 0, bufsize = 0; - IppStatus status = (IppStatus)-1; - - if ((status = ippiFilterBorderGetSize(kernelSize, dstRoiSize, dataType, kernelType, cn, &specSize, &bufsize)) >= 0) - { - IppiFilterBorderSpec * spec = (IppiFilterBorderSpec *)ippMalloc(specSize); - Ipp8u * buffer = ippsMalloc_8u(bufsize); - Ipp32f borderValue[4] = { 0, 0, 0, 0 }; - - Mat reversedKernel; - flip(kernel, reversedKernel, -1); - - if ((kdepth == CV_32F && (status = ippiFilterBorderInit_32f((const Ipp32f *)reversedKernel.data, kernelSize, - dataType, cn, ippRndFinancial, spec)) >= 0 ) || - (kdepth == CV_16S && (status = ippiFilterBorderInit_16s((const Ipp16s *)reversedKernel.data, - kernelSize, 0, dataType, cn, ippRndFinancial, spec)) >= 0)) - { - status = ippFunc(src.data, (int)src.step, dst.data, (int)dst.step, dstRoiSize, - ippBorderType, borderValue, spec, buffer); - } - - ippsFree(buffer); - ippsFree(spec); - } - - if (status >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - } - } -#endif #ifdef HAVE_TEGRA_OPTIMIZATION if( tegra::useTegra() && tegra::filter2D(src, dst, kernel, anchor, delta, borderType) ) diff --git a/modules/imgproc/src/histogram.cpp b/modules/imgproc/src/histogram.cpp index ec8de4d815..d9c5edfd4b 100644 --- a/modules/imgproc/src/histogram.cpp +++ b/modules/imgproc/src/histogram.cpp @@ -1220,7 +1220,10 @@ private: } -void cv::calcHist( const Mat* images, int nimages, const int* channels, +#if defined(HAVE_IPP) +namespace cv +{ +static bool ipp_calchist(const Mat* images, int nimages, const int* channels, InputArray _mask, OutputArray _hist, int dims, const int* histSize, const float** ranges, bool uniform, bool accumulate ) { @@ -1228,13 +1231,10 @@ void cv::calcHist( const Mat* images, int nimages, const int* channels, CV_Assert(dims > 0 && histSize); - const uchar* const histdata = _hist.getMat().ptr(); _hist.create(dims, histSize, CV_32F); Mat hist = _hist.getMat(), ihist = hist; ihist.flags = (ihist.flags & ~CV_MAT_TYPE_MASK)|CV_32S; -#ifdef HAVE_IPP - CV_IPP_CHECK() { if (nimages == 1 && images[0].type() == CV_8UC1 && dims == 1 && channels && channels[0] == 0 && mask.empty() && images[0].dims <= 2 && @@ -1256,14 +1256,37 @@ void cv::calcHist( const Mat* images, int nimages, const int* channels, if (ok) { ihist.convertTo(hist, CV_32F); - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; + CV_IMPL_ADD(CV_IMPL_IPP); + return true; } - setIppErrorStatus(); } } + return false; +} +} #endif +void cv::calcHist( const Mat* images, int nimages, const int* channels, + InputArray _mask, OutputArray _hist, int dims, const int* histSize, + const float** ranges, bool uniform, bool accumulate ) +{ + + CV_IPP_RUN(nimages == 1 && images[0].type() == CV_8UC1 && dims == 1 && channels && + channels[0] == 0 && _mask.getMat().empty() && images[0].dims <= 2 && + !accumulate && uniform, + ipp_calchist(images, nimages, channels, + _mask, _hist, dims, histSize, + ranges, uniform, accumulate)); + + Mat mask = _mask.getMat(); + + CV_Assert(dims > 0 && histSize); + + const uchar* const histdata = _hist.getMat().ptr(); + _hist.create(dims, histSize, CV_32F); + Mat hist = _hist.getMat(), ihist = hist; + ihist.flags = (ihist.flags & ~CV_MAT_TYPE_MASK)|CV_32S; + if( !accumulate || histdata != hist.data ) hist = Scalar(0.); else diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index 760f3fb0a2..a78043843f 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -3092,10 +3092,34 @@ static bool ocl_resize( InputArray _src, OutputArray _dst, Size dsize, #endif +#if IPP_VERSION_X100 >= 701 +static bool ipp_resize_mt( Mat src, Mat dst, + double inv_scale_x, double inv_scale_y, int interpolation) +{ + int mode = -1; + if (interpolation == INTER_LINEAR && src.rows >= 2 && src.cols >= 2) + mode = ippLinear; + else if (interpolation == INTER_CUBIC && src.rows >= 4 && src.cols >= 4) + mode = ippCubic; + else + return false; + + bool ok = true; + Range range(0, src.rows); + IPPresizeInvoker invoker(src, dst, inv_scale_x, inv_scale_y, mode, &ok); + parallel_for_(range, invoker, dst.total()/(double)(1<<16)); + if( ok ) + return true; + + return false; +} +#endif + } -////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////// void cv::resize( InputArray _src, OutputArray _dst, Size dsize, double inv_scale_x, double inv_scale_y, int interpolation ) { @@ -3219,6 +3243,17 @@ void cv::resize( InputArray _src, OutputArray _dst, Size dsize, inv_scale_y = (double)dsize.height/ssize.height; } + + int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); + double scale_x = 1./inv_scale_x, scale_y = 1./inv_scale_y; + + int iscale_x = saturate_cast(scale_x); + int iscale_y = saturate_cast(scale_y); + + bool is_area_fast = std::abs(scale_x - iscale_x) < DBL_EPSILON && + std::abs(scale_y - iscale_y) < DBL_EPSILON; + + CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat() && _src.cols() > 10 && _src.rows() > 10, ocl_resize(_src, _dst, dsize, inv_scale_x, inv_scale_y, interpolation)) @@ -3231,53 +3266,23 @@ void cv::resize( InputArray _src, OutputArray _dst, Size dsize, return; #endif - int type = src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); - double scale_x = 1./inv_scale_x, scale_y = 1./inv_scale_y; - int k, sx, sy, dx, dy; +#ifdef HAVE_IPP + int mode = -1; + if (interpolation == INTER_LINEAR && _src.rows() >= 2 && _src.cols() >= 2) + mode = INTER_LINEAR; + else if (interpolation == INTER_CUBIC && _src.rows() >= 4 && _src.cols() >= 4) + mode = INTER_CUBIC; - int iscale_x = saturate_cast(scale_x); - int iscale_y = saturate_cast(scale_y); - - bool is_area_fast = std::abs(scale_x - iscale_x) < DBL_EPSILON && - std::abs(scale_y - iscale_y) < DBL_EPSILON; - -#if IPP_VERSION_X100 >= 701 - CV_IPP_CHECK() - { -#define IPP_RESIZE_EPS 1e-10 - - double ex = fabs((double)dsize.width / src.cols - inv_scale_x) / inv_scale_x; - double ey = fabs((double)dsize.height / src.rows - inv_scale_y) / inv_scale_y; - - if ( ((ex < IPP_RESIZE_EPS && ey < IPP_RESIZE_EPS && depth != CV_64F) || (ex == 0 && ey == 0 && depth == CV_64F)) && - (interpolation == INTER_LINEAR || interpolation == INTER_CUBIC) && - !(interpolation == INTER_LINEAR && is_area_fast && iscale_x == 2 && iscale_y == 2 && depth == CV_8U)) - { - int mode = -1; - if (interpolation == INTER_LINEAR && src.rows >= 2 && src.cols >= 2) - mode = ippLinear; - else if (interpolation == INTER_CUBIC && src.rows >= 4 && src.cols >= 4) - mode = ippCubic; - - if( mode >= 0 && (cn == 1 || cn == 3 || cn == 4) && - (depth == CV_16U || depth == CV_16S || depth == CV_32F || - (depth == CV_64F && mode == ippLinear))) - { - bool ok = true; - Range range(0, src.rows); - IPPresizeInvoker invoker(src, dst, inv_scale_x, inv_scale_y, mode, &ok); - parallel_for_(range, invoker, dst.total()/(double)(1<<16)); - if( ok ) - { - CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT); - return; - } - setIppErrorStatus(); - } - } -#undef IPP_RESIZE_EPS - } + const double IPP_RESIZE_EPS = 1e-10; + double ex = fabs((double)dsize.width / _src.cols() - inv_scale_x) / inv_scale_x; + double ey = fabs((double)dsize.height / _src.rows() - inv_scale_y) / inv_scale_y; #endif + CV_IPP_RUN(IPP_VERSION_X100 >= 701 && ((ex < IPP_RESIZE_EPS && ey < IPP_RESIZE_EPS && depth != CV_64F) || (ex == 0 && ey == 0 && depth == CV_64F)) && + (interpolation == INTER_LINEAR || interpolation == INTER_CUBIC) && + !(interpolation == INTER_LINEAR && is_area_fast && iscale_x == 2 && iscale_y == 2 && depth == CV_8U) && + mode >= 0 && (cn == 1 || cn == 3 || cn == 4) && (depth == CV_16U || depth == CV_16S || depth == CV_32F || + (depth == CV_64F && mode == INTER_LINEAR)), ipp_resize_mt(src, dst, inv_scale_x, inv_scale_y, interpolation)) + if( interpolation == INTER_NEAREST ) { @@ -3285,6 +3290,9 @@ void cv::resize( InputArray _src, OutputArray _dst, Size dsize, return; } + int k, sx, sy, dx, dy; + + { // in case of scale_x && scale_y is equal to 2 // INTER_AREA (fast) also is equal to INTER_LINEAR @@ -3474,6 +3482,7 @@ void cv::resize( InputArray _src, OutputArray _dst, Size dsize, } + /****************************************************************************************\ * General warping (affine, perspective, remap) * \****************************************************************************************/ diff --git a/modules/imgproc/src/morph.cpp b/modules/imgproc/src/morph.cpp index f2d971bea3..f40ce1c40b 100644 --- a/modules/imgproc/src/morph.cpp +++ b/modules/imgproc/src/morph.cpp @@ -1711,16 +1711,9 @@ static void morphOp( int op, InputArray _src, OutputArray _dst, iterations = 1; } -#if IPP_VERSION_X100 >= 801 - CV_IPP_CHECK() - { - if( IPPMorphOp(op, _src, _dst, kernel, anchor, iterations, borderType, borderValue) ) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - } -#endif + + CV_IPP_RUN(IPP_VERSION_X100 >= 801, IPPMorphOp(op, _src, _dst, kernel, anchor, iterations, borderType, borderValue)) + Mat src = _src.getMat(); _dst.create( src.size(), src.type() ); diff --git a/modules/imgproc/src/pyramids.cpp b/modules/imgproc/src/pyramids.cpp index 4018e08f70..1d8c943333 100644 --- a/modules/imgproc/src/pyramids.cpp +++ b/modules/imgproc/src/pyramids.cpp @@ -1166,103 +1166,22 @@ static bool ocl_pyrUp( InputArray _src, OutputArray _dst, const Size& _dsz, int } -void cv::pyrDown( InputArray _src, OutputArray _dst, const Size& _dsz, int borderType ) +#if defined(HAVE_IPP) +namespace cv { - CV_Assert(borderType != BORDER_CONSTANT); - - CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(), - ocl_pyrDown(_src, _dst, _dsz, borderType)) +static bool ipp_pyrdown( InputArray _src, OutputArray _dst, const Size& _dsz, int borderType ) +{ +#if IPP_VERSION_X100 >= 801 && 0 + Size dsz = _dsz.area() == 0 ? Size((_src.cols() + 1)/2, (_src.rows() + 1)/2) : _dsz; + bool isolated = (borderType & BORDER_ISOLATED) != 0; + int borderTypeNI = borderType & ~BORDER_ISOLATED; Mat src = _src.getMat(); - Size dsz = _dsz.area() == 0 ? Size((src.cols + 1)/2, (src.rows + 1)/2) : _dsz; _dst.create( dsz, src.type() ); Mat dst = _dst.getMat(); int depth = src.depth(); -#ifdef HAVE_TEGRA_OPTIMIZATION - if(borderType == BORDER_DEFAULT && tegra::useTegra() && tegra::pyrDown(src, dst)) - return; -#endif -#if IPP_VERSION_X100 >= 801 && 0 - CV_IPP_CHECK() - { - bool isolated = (borderType & BORDER_ISOLATED) != 0; - int borderTypeNI = borderType & ~BORDER_ISOLATED; - if (borderTypeNI == BORDER_DEFAULT && (!src.isSubmatrix() || isolated) && dsz == Size((src.cols + 1)/2, (src.rows + 1)/2)) - { - typedef IppStatus (CV_STDCALL * ippiPyrDown)(const void* pSrc, int srcStep, void* pDst, int dstStep, IppiSize srcRoi, Ipp8u* buffer); - int type = src.type(); - CV_SUPPRESS_DEPRECATED_START - ippiPyrDown pyrDownFunc = type == CV_8UC1 ? (ippiPyrDown) ippiPyrDown_Gauss5x5_8u_C1R : - type == CV_8UC3 ? (ippiPyrDown) ippiPyrDown_Gauss5x5_8u_C3R : - type == CV_32FC1 ? (ippiPyrDown) ippiPyrDown_Gauss5x5_32f_C1R : - type == CV_32FC3 ? (ippiPyrDown) ippiPyrDown_Gauss5x5_32f_C3R : 0; - CV_SUPPRESS_DEPRECATED_END - - if (pyrDownFunc) - { - int bufferSize; - IppiSize srcRoi = { src.cols, src.rows }; - IppDataType dataType = depth == CV_8U ? ipp8u : ipp32f; - CV_SUPPRESS_DEPRECATED_START - IppStatus ok = ippiPyrDownGetBufSize_Gauss5x5(srcRoi.width, dataType, src.channels(), &bufferSize); - CV_SUPPRESS_DEPRECATED_END - if (ok >= 0) - { - Ipp8u* buffer = ippsMalloc_8u(bufferSize); - ok = pyrDownFunc(src.data, (int) src.step, dst.data, (int) dst.step, srcRoi, buffer); - ippsFree(buffer); - - if (ok >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - } - } - } -#endif - - PyrFunc func = 0; - if( depth == CV_8U ) - func = pyrDown_, PyrDownVec_32s8u>; - else if( depth == CV_16S ) - func = pyrDown_, PyrDownVec_32s16s >; - else if( depth == CV_16U ) - func = pyrDown_, PyrDownVec_32s16u >; - else if( depth == CV_32F ) - func = pyrDown_, PyrDownVec_32f>; - else if( depth == CV_64F ) - func = pyrDown_, PyrDownNoVec >; - else - CV_Error( CV_StsUnsupportedFormat, "" ); - - func( src, dst, borderType ); -} - -void cv::pyrUp( InputArray _src, OutputArray _dst, const Size& _dsz, int borderType ) -{ - CV_Assert(borderType == BORDER_DEFAULT); - - CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(), - ocl_pyrUp(_src, _dst, _dsz, borderType)) - - Mat src = _src.getMat(); - Size dsz = _dsz.area() == 0 ? Size(src.cols*2, src.rows*2) : _dsz; - _dst.create( dsz, src.type() ); - Mat dst = _dst.getMat(); - int depth = src.depth(); - -#ifdef HAVE_TEGRA_OPTIMIZATION - if(borderType == BORDER_DEFAULT && tegra::useTegra() && tegra::pyrUp(src, dst)) - return; -#endif - -#if IPP_VERSION_X100 >= 801 && 0 - CV_IPP_CHECK() { bool isolated = (borderType & BORDER_ISOLATED) != 0; int borderTypeNI = borderType & ~BORDER_ISOLATED; @@ -1294,14 +1213,149 @@ void cv::pyrUp( InputArray _src, OutputArray _dst, const Size& _dsz, int borderT if (ok >= 0) { CV_IMPL_ADD(CV_IMPL_IPP); - return; + return true; } - setIppErrorStatus(); } } } } +#else + CV_UNUSED(_src); CV_UNUSED(_dst); CV_UNUSED(_dsz); CV_UNUSED(borderType); #endif + return false; +} +} +#endif + +void cv::pyrDown( InputArray _src, OutputArray _dst, const Size& _dsz, int borderType ) +{ + CV_Assert(borderType != BORDER_CONSTANT); + + CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(), + ocl_pyrDown(_src, _dst, _dsz, borderType)) + + Mat src = _src.getMat(); + Size dsz = _dsz.area() == 0 ? Size((src.cols + 1)/2, (src.rows + 1)/2) : _dsz; + _dst.create( dsz, src.type() ); + Mat dst = _dst.getMat(); + int depth = src.depth(); + +#ifdef HAVE_TEGRA_OPTIMIZATION + if(borderType == BORDER_DEFAULT && tegra::useTegra() && tegra::pyrDown(src, dst)) + return; +#endif + +#ifdef HAVE_IPP + bool isolated = (borderType & BORDER_ISOLATED) != 0; + int borderTypeNI = borderType & ~BORDER_ISOLATED; +#endif + CV_IPP_RUN(borderTypeNI == BORDER_DEFAULT && (!_src.isSubmatrix() || isolated) && dsz == Size((_src.cols() + 1)/2, (_src.rows() + 1)/2), + ipp_pyrdown( _src, _dst, _dsz, borderType)); + + + PyrFunc func = 0; + if( depth == CV_8U ) + func = pyrDown_, PyrDownVec_32s8u>; + else if( depth == CV_16S ) + func = pyrDown_, PyrDownVec_32s16s >; + else if( depth == CV_16U ) + func = pyrDown_, PyrDownVec_32s16u >; + else if( depth == CV_32F ) + func = pyrDown_, PyrDownVec_32f>; + else if( depth == CV_64F ) + func = pyrDown_, PyrDownNoVec >; + else + CV_Error( CV_StsUnsupportedFormat, "" ); + + func( src, dst, borderType ); +} + + +#if defined(HAVE_IPP) +namespace cv +{ +static bool ipp_pyrup( InputArray _src, OutputArray _dst, const Size& _dsz, int borderType ) +{ +#if IPP_VERSION_X100 >= 801 && 0 + Size sz = _src.dims() <= 2 ? _src.size() : Size(); + Size dsz = _dsz.area() == 0 ? Size(_src.cols()*2, _src.rows()*2) : _dsz; + + Mat src = _src.getMat(); + _dst.create( dsz, src.type() ); + Mat dst = _dst.getMat(); + int depth = src.depth(); + + { + bool isolated = (borderType & BORDER_ISOLATED) != 0; + int borderTypeNI = borderType & ~BORDER_ISOLATED; + if (borderTypeNI == BORDER_DEFAULT && (!src.isSubmatrix() || isolated) && dsz == Size(src.cols*2, src.rows*2)) + { + typedef IppStatus (CV_STDCALL * ippiPyrUp)(const void* pSrc, int srcStep, void* pDst, int dstStep, IppiSize srcRoi, Ipp8u* buffer); + int type = src.type(); + CV_SUPPRESS_DEPRECATED_START + ippiPyrUp pyrUpFunc = type == CV_8UC1 ? (ippiPyrUp) ippiPyrUp_Gauss5x5_8u_C1R : + type == CV_8UC3 ? (ippiPyrUp) ippiPyrUp_Gauss5x5_8u_C3R : + type == CV_32FC1 ? (ippiPyrUp) ippiPyrUp_Gauss5x5_32f_C1R : + type == CV_32FC3 ? (ippiPyrUp) ippiPyrUp_Gauss5x5_32f_C3R : 0; + CV_SUPPRESS_DEPRECATED_END + + if (pyrUpFunc) + { + int bufferSize; + IppiSize srcRoi = { src.cols, src.rows }; + IppDataType dataType = depth == CV_8U ? ipp8u : ipp32f; + CV_SUPPRESS_DEPRECATED_START + IppStatus ok = ippiPyrUpGetBufSize_Gauss5x5(srcRoi.width, dataType, src.channels(), &bufferSize); + CV_SUPPRESS_DEPRECATED_END + if (ok >= 0) + { + Ipp8u* buffer = ippsMalloc_8u(bufferSize); + ok = pyrUpFunc(src.data, (int) src.step, dst.data, (int) dst.step, srcRoi, buffer); + ippsFree(buffer); + + if (ok >= 0) + { + CV_IMPL_ADD(CV_IMPL_IPP); + return true; + } + } + } + } + } +#else + CV_UNUSED(_src); CV_UNUSED(_dst); CV_UNUSED(_dsz); CV_UNUSED(borderType); +#endif + return false; +} +} +#endif + +void cv::pyrUp( InputArray _src, OutputArray _dst, const Size& _dsz, int borderType ) +{ + CV_Assert(borderType == BORDER_DEFAULT); + + CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(), + ocl_pyrUp(_src, _dst, _dsz, borderType)) + + + Mat src = _src.getMat(); + Size dsz = _dsz.area() == 0 ? Size(src.cols*2, src.rows*2) : _dsz; + _dst.create( dsz, src.type() ); + Mat dst = _dst.getMat(); + int depth = src.depth(); + +#ifdef HAVE_TEGRA_OPTIMIZATION + if(borderType == BORDER_DEFAULT && tegra::useTegra() && tegra::pyrUp(src, dst)) + return; +#endif + +#ifdef HAVE_IPP + bool isolated = (borderType & BORDER_ISOLATED) != 0; + int borderTypeNI = borderType & ~BORDER_ISOLATED; +#endif + CV_IPP_RUN(borderTypeNI == BORDER_DEFAULT && (!_src.isSubmatrix() || isolated) && dsz == Size(_src.cols()*2, _src.rows()*2), + ipp_pyrup( _src, _dst, _dsz, borderType)); + PyrFunc func = 0; if( depth == CV_8U ) @@ -1320,28 +1374,19 @@ void cv::pyrUp( InputArray _src, OutputArray _dst, const Size& _dsz, int borderT func( src, dst, borderType ); } -void cv::buildPyramid( InputArray _src, OutputArrayOfArrays _dst, int maxlevel, int borderType ) + +#if 0 //#ifdef HAVE_IPP +namespace cv { - CV_Assert(borderType != BORDER_CONSTANT); - - if (_src.dims() <= 2 && _dst.isUMatVector()) - { - UMat src = _src.getUMat(); - _dst.create( maxlevel + 1, 1, 0 ); - _dst.getUMatRef(0) = src; - for( int i = 1; i <= maxlevel; i++ ) - pyrDown( _dst.getUMatRef(i-1), _dst.getUMatRef(i), Size(), borderType ); - return; - } - +static bool ipp_buildpyramid( InputArray _src, OutputArrayOfArrays _dst, int maxlevel, int borderType ) +{ +#if IPP_VERSION_X100 >= 801 && 0 Mat src = _src.getMat(); _dst.create( maxlevel + 1, 1, 0 ); _dst.getMatRef(0) = src; int i=1; -#if IPP_VERSION_X100 >= 801 && 0 - CV_IPP_CHECK() { bool isolated = (borderType & BORDER_ISOLATED) != 0; int borderTypeNI = borderType & ~BORDER_ISOLATED; @@ -1414,8 +1459,8 @@ void cv::buildPyramid( InputArray _src, OutputArrayOfArrays _dst, int maxlevel, if (ok < 0) { - setIppErrorStatus(); - break; + pyrFreeFunc(gPyr->pState); + return false; } else { @@ -1425,13 +1470,52 @@ void cv::buildPyramid( InputArray _src, OutputArrayOfArrays _dst, int maxlevel, pyrFreeFunc(gPyr->pState); } else - setIppErrorStatus(); - + { + ippiPyramidFree(gPyr); + return false; + } ippiPyramidFree(gPyr); } + return true; } + return false; } +#else + CV_UNUSED(_src); CV_UNUSED(_dst); CV_UNUSED(maxlevel); CV_UNUSED(borderType); #endif + return false; +} +} +#endif + +void cv::buildPyramid( InputArray _src, OutputArrayOfArrays _dst, int maxlevel, int borderType ) +{ + CV_Assert(borderType != BORDER_CONSTANT); + + if (_src.dims() <= 2 && _dst.isUMatVector()) + { + UMat src = _src.getUMat(); + _dst.create( maxlevel + 1, 1, 0 ); + _dst.getUMatRef(0) = src; + for( int i = 1; i <= maxlevel; i++ ) + pyrDown( _dst.getUMatRef(i-1), _dst.getUMatRef(i), Size(), borderType ); + return; + } + + Mat src = _src.getMat(); + _dst.create( maxlevel + 1, 1, 0 ); + _dst.getMatRef(0) = src; + + int i=1; + +#if (IPP_VERSION_X100 >= 801 && 0) + bool isolated = (borderType & BORDER_ISOLATED) != 0; + int borderTypeNI = borderType & ~BORDER_ISOLATED; + CV_IPP_RUN(((IPP_VERSION_X100 >= 801 && 0) && (borderTypeNI == BORDER_DEFAULT && (!_src.isSubmatrix() || isolated))), + ipp_buildpyramid( _src, _dst, maxlevel, borderType)); +#endif + + for( ; i <= maxlevel; i++ ) pyrDown( _dst.getMatRef(i-1), _dst.getMatRef(i), Size(), borderType ); } diff --git a/modules/imgproc/src/smooth.cpp b/modules/imgproc/src/smooth.cpp index dbe8a6315a..012b90418e 100644 --- a/modules/imgproc/src/smooth.cpp +++ b/modules/imgproc/src/smooth.cpp @@ -1303,17 +1303,24 @@ cv::Ptr cv::createBoxFilter( int srcType, int dstType, Size ks srcType, dstType, sumType, borderType ); } - -void cv::boxFilter( InputArray _src, OutputArray _dst, int ddepth, +#if defined(HAVE_IPP) +namespace cv +{ +static bool ipp_boxfilter( InputArray _src, OutputArray _dst, int ddepth, Size ksize, Point anchor, bool normalize, int borderType ) { - CV_OCL_RUN(_dst.isUMat(), ocl_boxFilter(_src, _dst, ddepth, ksize, anchor, borderType, normalize)) - - Mat src = _src.getMat(); - int stype = src.type(), sdepth = CV_MAT_DEPTH(stype), cn = CV_MAT_CN(stype); + int stype = _src.type(), sdepth = CV_MAT_DEPTH(stype), cn = CV_MAT_CN(stype); if( ddepth < 0 ) ddepth = sdepth; + int ippBorderType = borderType & ~BORDER_ISOLATED; + Point ocvAnchor, ippAnchor; + ocvAnchor.x = anchor.x < 0 ? ksize.width / 2 : anchor.x; + ocvAnchor.y = anchor.y < 0 ? ksize.height / 2 : anchor.y; + ippAnchor.x = ksize.width / 2 - (ksize.width % 2 == 0 ? 1 : 0); + ippAnchor.y = ksize.height / 2 - (ksize.height % 2 == 0 ? 1 : 0); + + Mat src = _src.getMat(); _dst.create( src.size(), CV_MAKETYPE(ddepth, cn) ); Mat dst = _dst.getMat(); if( borderType != BORDER_CONSTANT && normalize && (borderType & BORDER_ISOLATED) != 0 ) @@ -1323,21 +1330,8 @@ void cv::boxFilter( InputArray _src, OutputArray _dst, int ddepth, if( src.cols == 1 ) ksize.width = 1; } -#ifdef HAVE_TEGRA_OPTIMIZATION - if ( tegra::useTegra() && tegra::box(src, dst, ksize, anchor, normalize, borderType) ) - return; -#endif -#if defined(HAVE_IPP) - CV_IPP_CHECK() { - int ippBorderType = borderType & ~BORDER_ISOLATED; - Point ocvAnchor, ippAnchor; - ocvAnchor.x = anchor.x < 0 ? ksize.width / 2 : anchor.x; - ocvAnchor.y = anchor.y < 0 ? ksize.height / 2 : anchor.y; - ippAnchor.x = ksize.width / 2 - (ksize.width % 2 == 0 ? 1 : 0); - ippAnchor.y = ksize.height / 2 - (ksize.height % 2 == 0 ? 1 : 0); - if (normalize && !src.isSubmatrix() && ddepth == sdepth && (/*ippBorderType == BORDER_REPLICATE ||*/ /* returns ippStsStepErr: Step value is not valid */ ippBorderType == BORDER_CONSTANT) && ocvAnchor == ippAnchor && @@ -1361,10 +1355,9 @@ void cv::boxFilter( InputArray _src, OutputArray _dst, int ddepth, if (status >= 0) \ { \ CV_IMPL_ADD(CV_IMPL_IPP); \ - return; \ + return true; \ } \ } \ - setIppErrorStatus(); \ } while ((void)0, 0) if (stype == CV_8UC1) @@ -1399,13 +1392,57 @@ void cv::boxFilter( InputArray _src, OutputArray _dst, int ddepth, } #undef IPP_FILTER_BOX_BORDER } + return false; +} +} #endif + +void cv::boxFilter( InputArray _src, OutputArray _dst, int ddepth, + Size ksize, Point anchor, + bool normalize, int borderType ) +{ + CV_OCL_RUN(_dst.isUMat(), ocl_boxFilter(_src, _dst, ddepth, ksize, anchor, borderType, normalize)) + + Mat src = _src.getMat(); + int stype = src.type(), sdepth = CV_MAT_DEPTH(stype), cn = CV_MAT_CN(stype); + if( ddepth < 0 ) + ddepth = sdepth; + _dst.create( src.size(), CV_MAKETYPE(ddepth, cn) ); + Mat dst = _dst.getMat(); + if( borderType != BORDER_CONSTANT && normalize && (borderType & BORDER_ISOLATED) != 0 ) + { + if( src.rows == 1 ) + ksize.height = 1; + if( src.cols == 1 ) + ksize.width = 1; + } +#ifdef HAVE_TEGRA_OPTIMIZATION + if ( tegra::useTegra() && tegra::box(src, dst, ksize, anchor, normalize, borderType) ) + return; +#endif + +#ifdef HAVE_IPP + int ippBorderType = borderType & ~BORDER_ISOLATED; +#endif + Point ocvAnchor, ippAnchor; + ocvAnchor.x = anchor.x < 0 ? ksize.width / 2 : anchor.x; + ocvAnchor.y = anchor.y < 0 ? ksize.height / 2 : anchor.y; + ippAnchor.x = ksize.width / 2 - (ksize.width % 2 == 0 ? 1 : 0); + ippAnchor.y = ksize.height / 2 - (ksize.height % 2 == 0 ? 1 : 0); + CV_IPP_RUN((normalize && !_src.isSubmatrix() && ddepth == sdepth && + (/*ippBorderType == BORDER_REPLICATE ||*/ /* returns ippStsStepErr: Step value is not valid */ + ippBorderType == BORDER_CONSTANT) && ocvAnchor == ippAnchor && + _dst.cols() != ksize.width && _dst.rows() != ksize.height), + ipp_boxfilter( _src, _dst, ddepth, ksize, anchor, normalize, borderType)); + + Ptr f = createBoxFilter( src.type(), dst.type(), ksize, anchor, normalize, borderType ); f->apply( src, dst ); } + void cv::blur( InputArray src, OutputArray dst, Size ksize, Point anchor, int borderType ) { @@ -1624,6 +1661,103 @@ cv::Ptr cv::createGaussianFilter( int type, Size ksize, return createSeparableLinearFilter( type, type, kx, ky, Point(-1,-1), 0, borderType ); } +#ifdef HAVE_IPP +namespace cv +{ +static bool ipp_GaussianBlur( InputArray _src, OutputArray _dst, Size ksize, + double sigma1, double sigma2, + int borderType ) +{ + int type = _src.type(); + Size size = _src.size(); + + if( borderType != BORDER_CONSTANT && (borderType & BORDER_ISOLATED) != 0 ) + { + if( size.height == 1 ) + ksize.height = 1; + if( size.width == 1 ) + ksize.width = 1; + } + + int depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); + + if ((depth == CV_8U || depth == CV_16U || depth == CV_16S || depth == CV_32F) && (cn == 1 || cn == 3) && + sigma1 == sigma2 && ksize.width == ksize.height && sigma1 != 0.0 ) + { + IppiBorderType ippBorder = ippiGetBorderType(borderType); + if (ippBorderConst == ippBorder || ippBorderRepl == ippBorder) + { + Mat src = _src.getMat(), dst = _dst.getMat(); + IppiSize roiSize = { src.cols, src.rows }; + IppDataType dataType = ippiGetDataType(depth); + Ipp32s specSize = 0, bufferSize = 0; + + if (ippiFilterGaussianGetBufferSize(roiSize, (Ipp32u)ksize.width, dataType, cn, &specSize, &bufferSize) >= 0) + { + IppFilterGaussianSpec * pSpec = (IppFilterGaussianSpec *)ippMalloc(specSize); + Ipp8u * pBuffer = (Ipp8u*)ippMalloc(bufferSize); + + if (ippiFilterGaussianInit(roiSize, (Ipp32u)ksize.width, (Ipp32f)sigma1, ippBorder, dataType, 1, pSpec, pBuffer) >= 0) + { +#define IPP_FILTER_GAUSS_C1(ippfavor) \ + { \ + typedef Ipp##ippfavor ippType; \ + ippType borderValues = 0; \ + status = ippiFilterGaussianBorder_##ippfavor##_C1R(src.ptr(), (int)src.step, \ + dst.ptr(), (int)dst.step, roiSize, borderValues, pSpec, pBuffer); \ + } + +#define IPP_FILTER_GAUSS_CN(ippfavor, ippcn) \ + { \ + typedef Ipp##ippfavor ippType; \ + ippType borderValues[] = { 0, 0, 0 }; \ + status = ippiFilterGaussianBorder_##ippfavor##_C##ippcn##R(src.ptr(), (int)src.step, \ + dst.ptr(), (int)dst.step, roiSize, borderValues, pSpec, pBuffer); \ + } + + IppStatus status = ippStsErr; +#if !HAVE_ICV + if (type == CV_8UC1) + IPP_FILTER_GAUSS_C1(8u) + else if (type == CV_8UC3) + IPP_FILTER_GAUSS_CN(8u, 3) + else if (type == CV_16UC1) + IPP_FILTER_GAUSS_C1(16u) + else if (type == CV_16UC3) + IPP_FILTER_GAUSS_CN(16u, 3) + else if (type == CV_16SC1) + IPP_FILTER_GAUSS_C1(16s) + else if (type == CV_16SC3) + IPP_FILTER_GAUSS_CN(16s, 3) + else if (type == CV_32FC3) + IPP_FILTER_GAUSS_CN(32f, 3) + else +#endif + if (type == CV_32FC1) + IPP_FILTER_GAUSS_C1(32f) + + if (pSpec) + ippFree(pSpec); + if (pBuffer) + ippFree(pBuffer); + + if(status >= 0) + { + CV_IMPL_ADD(CV_IMPL_IPP); + return true; + } + +#undef IPP_FILTER_GAUSS_C1 +#undef IPP_FILTER_GAUSS_CN + } + } + } + } + return false; +} +} +#endif + void cv::GaussianBlur( InputArray _src, OutputArray _dst, Size ksize, double sigma1, double sigma2, @@ -1654,72 +1788,9 @@ void cv::GaussianBlur( InputArray _src, OutputArray _dst, Size ksize, return; #endif -#if IPP_VERSION_X100 >= 801 && 0 // these functions are slower in IPP 8.1 - CV_IPP_CHECK() - { - int depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); - if ((depth == CV_8U || depth == CV_16U || depth == CV_16S || depth == CV_32F) && (cn == 1 || cn == 3) && - sigma1 == sigma2 && ksize.width == ksize.height && sigma1 != 0.0 ) - { - IppiBorderType ippBorder = ippiGetBorderType(borderType); - if (ippBorderConst == ippBorder || ippBorderRepl == ippBorder) - { - Mat src = _src.getMat(), dst = _dst.getMat(); - IppiSize roiSize = { src.cols, src.rows }; - IppDataType dataType = ippiGetDataType(depth); - Ipp32s specSize = 0, bufferSize = 0; + CV_IPP_RUN(true, ipp_GaussianBlur( _src, _dst, ksize, sigma1, sigma2, borderType)); - if (ippiFilterGaussianGetBufferSize(roiSize, (Ipp32u)ksize.width, dataType, cn, &specSize, &bufferSize) >= 0) - { - IppFilterGaussianSpec * pSpec = (IppFilterGaussianSpec *)ippMalloc(specSize); - Ipp8u * pBuffer = (Ipp8u*)ippMalloc(bufferSize); - - if (ippiFilterGaussianInit(roiSize, (Ipp32u)ksize.width, (Ipp32f)sigma1, ippBorder, dataType, 1, pSpec, pBuffer) >= 0) - { -#define IPP_FILTER_GAUSS(ippfavor, ippcn) \ - do \ - { \ - typedef Ipp##ippfavor ippType; \ - ippType borderValues[] = { 0, 0, 0 }; \ - IppStatus status = ippcn == 1 ? \ - ippiFilterGaussianBorder_##ippfavor##_C1R(src.ptr(), (int)src.step, \ - dst.ptr(), (int)dst.step, roiSize, borderValues[0], pSpec, pBuffer) : \ - ippiFilterGaussianBorder_##ippfavor##_C3R(src.ptr(), (int)src.step, \ - dst.ptr(), (int)dst.step, roiSize, borderValues, pSpec, pBuffer); \ - ippFree(pBuffer); \ - ippFree(pSpec); \ - if (status >= 0) \ - { \ - CV_IMPL_ADD(CV_IMPL_IPP); \ - return; \ - } \ - } while ((void)0, 0) - - if (type == CV_8UC1) - IPP_FILTER_GAUSS(8u, 1); - else if (type == CV_8UC3) - IPP_FILTER_GAUSS(8u, 3); - else if (type == CV_16UC1) - IPP_FILTER_GAUSS(16u, 1); - else if (type == CV_16UC3) - IPP_FILTER_GAUSS(16u, 3); - else if (type == CV_16SC1) - IPP_FILTER_GAUSS(16s, 1); - else if (type == CV_16SC3) - IPP_FILTER_GAUSS(16s, 3); - else if (type == CV_32FC1) - IPP_FILTER_GAUSS(32f, 1); - else if (type == CV_32FC3) - IPP_FILTER_GAUSS(32f, 3); -#undef IPP_FILTER_GAUSS - } - } - setIppErrorStatus(); - } - } - } -#endif Mat kx, ky; createGaussianKernels(kx, ky, type, ksize, sigma1, sigma2); @@ -2632,6 +2703,63 @@ static bool ocl_medianFilter(InputArray _src, OutputArray _dst, int m) } +#ifdef HAVE_IPP +namespace cv +{ +static bool ipp_medianFilter( InputArray _src0, OutputArray _dst, int ksize ) +{ +#if IPP_VERSION_X100 >= 801 + Mat src0 = _src0.getMat(); + _dst.create( src0.size(), src0.type() ); + Mat dst = _dst.getMat(); + +#define IPP_FILTER_MEDIAN_BORDER(ippType, ippDataType, flavor) \ + do \ + { \ + if (ippiFilterMedianBorderGetBufferSize(dstRoiSize, maskSize, \ + ippDataType, CV_MAT_CN(type), &bufSize) >= 0) \ + { \ + Ipp8u * buffer = ippsMalloc_8u(bufSize); \ + IppStatus status = ippiFilterMedianBorder_##flavor(src.ptr(), (int)src.step, \ + dst.ptr(), (int)dst.step, dstRoiSize, maskSize, \ + ippBorderRepl, (ippType)0, buffer); \ + ippsFree(buffer); \ + if (status >= 0) \ + { \ + CV_IMPL_ADD(CV_IMPL_IPP); \ + return true; \ + } \ + } \ + } \ + while ((void)0, 0) + + if( ksize <= 5 ) + { + Ipp32s bufSize; + IppiSize dstRoiSize = ippiSize(dst.cols, dst.rows), maskSize = ippiSize(ksize, ksize); + Mat src; + if( dst.data != src0.data ) + src = src0; + else + src0.copyTo(src); + + int type = src0.type(); + if (type == CV_8UC1) + IPP_FILTER_MEDIAN_BORDER(Ipp8u, ipp8u, 8u_C1R); + else if (type == CV_16UC1) + IPP_FILTER_MEDIAN_BORDER(Ipp16u, ipp16u, 16u_C1R); + else if (type == CV_16SC1) + IPP_FILTER_MEDIAN_BORDER(Ipp16s, ipp16s, 16s_C1R); + else if (type == CV_32FC1) + IPP_FILTER_MEDIAN_BORDER(Ipp32f, ipp32f, 32f_C1R); + } +#undef IPP_FILTER_MEDIAN_BORDER +#endif + return false; +} +} +#endif + void cv::medianBlur( InputArray _src0, OutputArray _dst, int ksize ) { CV_Assert( (ksize % 2 == 1) && (_src0.dims() <= 2 )); @@ -2649,53 +2777,7 @@ void cv::medianBlur( InputArray _src0, OutputArray _dst, int ksize ) _dst.create( src0.size(), src0.type() ); Mat dst = _dst.getMat(); -#if IPP_VERSION_X100 >= 801 - CV_IPP_CHECK() - { -#define IPP_FILTER_MEDIAN_BORDER(ippType, ippDataType, flavor) \ - do \ - { \ - if (ippiFilterMedianBorderGetBufferSize(dstRoiSize, maskSize, \ - ippDataType, CV_MAT_CN(type), &bufSize) >= 0) \ - { \ - Ipp8u * buffer = ippsMalloc_8u(bufSize); \ - IppStatus status = ippiFilterMedianBorder_##flavor(src.ptr(), (int)src.step, \ - dst.ptr(), (int)dst.step, dstRoiSize, maskSize, \ - ippBorderRepl, (ippType)0, buffer); \ - ippsFree(buffer); \ - if (status >= 0) \ - { \ - CV_IMPL_ADD(CV_IMPL_IPP); \ - return; \ - } \ - } \ - setIppErrorStatus(); \ - } \ - while ((void)0, 0) - - if( ksize <= 5 ) - { - Ipp32s bufSize; - IppiSize dstRoiSize = ippiSize(dst.cols, dst.rows), maskSize = ippiSize(ksize, ksize); - Mat src; - if( dst.data != src0.data ) - src = src0; - else - src0.copyTo(src); - - int type = src0.type(); - if (type == CV_8UC1) - IPP_FILTER_MEDIAN_BORDER(Ipp8u, ipp8u, 8u_C1R); - else if (type == CV_16UC1) - IPP_FILTER_MEDIAN_BORDER(Ipp16u, ipp16u, 16u_C1R); - else if (type == CV_16SC1) - IPP_FILTER_MEDIAN_BORDER(Ipp16s, ipp16s, 16s_C1R); - else if (type == CV_32FC1) - IPP_FILTER_MEDIAN_BORDER(Ipp32f, ipp32f, 32f_C1R); - } -#undef IPP_FILTER_MEDIAN_BORDER - } -#endif + CV_IPP_RUN(IPP_VERSION_X100 >= 801 && ksize <= 5, ipp_medianFilter(_src0,_dst, ksize)); #ifdef HAVE_TEGRA_OPTIMIZATION if (tegra::useTegra() && tegra::medianBlur(src0, dst, ksize)) diff --git a/modules/imgproc/src/sumpixels.cpp b/modules/imgproc/src/sumpixels.cpp index 16c7c7ef26..033006850d 100755 --- a/modules/imgproc/src/sumpixels.cpp +++ b/modules/imgproc/src/sumpixels.cpp @@ -423,6 +423,69 @@ static bool ocl_integral( InputArray _src, OutputArray _sum, OutputArray _sqsum, } +#if defined(HAVE_IPP) +namespace cv +{ +static bool ipp_integral(InputArray _src, OutputArray _sum, OutputArray _sqsum, OutputArray _tilted, int sdepth, int sqdepth) +{ +#if !defined(HAVE_IPP_ICV_ONLY) // Disabled on ICV due invalid results + int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); + if( sdepth <= 0 ) + sdepth = depth == CV_8U ? CV_32S : CV_64F; + if ( sqdepth <= 0 ) + sqdepth = CV_64F; + sdepth = CV_MAT_DEPTH(sdepth), sqdepth = CV_MAT_DEPTH(sqdepth); + + + Size ssize = _src.size(), isize(ssize.width + 1, ssize.height + 1); + _sum.create( isize, CV_MAKETYPE(sdepth, cn) ); + Mat src = _src.getMat(), sum =_sum.getMat(), sqsum, tilted; + + if( _sqsum.needed() ) + { + _sqsum.create( isize, CV_MAKETYPE(sqdepth, cn) ); + sqsum = _sqsum.getMat(); + }; + + if( ( depth == CV_8U ) && ( sdepth == CV_32F || sdepth == CV_32S ) && ( !_tilted.needed() ) && ( !_sqsum.needed() || sqdepth == CV_64F ) && ( cn == 1 ) ) + { + IppStatus status = ippStsErr; + IppiSize srcRoiSize = ippiSize( src.cols, src.rows ); + if( sdepth == CV_32F ) + { + if( _sqsum.needed() ) + { + status = ippiSqrIntegral_8u32f64f_C1R( (const Ipp8u*)src.data, (int)src.step, (Ipp32f*)sum.data, (int)sum.step, (Ipp64f*)sqsum.data, (int)sqsum.step, srcRoiSize, 0, 0 ); + } + else + { + status = ippiIntegral_8u32f_C1R( (const Ipp8u*)src.data, (int)src.step, (Ipp32f*)sum.data, (int)sum.step, srcRoiSize, 0 ); + } + } + else if( sdepth == CV_32S ) + { + if( _sqsum.needed() ) + { + status = ippiSqrIntegral_8u32s64f_C1R( (const Ipp8u*)src.data, (int)src.step, (Ipp32s*)sum.data, (int)sum.step, (Ipp64f*)sqsum.data, (int)sqsum.step, srcRoiSize, 0, 0 ); + } + else + { + status = ippiIntegral_8u32s_C1R( (const Ipp8u*)src.data, (int)src.step, (Ipp32s*)sum.data, (int)sum.step, srcRoiSize, 0 ); + } + } + if (0 <= status) + { + CV_IMPL_ADD(CV_IMPL_IPP); + return true; + } + } +#else + CV_UNUSED(_src); CV_UNUSED(_sum); CV_UNUSED(_sqsum); CV_UNUSED(_tilted); CV_UNUSED(sdepth); CV_UNUSED(sqdepth); +#endif + return false; +} +} +#endif void cv::integral( InputArray _src, OutputArray _sum, OutputArray _sqsum, OutputArray _tilted, int sdepth, int sqdepth ) { @@ -455,44 +518,9 @@ void cv::integral( InputArray _src, OutputArray _sum, OutputArray _sqsum, Output sqsum = _sqsum.getMat(); }; -#if defined(HAVE_IPP) && !defined(HAVE_IPP_ICV_ONLY) // Disabled on ICV due invalid results - CV_IPP_CHECK() - { - if( ( depth == CV_8U ) && ( sdepth == CV_32F || sdepth == CV_32S ) && ( !_tilted.needed() ) && ( !_sqsum.needed() || sqdepth == CV_64F ) && ( cn == 1 ) ) - { - IppStatus status = ippStsErr; - IppiSize srcRoiSize = ippiSize( src.cols, src.rows ); - if( sdepth == CV_32F ) - { - if( _sqsum.needed() ) - { - status = ippiSqrIntegral_8u32f64f_C1R( (const Ipp8u*)src.data, (int)src.step, (Ipp32f*)sum.data, (int)sum.step, (Ipp64f*)sqsum.data, (int)sqsum.step, srcRoiSize, 0, 0 ); - } - else - { - status = ippiIntegral_8u32f_C1R( (const Ipp8u*)src.data, (int)src.step, (Ipp32f*)sum.data, (int)sum.step, srcRoiSize, 0 ); - } - } - else if( sdepth == CV_32S ) - { - if( _sqsum.needed() ) - { - status = ippiSqrIntegral_8u32s64f_C1R( (const Ipp8u*)src.data, (int)src.step, (Ipp32s*)sum.data, (int)sum.step, (Ipp64f*)sqsum.data, (int)sqsum.step, srcRoiSize, 0, 0 ); - } - else - { - status = ippiIntegral_8u32s_C1R( (const Ipp8u*)src.data, (int)src.step, (Ipp32s*)sum.data, (int)sum.step, srcRoiSize, 0 ); - } - } - if (0 <= status) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - } -#endif + CV_IPP_RUN(( depth == CV_8U ) && ( sdepth == CV_32F || sdepth == CV_32S ) && + ( !_tilted.needed() ) && ( !_sqsum.needed() || sqdepth == CV_64F ) && ( cn == 1 ), + ipp_integral(_src, _sum, _sqsum, _tilted, sdepth, sqdepth)); if( _tilted.needed() ) { diff --git a/modules/imgproc/src/templmatch.cpp b/modules/imgproc/src/templmatch.cpp index c3d583bcca..e5f4986cf9 100644 --- a/modules/imgproc/src/templmatch.cpp +++ b/modules/imgproc/src/templmatch.cpp @@ -895,28 +895,13 @@ static void matchTemplateMask( InputArray _img, InputArray _templ, OutputArray _ } } -//////////////////////////////////////////////////////////////////////////////////////////////////////// -void cv::matchTemplate( InputArray _img, InputArray _templ, OutputArray _result, int method, InputArray _mask ) +namespace cv { - if (!_mask.empty()) - { - cv::matchTemplateMask(_img, _templ, _result, method, _mask); +static void common_matchTemplate( Mat& img, Mat& templ, Mat& result, int method, int cn ) +{ + if( method == CV_TM_CCORR ) return; - } - - int type = _img.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); - CV_Assert( CV_TM_SQDIFF <= method && method <= CV_TM_CCOEFF_NORMED ); - CV_Assert( (depth == CV_8U || depth == CV_32F) && type == _templ.type() && _img.dims() <= 2 ); - - bool needswap = _img.size().height < _templ.size().height || _img.size().width < _templ.size().width; - if (needswap) - { - CV_Assert(_img.size().height <= _templ.size().height && _img.size().width <= _templ.size().width); - } - - CV_OCL_RUN(_img.dims() <= 2 && _result.isUMat(), - (!needswap ? ocl_matchTemplate(_img, _templ, _result, method) : ocl_matchTemplate(_templ, _img, _result, method))) int numType = method == CV_TM_CCORR || method == CV_TM_CCORR_NORMED ? 0 : method == CV_TM_CCOEFF || method == CV_TM_CCOEFF_NORMED ? 1 : 2; @@ -924,57 +909,6 @@ void cv::matchTemplate( InputArray _img, InputArray _templ, OutputArray _result, method == CV_TM_SQDIFF_NORMED || method == CV_TM_CCOEFF_NORMED; - Mat img = _img.getMat(), templ = _templ.getMat(); - if (needswap) - std::swap(img, templ); - - Size corrSize(img.cols - templ.cols + 1, img.rows - templ.rows + 1); - _result.create(corrSize, CV_32F); - Mat result = _result.getMat(); - -#ifdef HAVE_TEGRA_OPTIMIZATION - if (tegra::useTegra() && tegra::matchTemplate(img, templ, result, method)) - return; -#endif - -#if defined HAVE_IPP - bool useIppMT = false; - CV_IPP_CHECK() - { - useIppMT = (templ.rows < img.rows/2 && templ.cols < img.cols/2); - - if (method == CV_TM_SQDIFF && cn == 1 && useIppMT) - { - if (ipp_sqrDistance(img, templ, result)) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return; - } - setIppErrorStatus(); - } - } -#endif - -#if defined HAVE_IPP - if (cn == 1 && useIppMT) - { - if (!ipp_crossCorr(img, templ, result)) - { - setIppErrorStatus(); - crossCorr( img, templ, result, result.size(), result.type(), Point(0,0), 0, 0); - } - else - { - CV_IMPL_ADD(CV_IMPL_IPP); - } - } - else -#endif - crossCorr( img, templ, result, result.size(), result.type(), Point(0,0), 0, 0); - - if( method == CV_TM_CCORR ) - return; - double invArea = 1./((double)templ.rows * templ.cols); Mat sum, sqsum; @@ -1081,8 +1015,81 @@ void cv::matchTemplate( InputArray _img, InputArray _templ, OutputArray _result, } } } +} +#if defined HAVE_IPP +namespace cv +{ +static bool ipp_matchTemplate( Mat& img, Mat& templ, Mat& result, int method, int cn ) +{ + bool useIppMT = (templ.rows < img.rows/2 && templ.cols < img.cols/2); + + if(cn == 1 && useIppMT) + { + if(method == CV_TM_SQDIFF) + { + if (ipp_sqrDistance(img, templ, result)) + return true; + } + else + { + if(ipp_crossCorr(img, templ, result)) + { + common_matchTemplate(img, templ, result, method, cn); + return true; + } + } + } + + return false; +} +} +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +void cv::matchTemplate( InputArray _img, InputArray _templ, OutputArray _result, int method, InputArray _mask ) +{ + if (!_mask.empty()) + { + cv::matchTemplateMask(_img, _templ, _result, method, _mask); + return; + } + + int type = _img.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); + CV_Assert( CV_TM_SQDIFF <= method && method <= CV_TM_CCOEFF_NORMED ); + CV_Assert( (depth == CV_8U || depth == CV_32F) && type == _templ.type() && _img.dims() <= 2 ); + + bool needswap = _img.size().height < _templ.size().height || _img.size().width < _templ.size().width; + if (needswap) + { + CV_Assert(_img.size().height <= _templ.size().height && _img.size().width <= _templ.size().width); + } + + CV_OCL_RUN(_img.dims() <= 2 && _result.isUMat(), + (!needswap ? ocl_matchTemplate(_img, _templ, _result, method) : ocl_matchTemplate(_templ, _img, _result, method))) + + Mat img = _img.getMat(), templ = _templ.getMat(); + if (needswap) + std::swap(img, templ); + + Size corrSize(img.cols - templ.cols + 1, img.rows - templ.rows + 1); + _result.create(corrSize, CV_32F); + Mat result = _result.getMat(); + +#ifdef HAVE_TEGRA_OPTIMIZATION + if (tegra::useTegra() && tegra::matchTemplate(img, templ, result, method)) + return; +#endif + + CV_IPP_RUN(true, ipp_matchTemplate(img, templ, result, method, cn)) + + crossCorr( img, templ, result, result.size(), result.type(), Point(0,0), 0, 0); + + common_matchTemplate(img, templ, result, method, cn); +} + CV_IMPL void cvMatchTemplate( const CvArr* _img, const CvArr* _templ, CvArr* _result, int method ) { diff --git a/modules/imgproc/src/thresh.cpp b/modules/imgproc/src/thresh.cpp index 490bdff3e5..1fafeeef09 100644 --- a/modules/imgproc/src/thresh.cpp +++ b/modules/imgproc/src/thresh.cpp @@ -904,6 +904,24 @@ thresh_32f( const Mat& _src, Mat& _dst, float thresh, float maxval, int type ) } } +#ifdef HAVE_IPP +static bool ipp_getThreshVal_Otsu_8u( const unsigned char* _src, int step, Size size, unsigned char &thresh) +{ + int ippStatus = -1; +#if IPP_VERSION_X100 >= 801 && !defined(HAVE_IPP_ICV_ONLY) + IppiSize srcSize = { size.width, size.height }; + CV_SUPPRESS_DEPRECATED_START + ippStatus = ippiComputeThreshold_Otsu_8u_C1R(_src, step, srcSize, &thresh); + CV_SUPPRESS_DEPRECATED_END +#else + CV_UNUSED(_src); CV_UNUSED(step); CV_UNUSED(size); CV_UNUSED(thresh); +#endif + if(ippStatus >= 0) + return true; + return false; +} +#endif + static double getThreshVal_Otsu_8u( const Mat& _src ) @@ -917,22 +935,11 @@ getThreshVal_Otsu_8u( const Mat& _src ) step = size.width; } -#if IPP_VERSION_X100 >= 801 && !defined(HAVE_IPP_ICV_ONLY) - CV_IPP_CHECK() - { - IppiSize srcSize = { size.width, size.height }; - Ipp8u thresh; - CV_SUPPRESS_DEPRECATED_START - IppStatus ok = ippiComputeThreshold_Otsu_8u_C1R(_src.ptr(), step, srcSize, &thresh); - CV_SUPPRESS_DEPRECATED_END - if (ok >= 0) - { - CV_IMPL_ADD(CV_IMPL_IPP); - return thresh; - } - setIppErrorStatus(); - } +#ifdef HAVE_IPP + unsigned char thresh; #endif + CV_IPP_RUN(true, ipp_getThreshVal_Otsu_8u(_src.ptr(), step, size, thresh), thresh); + const int N = 256; int i, j, h[N] = {0}; From 032b6322fc7c8a2e5b90df0293322cebf61b02e6 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 18 Jun 2015 14:02:01 +0300 Subject: [PATCH 037/133] fix MatAllocator creation/destruction issues --- modules/core/src/matrix.cpp | 8 ++++++-- modules/core/src/ocl.cpp | 9 +++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/modules/core/src/matrix.cpp b/modules/core/src/matrix.cpp index b273c8a7d8..5de7e034fd 100644 --- a/modules/core/src/matrix.cpp +++ b/modules/core/src/matrix.cpp @@ -222,10 +222,14 @@ public: } }; +static StdMatAllocator *mat_allocator = NULL; MatAllocator* Mat::getStdAllocator() { - static StdMatAllocator allocator; - return &allocator; + if (mat_allocator == NULL) + { + mat_allocator = new StdMatAllocator(); + } + return mat_allocator; } void swap( Mat& a, Mat& b ) diff --git a/modules/core/src/ocl.cpp b/modules/core/src/ocl.cpp index 5d68a36832..4f1231238e 100644 --- a/modules/core/src/ocl.cpp +++ b/modules/core/src/ocl.cpp @@ -5160,10 +5160,15 @@ public: MatAllocator* matStdAllocator; }; +// This line should not force OpenCL runtime initialization! (don't put "new OpenCLAllocator()" here) +static MatAllocator *ocl_allocator = NULL; MatAllocator* getOpenCLAllocator() { - static MatAllocator * allocator = new OpenCLAllocator(); - return allocator; + if (ocl_allocator == NULL) + { + ocl_allocator = new OpenCLAllocator(); + } + return ocl_allocator; } ///////////////////////////////////////////// Utility functions ///////////////////////////////////////////////// From 9f1c641199f639fda3bf91c1338696c25e6604f7 Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Thu, 18 Jun 2015 17:42:32 +0200 Subject: [PATCH 038/133] spatialGradient: Add test class and Sobel proxy method --- modules/imgproc/include/opencv2/imgproc.hpp | 9 +++ modules/imgproc/src/spatialgradient.cpp | 56 +++++++++++++++++++ modules/imgproc/test/test_filter.cpp | 61 +++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 modules/imgproc/src/spatialgradient.cpp diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index a9fa7ae21e..c93975eeb8 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1369,6 +1369,15 @@ CV_EXPORTS_W void Sobel( InputArray src, OutputArray dst, int ddepth, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT ); +/** @brief TODO + +TODO + + */ + +CV_EXPORTS_W void spatialGradient( InputArray src, OutputArray dx, + OutputArray dy, int ksize ); + /** @brief Calculates the first x- or y- image derivative using Scharr operator. The function computes the first x- or y- spatial image derivative using the Scharr operator. The diff --git a/modules/imgproc/src/spatialgradient.cpp b/modules/imgproc/src/spatialgradient.cpp new file mode 100644 index 0000000000..9b1ac89b6f --- /dev/null +++ b/modules/imgproc/src/spatialgradient.cpp @@ -0,0 +1,56 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "precomp.hpp" + +namespace cv +{ + +void spatialGradient( InputArray src, OutputArray dx, OutputArray dy, int ksize ) +{ + + // TODO: Vectorize using hal intrinsics + Sobel( src, dx, CV_16S, 1, 0, 3 ); + Sobel( src, dy, CV_16S, 0, 1, 3 ); +} + +} diff --git a/modules/imgproc/test/test_filter.cpp b/modules/imgproc/test/test_filter.cpp index 9253132186..e618c1b588 100644 --- a/modules/imgproc/test/test_filter.cpp +++ b/modules/imgproc/test/test_filter.cpp @@ -552,6 +552,66 @@ void CV_SobelTest::prepare_to_validation( int /*test_case_idx*/ ) } +/////////////// spatialGradient /////////////// + +class CV_SpatialGradientTest : public CV_DerivBaseTest +{ +public: + CV_SpatialGradientTest(); + +protected: + void prepare_to_validation( int test_case_idx ); + void run_func(); + void get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, vector >& types ); + int ksize; +}; + +CV_SpatialGradientTest::CV_SpatialGradientTest() { + test_array[OUTPUT].push_back(NULL); + test_array[REF_OUTPUT].push_back(NULL); + inplace = false; +} + + +void CV_SpatialGradientTest::get_test_array_types_and_sizes( int test_case_idx, + vector >& sizes, + vector >& types ) +{ + CV_DerivBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types ); + + sizes[OUTPUT][1] = sizes[REF_OUTPUT][1] = sizes[OUTPUT][0]; + + // Only CV_16S1 for now + types[INPUT][0] = types[OUTPUT][0] = types[OUTPUT][1] = types[REF_OUTPUT][0] + = types[REF_OUTPUT][1] = CV_MAKETYPE(CV_16S, 1); + + ksize = 3; +} + + +void CV_SpatialGradientTest::run_func() +{ + spatialGradient( cvarrToMat(test_array[INPUT][0]), + cvarrToMat(test_array[OUTPUT][0]), + cvarrToMat(test_array[OUTPUT][1]), + ksize + ); +} + + +void CV_SpatialGradientTest::prepare_to_validation( int /*test_case_idx*/ ) +{ + int dx, dy; + + dx = 1; dy = 0; + Sobel( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], CV_16SC1, dx, dy, ksize ); + + dx = 0; dy = 1; + Sobel( test_mat[INPUT][0], test_mat[REF_OUTPUT][1], CV_16SC1, dx, dy, ksize ); +} + + /////////////// laplace /////////////// class CV_LaplaceTest : public CV_DerivBaseTest @@ -1773,6 +1833,7 @@ TEST(Imgproc_Dilate, accuracy) { CV_DilateTest test; test.safe_run(); } TEST(Imgproc_MorphologyEx, accuracy) { CV_MorphExTest test; test.safe_run(); } TEST(Imgproc_Filter2D, accuracy) { CV_FilterTest test; test.safe_run(); } TEST(Imgproc_Sobel, accuracy) { CV_SobelTest test; test.safe_run(); } +TEST(Imgproc_SpatialGradient, accuracy) { CV_SpatialGradientTest test; test.safe_run(); } TEST(Imgproc_Laplace, accuracy) { CV_LaplaceTest test; test.safe_run(); } TEST(Imgproc_Blur, accuracy) { CV_BlurTest test; test.safe_run(); } TEST(Imgproc_GaussianBlur, accuracy) { CV_GaussianBlurTest test; test.safe_run(); } From 11fb1f74cc7b7444833ef808b675cdecc98b7d1e Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Fri, 19 Jun 2015 01:23:01 +0200 Subject: [PATCH 039/133] spatialGradient: Add asserts --- modules/imgproc/src/spatialgradient.cpp | 18 +++++++++++++++--- modules/imgproc/test/test_filter.cpp | 19 +++++++++++-------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/modules/imgproc/src/spatialgradient.cpp b/modules/imgproc/src/spatialgradient.cpp index 9b1ac89b6f..86068813bf 100644 --- a/modules/imgproc/src/spatialgradient.cpp +++ b/modules/imgproc/src/spatialgradient.cpp @@ -45,12 +45,24 @@ namespace cv { -void spatialGradient( InputArray src, OutputArray dx, OutputArray dy, int ksize ) +void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksize ) { + Mat src = _src.getMat(); + CV_Assert(!src.empty()); + CV_Assert(src.isContinuous()); + CV_Assert(src.type() == CV_8UC1); + + _dx.create(src.size(), CV_16SC1); + _dy.create(src.size(), CV_16SC1); + Mat dx = _dx.getMat(), + dy = _dy.getMat(); + CV_Assert(dx.isContinuous()); + CV_Assert(dy.isContinuous()); + // TODO: Vectorize using hal intrinsics - Sobel( src, dx, CV_16S, 1, 0, 3 ); - Sobel( src, dy, CV_16S, 0, 1, 3 ); + Sobel( src, dx, CV_16SC1, 1, 0, ksize ); + Sobel( src, dy, CV_16SC1, 0, 1, ksize ); } } diff --git a/modules/imgproc/test/test_filter.cpp b/modules/imgproc/test/test_filter.cpp index e618c1b588..968d01eda9 100644 --- a/modules/imgproc/test/test_filter.cpp +++ b/modules/imgproc/test/test_filter.cpp @@ -582,9 +582,12 @@ void CV_SpatialGradientTest::get_test_array_types_and_sizes( int test_case_idx, sizes[OUTPUT][1] = sizes[REF_OUTPUT][1] = sizes[OUTPUT][0]; - // Only CV_16S1 for now - types[INPUT][0] = types[OUTPUT][0] = types[OUTPUT][1] = types[REF_OUTPUT][0] - = types[REF_OUTPUT][1] = CV_MAKETYPE(CV_16S, 1); + // Inputs are only CV_8UC1 for now + types[INPUT][0] = CV_8UC1; + + // Outputs are only CV_16SC1 for now + types[OUTPUT][0] = types[OUTPUT][1] = types[REF_OUTPUT][0] + = types[REF_OUTPUT][1] = CV_16SC1; ksize = 3; } @@ -592,11 +595,11 @@ void CV_SpatialGradientTest::get_test_array_types_and_sizes( int test_case_idx, void CV_SpatialGradientTest::run_func() { - spatialGradient( cvarrToMat(test_array[INPUT][0]), - cvarrToMat(test_array[OUTPUT][0]), - cvarrToMat(test_array[OUTPUT][1]), - ksize - ); + Mat dx, dy; + spatialGradient( test_mat[INPUT][0].clone(), dx, dy, ksize ); + + test_mat[OUTPUT][0] = dx; + test_mat[OUTPUT][1] = dy; } From 770e742e04b2f2e78b73ef05faf173decfa95da7 Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Fri, 19 Jun 2015 02:56:51 +0200 Subject: [PATCH 040/133] spatialGradient: Add non-SSE version --- modules/imgproc/src/spatialgradient.cpp | 135 ++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 10 deletions(-) diff --git a/modules/imgproc/src/spatialgradient.cpp b/modules/imgproc/src/spatialgradient.cpp index 86068813bf..b75b0a59f2 100644 --- a/modules/imgproc/src/spatialgradient.cpp +++ b/modules/imgproc/src/spatialgradient.cpp @@ -48,21 +48,136 @@ namespace cv void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksize ) { + // Prepare InputArray src Mat src = _src.getMat(); - CV_Assert(!src.empty()); - CV_Assert(src.isContinuous()); - CV_Assert(src.type() == CV_8UC1); + CV_Assert( !src.empty() ); + CV_Assert( src.isContinuous() ); + CV_Assert( src.type() == CV_8UC1 ); - _dx.create(src.size(), CV_16SC1); - _dy.create(src.size(), CV_16SC1); + // Prepare OutputArrays dx, dy + _dx.create( src.size(), CV_16SC1 ); + _dy.create( src.size(), CV_16SC1 ); Mat dx = _dx.getMat(), dy = _dy.getMat(); - CV_Assert(dx.isContinuous()); - CV_Assert(dy.isContinuous()); + CV_Assert( dx.isContinuous() ); + CV_Assert( dy.isContinuous() ); + + // TODO: Allow for other kernel sizes + CV_Assert(ksize == 3); + + // Reference + //Sobel( src, dx, CV_16SC1, 1, 0, ksize ); + //Sobel( src, dy, CV_16SC1, 0, 1, ksize ); + + // Get dimensions + int H = src.rows, + W = src.cols, + N = H * W; + + // Get raw pointers to input/output data + uchar* p_src = src.ptr(0); + short* p_dx = dx.ptr(0); + short* p_dy = dy.ptr(0); + + // Row, column indices + int i, j; + + /* NOTE: + * + * Sobel-x: -1 0 1 + * -2 0 2 + * -1 0 1 + * + * Sobel-y: -1 -2 -1 + * 0 0 0 + * 1 2 1 + */ + + // No-SSE + int idx; + + + p_dx[0] = 0; // Top-left corner + p_dy[0] = 0; + p_dx[W-1] = 0; // Top-right corner + p_dy[W-1] = 0; + p_dx[N-1] = 0; // Bottom-right corner + p_dy[N-1] = 0; + p_dx[N-W] = 0; // Bottom-left corner + p_dy[N-W] = 0; + + // Handle special case: column matrix + if ( W == 1 ) + { + for ( i = 1; i < H - 1; i++ ) + { + p_dx[i] = 0; + p_dy[i] = 4*(p_src[i + 1] - p_src[i - 1]); // Should be 2?! 4 makes tests pass + } + return; + } + + // Handle special case: row matrix + if ( H == 1 ) + { + for ( j = 1; j < W - 1; j++ ) + { + p_dx[j] = 4*(p_src[j + 1] - p_src[j - 1]); // Should be 2?! 4 makes tests pass + p_dy[j] = 0; + } + return; + } + + // Do top row + for ( j = 1; j < W - 1; j++ ) + { + idx = j; + p_dx[idx] = -(p_src[idx+W-1] + 2*p_src[idx-1] + p_src[idx+W-1]) + + (p_src[idx+W+1] + 2*p_src[idx+1] + p_src[idx+W+1]); + p_dy[idx] = 0; + } + + // Do right column + idx = 2*W - 1; + for ( i = 1; i < H - 1; i++ ) + { + p_dx[idx] = 0; + p_dy[idx] = -(p_src[idx-W-1] + 2*p_src[idx-W] + p_src[idx-W-1]) + + (p_src[idx+W-1] + 2*p_src[idx+W] + p_src[idx+W-1]); + idx += W; + } + + // Do bottom row + idx = N - W + 1; + for ( j = 1; j < W - 1; j++ ) + { + p_dx[idx] = -(p_src[idx-W-1] + 2*p_src[idx-1] + p_src[idx-W-1]) + + (p_src[idx-W+1] + 2*p_src[idx+1] + p_src[idx-W+1]); + p_dy[idx] = 0; + idx++; + } + + // Do left column + idx = W; + for ( i = 1; i < H - 1; i++ ) + { + p_dx[idx] = 0; + p_dy[idx] = -(p_src[idx-W+1] + 2*p_src[idx-W] + p_src[idx-W+1]) + + (p_src[idx+W+1] + 2*p_src[idx+W] + p_src[idx+W+1]); + idx += W; + } + + // Do Inner area + for ( i = 1; i < H - 1; i++ ) + for ( j = 1; j < W - 1; j++ ) + { + idx = i*W + j; + p_dx[idx] = -(p_src[idx-W-1] + 2*p_src[idx-1] + p_src[idx+W-1]) + + (p_src[idx-W+1] + 2*p_src[idx+1] + p_src[idx+W+1]); + p_dy[idx] = -(p_src[idx-W-1] + 2*p_src[idx-W] + p_src[idx-W+1]) + + (p_src[idx+W-1] + 2*p_src[idx+W] + p_src[idx+W+1]); + } - // TODO: Vectorize using hal intrinsics - Sobel( src, dx, CV_16SC1, 1, 0, ksize ); - Sobel( src, dy, CV_16SC1, 0, 1, ksize ); } } From 88bc88125a6574501aac6f5536e8a2f8b6f37408 Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Fri, 19 Jun 2015 03:36:49 +0200 Subject: [PATCH 041/133] spatialGradient: Vectorise inner area --- modules/imgproc/src/precomp.hpp | 1 + modules/imgproc/src/spatialgradient.cpp | 97 ++++++++++++++++++++++++- 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/modules/imgproc/src/precomp.hpp b/modules/imgproc/src/precomp.hpp index e71a0356c0..7a0cece2f2 100644 --- a/modules/imgproc/src/precomp.hpp +++ b/modules/imgproc/src/precomp.hpp @@ -49,6 +49,7 @@ #include "opencv2/imgproc/imgproc_c.h" #include "opencv2/core/private.hpp" #include "opencv2/core/ocl.hpp" +#include "opencv2/hal.hpp" #include #include diff --git a/modules/imgproc/src/spatialgradient.cpp b/modules/imgproc/src/spatialgradient.cpp index b75b0a59f2..6e8c8400eb 100644 --- a/modules/imgproc/src/spatialgradient.cpp +++ b/modules/imgproc/src/spatialgradient.cpp @@ -41,6 +41,7 @@ //M*/ #include "precomp.hpp" +#include "opencv2/hal/intrin.hpp" namespace cv { @@ -96,7 +97,6 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksi // No-SSE int idx; - p_dx[0] = 0; // Top-left corner p_dy[0] = 0; p_dx[W-1] = 0; // Top-right corner @@ -168,6 +168,100 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksi } // Do Inner area +#if CV_SIMD128 + // Characters in variable names have the following meanings: + // u: unsigned char + // s: signed int + // + // [row][column] + // m: offset -1 + // n: offset 0 + // p: offset 1 + // Example: umn is offset -1 in row and offset 0 in column + v_uint8x16 v_umm, v_umn, v_ump, + v_unm, v_unn, v_unp, + v_upm, v_upn, v_upp; + v_uint16x8 v_umm1, v_umm2, v_umn1, v_umn2, v_ump1, v_ump2, + v_unm1, v_unm2, v_unn1, v_unn2, v_unp1, v_unp2, + v_upm1, v_upm2, v_upn1, v_upn2, v_upp1, v_upp2; + v_int16x8 v_smm1, v_smm2, v_smn1, v_smn2, v_smp1, v_smp2, + v_snm1, v_snm2, v_snn1, v_snn2, v_snp1, v_snp2, + v_spm1, v_spm2, v_spn1, v_spn2, v_spp1, v_spp2, + v_two = v_setall_s16(2), + v_sdx1, v_sdx2, v_sdy1, v_sdy2; + for ( i = 1; i < H - 1; i++ ) + for ( j = 1; j < W - 1 - 15; j += 16 ) + { + // Load + idx = i*W + j; + v_umm = v_load(&p_src[idx - W - 1]); + v_umn = v_load(&p_src[idx - W]); + v_ump = v_load(&p_src[idx - W + 1]); + v_unm = v_load(&p_src[idx - 1]); + v_unn = v_load(&p_src[idx]); + v_unp = v_load(&p_src[idx + 1]); + v_upm = v_load(&p_src[idx + W - 1]); + v_upn = v_load(&p_src[idx + W]); + v_upp = v_load(&p_src[idx + W + 1]); + + // Expand to uint + v_expand(v_umm, v_umm1, v_umm2); + v_expand(v_umn, v_umn1, v_umn2); + v_expand(v_ump, v_ump1, v_ump2); + v_expand(v_unm, v_unm1, v_unm2); + v_expand(v_unn, v_unn1, v_unn2); + v_expand(v_unp, v_unp1, v_unp2); + v_expand(v_upm, v_upm1, v_upm2); + v_expand(v_upn, v_upn1, v_upn2); + v_expand(v_upp, v_upp1, v_upp2); + + // Convert to int + v_smm1 = v_reinterpret_as_s16(v_umm1); + v_smm2 = v_reinterpret_as_s16(v_umm2); + v_smn1 = v_reinterpret_as_s16(v_umn1); + v_smn2 = v_reinterpret_as_s16(v_umn2); + v_smp1 = v_reinterpret_as_s16(v_ump1); + v_smp2 = v_reinterpret_as_s16(v_ump2); + v_snm1 = v_reinterpret_as_s16(v_unm1); + v_snm2 = v_reinterpret_as_s16(v_unm2); + v_snn1 = v_reinterpret_as_s16(v_unn1); + v_snn2 = v_reinterpret_as_s16(v_unn2); + v_snp1 = v_reinterpret_as_s16(v_unp1); + v_snp2 = v_reinterpret_as_s16(v_unp2); + v_spm1 = v_reinterpret_as_s16(v_upm1); + v_spm2 = v_reinterpret_as_s16(v_upm2); + v_spn1 = v_reinterpret_as_s16(v_upn1); + v_spn2 = v_reinterpret_as_s16(v_upn2); + v_spp1 = v_reinterpret_as_s16(v_upp1); + v_spp2 = v_reinterpret_as_s16(v_upp2); + + // dx + v_sdx1 = (v_smp1 - v_smm1) + v_two*(v_snp1 - v_snm1) + (v_spp1 - v_spm1); + v_sdx2 = (v_smp2 - v_smm2) + v_two*(v_snp2 - v_snm2) + (v_spp2 - v_spm2); + + // dy + v_sdy1 = (v_spm1 - v_smm1) + v_two*(v_spn1 - v_smn1) + (v_spp1 - v_smp1); + v_sdy2 = (v_spm2 - v_smm2) + v_two*(v_spn2 - v_smn2) + (v_spp2 - v_smp2); + + // Store + v_store(&p_dx[idx], v_sdx1); + v_store(&p_dx[idx+8], v_sdx2); + v_store(&p_dy[idx], v_sdy1); + v_store(&p_dy[idx+8], v_sdy2); + } + + // Cleanup + int end_j = j; + for ( i = 1; i < H - 1; i++ ) + for ( j = end_j; j < W - 1; j++ ) + { + idx = i*W + j; + p_dx[idx] = -(p_src[idx-W-1] + 2*p_src[idx-1] + p_src[idx+W-1]) + + (p_src[idx-W+1] + 2*p_src[idx+1] + p_src[idx+W+1]); + p_dy[idx] = -(p_src[idx-W-1] + 2*p_src[idx-W] + p_src[idx-W+1]) + + (p_src[idx+W-1] + 2*p_src[idx+W] + p_src[idx+W+1]); + } +#else for ( i = 1; i < H - 1; i++ ) for ( j = 1; j < W - 1; j++ ) { @@ -177,6 +271,7 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksi p_dy[idx] = -(p_src[idx-W-1] + 2*p_src[idx-W] + p_src[idx-W+1]) + (p_src[idx+W-1] + 2*p_src[idx+W] + p_src[idx+W+1]); } +#endif } From a2dbd2f10e70b11b48cf2b903a452d31730cd6fa Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Fri, 19 Jun 2015 04:07:18 +0200 Subject: [PATCH 042/133] spatialGradient: Less vector loads --- modules/imgproc/src/spatialgradient.cpp | 77 ++++++++++++++++--------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/modules/imgproc/src/spatialgradient.cpp b/modules/imgproc/src/spatialgradient.cpp index 6e8c8400eb..e0db8cd985 100644 --- a/modules/imgproc/src/spatialgradient.cpp +++ b/modules/imgproc/src/spatialgradient.cpp @@ -189,20 +189,18 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksi v_spm1, v_spm2, v_spn1, v_spn2, v_spp1, v_spp2, v_two = v_setall_s16(2), v_sdx1, v_sdx2, v_sdy1, v_sdy2; - for ( i = 1; i < H - 1; i++ ) + + // Go through 16-column chunks at a time for ( j = 1; j < W - 1 - 15; j += 16 ) { - // Load - idx = i*W + j; + // Load top two rows for 3x3 Sobel filter + idx = W + j; v_umm = v_load(&p_src[idx - W - 1]); v_umn = v_load(&p_src[idx - W]); v_ump = v_load(&p_src[idx - W + 1]); v_unm = v_load(&p_src[idx - 1]); v_unn = v_load(&p_src[idx]); v_unp = v_load(&p_src[idx + 1]); - v_upm = v_load(&p_src[idx + W - 1]); - v_upn = v_load(&p_src[idx + W]); - v_upp = v_load(&p_src[idx + W + 1]); // Expand to uint v_expand(v_umm, v_umm1, v_umm2); @@ -211,9 +209,6 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksi v_expand(v_unm, v_unm1, v_unm2); v_expand(v_unn, v_unn1, v_unn2); v_expand(v_unp, v_unp1, v_unp2); - v_expand(v_upm, v_upm1, v_upm2); - v_expand(v_upn, v_upn1, v_upn2); - v_expand(v_upp, v_upp1, v_upp2); // Convert to int v_smm1 = v_reinterpret_as_s16(v_umm1); @@ -228,26 +223,56 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksi v_snn2 = v_reinterpret_as_s16(v_unn2); v_snp1 = v_reinterpret_as_s16(v_unp1); v_snp2 = v_reinterpret_as_s16(v_unp2); - v_spm1 = v_reinterpret_as_s16(v_upm1); - v_spm2 = v_reinterpret_as_s16(v_upm2); - v_spn1 = v_reinterpret_as_s16(v_upn1); - v_spn2 = v_reinterpret_as_s16(v_upn2); - v_spp1 = v_reinterpret_as_s16(v_upp1); - v_spp2 = v_reinterpret_as_s16(v_upp2); - // dx - v_sdx1 = (v_smp1 - v_smm1) + v_two*(v_snp1 - v_snm1) + (v_spp1 - v_spm1); - v_sdx2 = (v_smp2 - v_smm2) + v_two*(v_snp2 - v_snm2) + (v_spp2 - v_spm2); + for ( i = 1; i < H - 1; i++ ) + { + // Load last row for 3x3 Sobel filter + idx = i*W + j; + v_upm = v_load(&p_src[idx + W - 1]); + v_upn = v_load(&p_src[idx + W]); + v_upp = v_load(&p_src[idx + W + 1]); - // dy - v_sdy1 = (v_spm1 - v_smm1) + v_two*(v_spn1 - v_smn1) + (v_spp1 - v_smp1); - v_sdy2 = (v_spm2 - v_smm2) + v_two*(v_spn2 - v_smn2) + (v_spp2 - v_smp2); + // Expand to uint + v_expand(v_upm, v_upm1, v_upm2); + v_expand(v_upn, v_upn1, v_upn2); + v_expand(v_upp, v_upp1, v_upp2); - // Store - v_store(&p_dx[idx], v_sdx1); - v_store(&p_dx[idx+8], v_sdx2); - v_store(&p_dy[idx], v_sdy1); - v_store(&p_dy[idx+8], v_sdy2); + // Convert to int + v_spm1 = v_reinterpret_as_s16(v_upm1); + v_spm2 = v_reinterpret_as_s16(v_upm2); + v_spn1 = v_reinterpret_as_s16(v_upn1); + v_spn2 = v_reinterpret_as_s16(v_upn2); + v_spp1 = v_reinterpret_as_s16(v_upp1); + v_spp2 = v_reinterpret_as_s16(v_upp2); + + // dx + v_sdx1 = (v_smp1 - v_smm1) + v_two*(v_snp1 - v_snm1) + (v_spp1 - v_spm1); + v_sdx2 = (v_smp2 - v_smm2) + v_two*(v_snp2 - v_snm2) + (v_spp2 - v_spm2); + + // dy + v_sdy1 = (v_spm1 - v_smm1) + v_two*(v_spn1 - v_smn1) + (v_spp1 - v_smp1); + v_sdy2 = (v_spm2 - v_smm2) + v_two*(v_spn2 - v_smn2) + (v_spp2 - v_smp2); + + // Store + v_store(&p_dx[idx], v_sdx1); + v_store(&p_dx[idx+8], v_sdx2); + v_store(&p_dy[idx], v_sdy1); + v_store(&p_dy[idx+8], v_sdy2); + + // Shift loaded rows up one + v_smm1 = v_snm1; + v_smm2 = v_snm2; + v_smn1 = v_snn1; + v_smn2 = v_snn2; + v_smp1 = v_snp1; + v_smp2 = v_snp2; + v_snm1 = v_spm1; + v_snm2 = v_spm2; + v_snn1 = v_spn1; + v_snn2 = v_spn2; + v_snp1 = v_spp1; + v_snp2 = v_spp2; + } } // Cleanup From f9c4c96663b029587220f0b8c324ea5ff7b6410e Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Fri, 19 Jun 2015 04:29:07 +0200 Subject: [PATCH 043/133] spatialGradient: Reduce temporary vectors --- modules/imgproc/src/spatialgradient.cpp | 87 +++++++++++-------------- 1 file changed, 38 insertions(+), 49 deletions(-) diff --git a/modules/imgproc/src/spatialgradient.cpp b/modules/imgproc/src/spatialgradient.cpp index e0db8cd985..7b35e249d9 100644 --- a/modules/imgproc/src/spatialgradient.cpp +++ b/modules/imgproc/src/spatialgradient.cpp @@ -178,12 +178,8 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksi // n: offset 0 // p: offset 1 // Example: umn is offset -1 in row and offset 0 in column - v_uint8x16 v_umm, v_umn, v_ump, - v_unm, v_unn, v_unp, - v_upm, v_upn, v_upp; - v_uint16x8 v_umm1, v_umm2, v_umn1, v_umn2, v_ump1, v_ump2, - v_unm1, v_unm2, v_unn1, v_unn2, v_unp1, v_unp2, - v_upm1, v_upm2, v_upn1, v_upn2, v_upp1, v_upp2; + v_uint8x16 v_um, v_un, v_up; + v_uint16x8 v_um1, v_um2, v_un1, v_un2, v_up1, v_up2; v_int16x8 v_smm1, v_smm2, v_smn1, v_smn2, v_smp1, v_smp2, v_snm1, v_snm2, v_snn1, v_snn2, v_snp1, v_snp2, v_spm1, v_spm2, v_spn1, v_spn2, v_spp1, v_spp2, @@ -195,55 +191,48 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksi { // Load top two rows for 3x3 Sobel filter idx = W + j; - v_umm = v_load(&p_src[idx - W - 1]); - v_umn = v_load(&p_src[idx - W]); - v_ump = v_load(&p_src[idx - W + 1]); - v_unm = v_load(&p_src[idx - 1]); - v_unn = v_load(&p_src[idx]); - v_unp = v_load(&p_src[idx + 1]); + v_um = v_load(&p_src[idx - W - 1]); + v_un = v_load(&p_src[idx - W]); + v_up = v_load(&p_src[idx - W + 1]); + v_expand(v_um, v_um1, v_um2); + v_expand(v_un, v_un1, v_un2); + v_expand(v_up, v_up1, v_up2); + v_smm1 = v_reinterpret_as_s16(v_um1); + v_smm2 = v_reinterpret_as_s16(v_um2); + v_smn1 = v_reinterpret_as_s16(v_un1); + v_smn2 = v_reinterpret_as_s16(v_un2); + v_smp1 = v_reinterpret_as_s16(v_up1); + v_smp2 = v_reinterpret_as_s16(v_up2); - // Expand to uint - v_expand(v_umm, v_umm1, v_umm2); - v_expand(v_umn, v_umn1, v_umn2); - v_expand(v_ump, v_ump1, v_ump2); - v_expand(v_unm, v_unm1, v_unm2); - v_expand(v_unn, v_unn1, v_unn2); - v_expand(v_unp, v_unp1, v_unp2); - - // Convert to int - v_smm1 = v_reinterpret_as_s16(v_umm1); - v_smm2 = v_reinterpret_as_s16(v_umm2); - v_smn1 = v_reinterpret_as_s16(v_umn1); - v_smn2 = v_reinterpret_as_s16(v_umn2); - v_smp1 = v_reinterpret_as_s16(v_ump1); - v_smp2 = v_reinterpret_as_s16(v_ump2); - v_snm1 = v_reinterpret_as_s16(v_unm1); - v_snm2 = v_reinterpret_as_s16(v_unm2); - v_snn1 = v_reinterpret_as_s16(v_unn1); - v_snn2 = v_reinterpret_as_s16(v_unn2); - v_snp1 = v_reinterpret_as_s16(v_unp1); - v_snp2 = v_reinterpret_as_s16(v_unp2); + v_um = v_load(&p_src[idx - 1]); + v_un = v_load(&p_src[idx]); + v_up = v_load(&p_src[idx + 1]); + v_expand(v_um, v_um1, v_um2); + v_expand(v_un, v_un1, v_un2); + v_expand(v_up, v_up1, v_up2); + v_snm1 = v_reinterpret_as_s16(v_um1); + v_snm2 = v_reinterpret_as_s16(v_um2); + v_snn1 = v_reinterpret_as_s16(v_un1); + v_snn2 = v_reinterpret_as_s16(v_un2); + v_snp1 = v_reinterpret_as_s16(v_up1); + v_snp2 = v_reinterpret_as_s16(v_up2); for ( i = 1; i < H - 1; i++ ) { // Load last row for 3x3 Sobel filter idx = i*W + j; - v_upm = v_load(&p_src[idx + W - 1]); - v_upn = v_load(&p_src[idx + W]); - v_upp = v_load(&p_src[idx + W + 1]); - - // Expand to uint - v_expand(v_upm, v_upm1, v_upm2); - v_expand(v_upn, v_upn1, v_upn2); - v_expand(v_upp, v_upp1, v_upp2); - - // Convert to int - v_spm1 = v_reinterpret_as_s16(v_upm1); - v_spm2 = v_reinterpret_as_s16(v_upm2); - v_spn1 = v_reinterpret_as_s16(v_upn1); - v_spn2 = v_reinterpret_as_s16(v_upn2); - v_spp1 = v_reinterpret_as_s16(v_upp1); - v_spp2 = v_reinterpret_as_s16(v_upp2); + v_um = v_load(&p_src[idx + W - 1]); + v_un = v_load(&p_src[idx + W]); + v_up = v_load(&p_src[idx + W + 1]); + v_expand(v_um, v_um1, v_um2); + v_expand(v_un, v_un1, v_un2); + v_expand(v_up, v_up1, v_up2); + v_spm1 = v_reinterpret_as_s16(v_um1); + v_spm2 = v_reinterpret_as_s16(v_um2); + v_spn1 = v_reinterpret_as_s16(v_un1); + v_spn2 = v_reinterpret_as_s16(v_un2); + v_spp1 = v_reinterpret_as_s16(v_up1); + v_spp2 = v_reinterpret_as_s16(v_up2); // dx v_sdx1 = (v_smp1 - v_smm1) + v_two*(v_snp1 - v_snm1) + (v_spp1 - v_spm1); From b5c4355c13c35de242c23d284886fc9e17589b9e Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Fri, 19 Jun 2015 04:29:28 +0200 Subject: [PATCH 044/133] spatialGradient: Add basic perf test --- modules/imgproc/perf/perf_spatialgradient.cpp | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 modules/imgproc/perf/perf_spatialgradient.cpp diff --git a/modules/imgproc/perf/perf_spatialgradient.cpp b/modules/imgproc/perf/perf_spatialgradient.cpp new file mode 100644 index 0000000000..31219a3d92 --- /dev/null +++ b/modules/imgproc/perf/perf_spatialgradient.cpp @@ -0,0 +1,34 @@ +#include "perf_precomp.hpp" + +using namespace std; +using namespace cv; +using namespace perf; +using namespace testing; +using std::tr1::make_tuple; +using std::tr1::get; + +typedef std::tr1::tuple Size_Ksize_t; +typedef perf::TestBaseWithParam Size_Ksize; + +PERF_TEST_P( Size_Ksize, spatialGradient, + Combine( + SZ_ALL_HD, + Values( 3 ) + ) +) +{ + Size size = std::tr1::get<0>(GetParam()); + int ksize = std::tr1::get<1>(GetParam()); + + Mat src(size, CV_8UC1); + Mat dx(size, CV_16SC1); + Mat dy(size, CV_16SC1); + + declare.in(src, WARMUP_RNG).out(dx, dy); + + TEST_CYCLE() spatialGradient(src, dx, dy, ksize); + + SANITY_CHECK(dx); + SANITY_CHECK(dy); +} + From 815cd8970d653cd1fcfd54e84af172d0701def14 Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Fri, 19 Jun 2015 04:46:17 +0200 Subject: [PATCH 045/133] spatialGradient: Remove unnecessary index calculation --- modules/imgproc/src/spatialgradient.cpp | 3 ++- modules/imgproc/test/test_filter.cpp | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/imgproc/src/spatialgradient.cpp b/modules/imgproc/src/spatialgradient.cpp index 7b35e249d9..7afcda52b2 100644 --- a/modules/imgproc/src/spatialgradient.cpp +++ b/modules/imgproc/src/spatialgradient.cpp @@ -220,7 +220,6 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksi for ( i = 1; i < H - 1; i++ ) { // Load last row for 3x3 Sobel filter - idx = i*W + j; v_um = v_load(&p_src[idx + W - 1]); v_un = v_load(&p_src[idx + W]); v_up = v_load(&p_src[idx + W + 1]); @@ -261,6 +260,8 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksi v_snn2 = v_spn2; v_snp1 = v_spp1; v_snp2 = v_spp2; + + idx += W; } } diff --git a/modules/imgproc/test/test_filter.cpp b/modules/imgproc/test/test_filter.cpp index 968d01eda9..0c98ed35c5 100644 --- a/modules/imgproc/test/test_filter.cpp +++ b/modules/imgproc/test/test_filter.cpp @@ -602,7 +602,6 @@ void CV_SpatialGradientTest::run_func() test_mat[OUTPUT][1] = dy; } - void CV_SpatialGradientTest::prepare_to_validation( int /*test_case_idx*/ ) { int dx, dy; From e633c991b0ab03a5fbbcb6ab58089fd34be17fbc Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Fri, 19 Jun 2015 04:57:01 +0200 Subject: [PATCH 046/133] spatialGradient: Doc, fix dangling newline error --- modules/imgproc/include/opencv2/imgproc.hpp | 17 ++++++++++++++--- modules/imgproc/perf/perf_spatialgradient.cpp | 1 - 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index c93975eeb8..947dfc3029 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1369,14 +1369,25 @@ CV_EXPORTS_W void Sobel( InputArray src, OutputArray dst, int ddepth, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT ); -/** @brief TODO +/** @brief Calculates the first order image derivative in both x and y using a Sobel operator -TODO +Equivalent to calling: +@code +Sobel( src, dx, CV_16SC1, 1, 0, 3 ); +Sobel( src, dy, CV_16SC1, 0, 1, 3 ); +@endcode + +@param src input image. +@param dx output image with first-order derivative in x. +@param dy output image with first-order derivative in y. +@param ksize size of Sobel kernel. It must be 3. + +@sa Sobel */ CV_EXPORTS_W void spatialGradient( InputArray src, OutputArray dx, - OutputArray dy, int ksize ); + OutputArray dy, int ksize = 3 ); /** @brief Calculates the first x- or y- image derivative using Scharr operator. diff --git a/modules/imgproc/perf/perf_spatialgradient.cpp b/modules/imgproc/perf/perf_spatialgradient.cpp index 31219a3d92..87456146de 100644 --- a/modules/imgproc/perf/perf_spatialgradient.cpp +++ b/modules/imgproc/perf/perf_spatialgradient.cpp @@ -31,4 +31,3 @@ PERF_TEST_P( Size_Ksize, spatialGradient, SANITY_CHECK(dx); SANITY_CHECK(dy); } - From 217dd63e0204032b3de7f94dc20c7d790d0b7914 Mon Sep 17 00:00:00 2001 From: Vladimir Dudnik Date: Wed, 27 May 2015 23:22:33 +0300 Subject: [PATCH 047/133] OpenCV-OpenCL interop (PR #4072): Commits: added new function, cv::ocl::attachContext(String& platformName, void* platformID, void* context, void* deviceID) which allow to attach externally created OpenCL context to OpenCV. add definitions of clRetainDevice, clRetainContext funcs removed definitions for clRetainContext, clRetainDevice fixed build issue under Linux fixed uninitialized vars, replace dbgassert in error handling remove function which is not ready yet add new function, cv::ocl::convertFromBuffer(int rows, int cols, int type, void* cl_mem_obj, UMat& dst, UMatUsageFlags usageFlags = cv::USAGE_DEFAULT) which attaches user allocated OpenCL clBuffer to UMat uncommented clGetMemObjectInfo definition (otherwise prevent opencv build) fixed build issue on linux and android add step parameter to cv::ocl::convertFromBuffer func suppress compile-time warning added sample opencl-opencv interoperability (showcase for cv::ocl::convertFromBuffer func) CMakeLists.txt modified to not create sample build script if OpenCL SDK not found in system fixed build issue (apple opencl include dir and spaces in CMake file) added call to clRetainContext for attachContext func and call to clRetainMemObject for convertFromBuffer func uncommented clRetainMemObject definition added comments and cleanup add local path to cmake modules search dirs (instead of replacing) remove REQUIRED for find_package call (sample build together with opencv). need to try standalone sample build opencl-interop sample moved to standalone build set minimum version requirement for sample's cmake to 3.1 put cmake_minimum_required under condition, so do not check if samples not builded remove code dups for setSize, updateContinuityFlag, and finalizeHdr commented out cmake_minimum_required(VERSION 3.1) add safety check for cmake version add convertFromImage func and update opencl-interop sample uncommented clGetImageInfo defs uncommented clEnqueueCopyImageToBuffer defs fixed clEnqueueCopyImageToBuffer defs add doxygen comments remove doxygen @fn tag try to restart buildbot add doxygen comments to directx interop funcs remove internal header, use fwd declarations in affected compile units instead --- modules/core/include/opencv2/core/directx.hpp | 74 +- modules/core/include/opencv2/core/ocl.hpp | 52 + modules/core/src/ocl.cpp | 262 ++++- modules/core/src/umatrix.cpp | 17 +- samples/CMakeLists.txt | 2 + samples/opencl/CMakeLists.txt | 68 ++ samples/opencl/opencl-opencv-interop.cpp | 966 ++++++++++++++++++ 7 files changed, 1423 insertions(+), 18 deletions(-) create mode 100644 samples/opencl/CMakeLists.txt create mode 100644 samples/opencl/opencl-opencv-interop.cpp diff --git a/modules/core/include/opencv2/core/directx.hpp b/modules/core/include/opencv2/core/directx.hpp index 837548e51b..275df720de 100644 --- a/modules/core/include/opencv2/core/directx.hpp +++ b/modules/core/include/opencv2/core/directx.hpp @@ -71,9 +71,28 @@ using namespace cv::ocl; //! @{ // TODO static functions in the Context class +//! @brief Creates OpenCL context from D3D11 device +// +//! @param pD3D11Device - pointer to D3D11 device +//! @return Returns reference to OpenCL Context CV_EXPORTS Context& initializeContextFromD3D11Device(ID3D11Device* pD3D11Device); + +//! @brief Creates OpenCL context from D3D10 device +// +//! @param pD3D10Device - pointer to D3D10 device +//! @return Returns reference to OpenCL Context CV_EXPORTS Context& initializeContextFromD3D10Device(ID3D10Device* pD3D10Device); + +//! @brief Creates OpenCL context from Direct3DDevice9Ex device +// +//! @param pDirect3DDevice9Ex - pointer to Direct3DDevice9Ex device +//! @return Returns reference to OpenCL Context CV_EXPORTS Context& initializeContextFromDirect3DDevice9Ex(IDirect3DDevice9Ex* pDirect3DDevice9Ex); + +//! @brief Creates OpenCL context from Direct3DDevice9 device +// +//! @param pDirect3DDevice9 - pointer to Direct3Device9 device +//! @return Returns reference to OpenCL Context CV_EXPORTS Context& initializeContextFromDirect3DDevice9(IDirect3DDevice9* pDirect3DDevice9); //! @} @@ -83,19 +102,70 @@ CV_EXPORTS Context& initializeContextFromDirect3DDevice9(IDirect3DDevice9* pDire //! @addtogroup core_directx //! @{ +//! @brief Converts InputArray to ID3D11Texture2D +// +//! @note Note: function does memory copy from src to +//! pD3D11Texture2D +// +//! @param src - source InputArray +//! @param pD3D11Texture2D - destination D3D11 texture CV_EXPORTS void convertToD3D11Texture2D(InputArray src, ID3D11Texture2D* pD3D11Texture2D); + +//! @brief Converts ID3D11Texture2D to OutputArray +// +//! @note Note: function does memory copy from pD3D11Texture2D +//! to dst +// +//! @param pD3D11Texture2D - source D3D11 texture +//! @param dst - destination OutputArray CV_EXPORTS void convertFromD3D11Texture2D(ID3D11Texture2D* pD3D11Texture2D, OutputArray dst); +//! @brief Converts InputArray to ID3D10Texture2D +// +//! @note Note: function does memory copy from src to +//! pD3D10Texture2D +// +//! @param src - source InputArray +//! @param pD3D10Texture2D - destination D3D10 texture CV_EXPORTS void convertToD3D10Texture2D(InputArray src, ID3D10Texture2D* pD3D10Texture2D); + +//! @brief Converts ID3D10Texture2D to OutputArray +// +//! @note Note: function does memory copy from pD3D10Texture2D +//! to dst +// +//! @param pD3D10Texture2D - source D3D10 texture +//! @param dst - destination OutputArray CV_EXPORTS void convertFromD3D10Texture2D(ID3D10Texture2D* pD3D10Texture2D, OutputArray dst); +//! @brief Converts InputArray to IDirect3DSurface9 +// +//! @note Note: function does memory copy from src to +//! pDirect3DSurface9 +// +//! @param src - source InputArray +//! @param pDirect3DSurface9 - destination D3D10 texture +//! @param surfaceSharedHandle - shared handle CV_EXPORTS void convertToDirect3DSurface9(InputArray src, IDirect3DSurface9* pDirect3DSurface9, void* surfaceSharedHandle = NULL); + +//! @brief Converts IDirect3DSurface9 to OutputArray +// +//! @note Note: function does memory copy from pDirect3DSurface9 +//! to dst +// +//! @param pDirect3DSurface9 - source D3D10 texture +//! @param dst - destination OutputArray +//! @param surfaceSharedHandle - shared handle CV_EXPORTS void convertFromDirect3DSurface9(IDirect3DSurface9* pDirect3DSurface9, OutputArray dst, void* surfaceSharedHandle = NULL); -// Get OpenCV type from DirectX type, return -1 if there is no equivalent +//! @brief Get OpenCV type from DirectX type +//! @param iDXGI_FORMAT - enum DXGI_FORMAT for D3D10/D3D11 +//! @return OpenCV type or -1 if there is no equivalent CV_EXPORTS int getTypeFromDXGI_FORMAT(const int iDXGI_FORMAT); // enum DXGI_FORMAT for D3D10/D3D11 -// Get OpenCV type from DirectX type, return -1 if there is no equivalent +//! @brief Get OpenCV type from DirectX type +//! @param iD3DFORMAT - enum D3DTYPE for D3D9 +//! @return OpenCV type or -1 if there is no equivalent CV_EXPORTS int getTypeFromD3DFORMAT(const int iD3DFORMAT); // enum D3DTYPE for D3D9 //! @} diff --git a/modules/core/include/opencv2/core/ocl.hpp b/modules/core/include/opencv2/core/ocl.hpp index 173722f61f..bc989a3285 100644 --- a/modules/core/include/opencv2/core/ocl.hpp +++ b/modules/core/include/opencv2/core/ocl.hpp @@ -276,6 +276,58 @@ protected: Impl* p; }; +/* +//! @brief Attaches OpenCL context to OpenCV +// +//! @note Note: +// OpenCV will check if available OpenCL platform has platformName name, +// then assign context to OpenCV and call clRetainContext function. +// The deviceID device will be used as target device and new command queue +// will be created. +// +// Params: +//! @param platformName - name of OpenCL platform to attach, +//! this string is used to check if platform is available +//! to OpenCV at runtime +//! @param platfromID - ID of platform attached context was created for +//! @param context - OpenCL context to be attached to OpenCV +//! @param deviceID - ID of device, must be created from attached context +*/ +CV_EXPORTS void attachContext(const String& platformName, void* platformID, void* context, void* deviceID); + +/* +//! @brief Convert OpenCL buffer to UMat +// +//! @note Note: +// OpenCL buffer (cl_mem_buffer) should contain 2D image data, compatible with OpenCV. +// Memory content is not copied from clBuffer to UMat. Instead, buffer handle assigned +// to UMat and clRetainMemObject is called. +// +// Params: +//! @param cl_mem_buffer - source clBuffer handle +//! @param step - num of bytes in single row +//! @param rows - number of rows +//! @param cols - number of cols +//! @param type - OpenCV type of image +//! @param dst - destination UMat +*/ +CV_EXPORTS void convertFromBuffer(void* cl_mem_buffer, size_t step, int rows, int cols, int type, UMat& dst); + +/* +//! @brief Convert OpenCL image2d_t to UMat +// +//! @note Note: +// OpenCL image2d_t (cl_mem_image), should be compatible with OpenCV +// UMat formats. +// Memory content is copied from image to UMat with +// clEnqueueCopyImageToBuffer function. +// +// Params: +//! @param cl_mem_image - source image2d_t handle +//! @param dst - destination UMat +*/ +CV_EXPORTS void convertFromImage(void* cl_mem_image, UMat& dst); + // TODO Move to internal header void initializeContextFromHandle(Context& ctx, void* platform, void* context, void* device); diff --git a/modules/core/src/ocl.cpp b/modules/core/src/ocl.cpp index 5d68a36832..4ee770c679 100644 --- a/modules/core/src/ocl.cpp +++ b/modules/core/src/ocl.cpp @@ -858,9 +858,9 @@ OCL_FUNC_P(cl_context, clCreateContext, OCL_FUNC(cl_int, clReleaseContext, (cl_context context), (context)) -/* -OCL_FUNC(cl_int, clRetainContext, (cl_context context), (context)) +OCL_FUNC(cl_int, clRetainContext, (cl_context context), (context)) +/* OCL_FUNC_P(cl_context, clCreateContextFromType, (const cl_context_properties * properties, cl_device_type device_type, @@ -945,7 +945,6 @@ OCL_FUNC(cl_int, clGetSupportedImageFormats, (context, flags, image_type, num_entries, image_formats, num_image_formats)) -/* OCL_FUNC(cl_int, clGetMemObjectInfo, (cl_mem memobj, cl_mem_info param_name, @@ -962,6 +961,7 @@ OCL_FUNC(cl_int, clGetImageInfo, size_t * param_value_size_ret), (image, param_name, param_value_size, param_value, param_value_size_ret)) +/* OCL_FUNC(cl_int, clCreateKernelsInProgram, (cl_program program, cl_uint num_kernels, @@ -1038,20 +1038,20 @@ OCL_FUNC(cl_int, clEnqueueCopyImage, cl_event * event), (command_queue, src_image, dst_image, src_origin, dst_origin, region, num_events_in_wait_list, event_wait_list, event)) +*/ OCL_FUNC(cl_int, clEnqueueCopyImageToBuffer, (cl_command_queue command_queue, cl_mem src_image, cl_mem dst_buffer, - const size_t * src_origin[3], - const size_t * region[3], + const size_t * src_origin, + const size_t * region, size_t dst_offset, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event), (command_queue, src_image, dst_buffer, src_origin, region, dst_offset, num_events_in_wait_list, event_wait_list, event)) -*/ OCL_FUNC(cl_int, clEnqueueCopyBufferToImage, (cl_command_queue command_queue, @@ -1100,10 +1100,10 @@ OCL_FUNC(cl_int, clGetKernelInfo, size_t * param_value_size_ret), (kernel, param_name, param_value_size, param_value, param_value_size_ret)) -OCL_FUNC(cl_int, clRetainMemObject, (cl_mem memobj), (memobj)) - */ +OCL_FUNC(cl_int, clRetainMemObject, (cl_mem memobj), (memobj)) + OCL_FUNC(cl_int, clReleaseMemObject, (cl_mem memobj), (memobj)) @@ -1348,7 +1348,7 @@ OCL_FUNC(cl_int, clReleaseEvent, (cl_event event), (event)) #define CL_VERSION_1_2 #endif -#endif +#endif // HAVE_OPENCL #ifdef _DEBUG #define CV_OclDbgAssert CV_DbgAssert @@ -2925,6 +2925,83 @@ CV_EXPORTS bool useSVM(UMatUsageFlags usageFlags) #endif // HAVE_OPENCL_SVM +static void get_platform_name(cl_platform_id id, String& name) +{ + // get platform name string length + size_t sz = 0; + if (CL_SUCCESS != clGetPlatformInfo(id, CL_PLATFORM_NAME, 0, 0, &sz)) + CV_ErrorNoReturn(cv::Error::OpenCLApiCallError, "clGetPlatformInfo failed!"); + + // get platform name string + AutoBuffer buf(sz + 1); + if (CL_SUCCESS != clGetPlatformInfo(id, CL_PLATFORM_NAME, sz, buf, 0)) + CV_ErrorNoReturn(cv::Error::OpenCLApiCallError, "clGetPlatformInfo failed!"); + + // just in case, ensure trailing zero for ASCIIZ string + buf[sz] = 0; + + name = (const char*)buf; +} + +/* +// Attaches OpenCL context to OpenCV +*/ +void attachContext(const String& platformName, void* platformID, void* context, void* deviceID) +{ + cl_uint cnt = 0; + + if(CL_SUCCESS != clGetPlatformIDs(0, 0, &cnt)) + CV_ErrorNoReturn(cv::Error::OpenCLApiCallError, "clGetPlatformIDs failed!"); + + if (cnt == 0) + CV_ErrorNoReturn(cv::Error::OpenCLApiCallError, "no OpenCL platform available!"); + + std::vector platforms(cnt); + + if(CL_SUCCESS != clGetPlatformIDs(cnt, &platforms[0], 0)) + CV_ErrorNoReturn(cv::Error::OpenCLApiCallError, "clGetPlatformIDs failed!"); + + bool platformAvailable = false; + + // check if external platformName contained in list of available platforms in OpenCV + for (unsigned int i = 0; i < cnt; i++) + { + String availablePlatformName; + get_platform_name(platforms[i], availablePlatformName); + // external platform is found in the list of available platforms + if (platformName == availablePlatformName) + { + platformAvailable = true; + break; + } + } + + if (!platformAvailable) + CV_ErrorNoReturn(cv::Error::OpenCLApiCallError, "No matched platforms available!"); + + // check if platformID corresponds to platformName + String actualPlatformName; + get_platform_name((cl_platform_id)platformID, actualPlatformName); + if (platformName != actualPlatformName) + CV_ErrorNoReturn(cv::Error::OpenCLApiCallError, "No matched platforms available!"); + + // do not initialize OpenCL context + Context ctx = Context::getDefault(false); + + // attach supplied context to OpenCV + initializeContextFromHandle(ctx, platformID, context, deviceID); + + if(CL_SUCCESS != clRetainContext((cl_context)context)) + CV_ErrorNoReturn(cv::Error::OpenCLApiCallError, "clRetainContext failed!"); + + // clear command queue, if any + getCoreTlsData().get()->oclQueue.finish(); + Queue q; + getCoreTlsData().get()->oclQueue = q; + + return; +} // attachContext() + void initializeContextFromHandle(Context& ctx, void* platform, void* _context, void* _device) { @@ -3150,10 +3227,10 @@ struct Kernel::Impl bool haveTempDstUMats; }; -}} +}} // namespace cv::ocl + +extern "C" { -extern "C" -{ static void CL_CALLBACK oclCleanupCallback(cl_event, cl_int, void *p) { ((cv::ocl::Kernel::Impl*)p)->finit(); @@ -5166,6 +5243,167 @@ MatAllocator* getOpenCLAllocator() return allocator; } +}} // namespace cv::ocl + + +namespace cv { + +// three funcs below are implemented in umatrix.cpp +void setSize( UMat& m, int _dims, const int* _sz, const size_t* _steps, + bool autoSteps = false ); + +void updateContinuityFlag(UMat& m); +void finalizeHdr(UMat& m); + +} // namespace cv + + +namespace cv { namespace ocl { + +/* +// Convert OpenCL buffer memory to UMat +*/ +void convertFromBuffer(void* cl_mem_buffer, size_t step, int rows, int cols, int type, UMat& dst) +{ + int d = 2; + int sizes[] = { rows, cols }; + + CV_Assert(0 <= d && d <= CV_MAX_DIM); + + dst.release(); + + dst.flags = (type & Mat::TYPE_MASK) | Mat::MAGIC_VAL; + dst.usageFlags = USAGE_DEFAULT; + + setSize(dst, d, sizes, 0, true); + dst.offset = 0; + + cl_mem memobj = (cl_mem)cl_mem_buffer; + cl_mem_object_type mem_type = 0; + + CV_Assert(clGetMemObjectInfo(memobj, CL_MEM_TYPE, sizeof(cl_mem_object_type), &mem_type, 0) == CL_SUCCESS); + + CV_Assert(CL_MEM_OBJECT_BUFFER == mem_type); + + size_t total = 0; + CV_Assert(clGetMemObjectInfo(memobj, CL_MEM_SIZE, sizeof(size_t), &total, 0) == CL_SUCCESS); + + CV_Assert(clRetainMemObject(memobj) == CL_SUCCESS); + + CV_Assert((int)step >= cols * CV_ELEM_SIZE(type)); + CV_Assert(total >= rows * step); + + // attach clBuffer to UMatData + dst.u = new UMatData(getOpenCLAllocator()); + dst.u->data = 0; + dst.u->allocatorFlags_ = 0; // not allocated from any OpenCV buffer pool + dst.u->flags = 0; + dst.u->handle = cl_mem_buffer; + dst.u->origdata = 0; + dst.u->prevAllocator = 0; + dst.u->size = total; + + finalizeHdr(dst); + dst.addref(); + + return; +} // convertFromBuffer() + + +/* +// Convert OpenCL image2d_t memory to UMat +*/ +void convertFromImage(void* cl_mem_image, UMat& dst) +{ + cl_mem clImage = (cl_mem)cl_mem_image; + cl_mem_object_type mem_type = 0; + + CV_Assert(clGetMemObjectInfo(clImage, CL_MEM_TYPE, sizeof(cl_mem_object_type), &mem_type, 0) == CL_SUCCESS); + + CV_Assert(CL_MEM_OBJECT_IMAGE2D == mem_type); + + cl_image_format fmt = { 0, 0 }; + CV_Assert(clGetImageInfo(clImage, CL_IMAGE_FORMAT, sizeof(cl_image_format), &fmt, 0) == CL_SUCCESS); + + int depth = CV_8U; + switch (fmt.image_channel_data_type) + { + case CL_UNORM_INT8: + case CL_UNSIGNED_INT8: + depth = CV_8U; + break; + + case CL_SNORM_INT8: + case CL_SIGNED_INT8: + depth = CV_8S; + break; + + case CL_UNORM_INT16: + case CL_UNSIGNED_INT16: + depth = CV_16U; + break; + + case CL_SNORM_INT16: + case CL_SIGNED_INT16: + depth = CV_16S; + break; + + case CL_SIGNED_INT32: + depth = CV_32S; + break; + + case CL_FLOAT: + depth = CV_32F; + break; + + default: + CV_Error(cv::Error::OpenCLApiCallError, "Not supported image_channel_data_type"); + } + + int type = CV_8UC1; + switch (fmt.image_channel_order) + { + case CL_R: + type = CV_MAKE_TYPE(depth, 1); + break; + + case CL_RGBA: + case CL_BGRA: + case CL_ARGB: + type = CV_MAKE_TYPE(depth, 4); + break; + + default: + CV_Error(cv::Error::OpenCLApiCallError, "Not supported image_channel_order"); + break; + } + + size_t step = 0; + CV_Assert(clGetImageInfo(clImage, CL_IMAGE_ROW_PITCH, sizeof(size_t), &step, 0) == CL_SUCCESS); + + size_t w = 0; + CV_Assert(clGetImageInfo(clImage, CL_IMAGE_WIDTH, sizeof(size_t), &w, 0) == CL_SUCCESS); + + size_t h = 0; + CV_Assert(clGetImageInfo(clImage, CL_IMAGE_HEIGHT, sizeof(size_t), &h, 0) == CL_SUCCESS); + + dst.create((int)h, (int)w, type); + + cl_mem clBuffer = (cl_mem)dst.handle(ACCESS_READ); + + cl_command_queue q = (cl_command_queue)Queue::getDefault().ptr(); + + size_t offset = 0; + size_t src_origin[3] = { 0, 0, 0 }; + size_t region[3] = { w, h, 1 }; + CV_Assert(clEnqueueCopyImageToBuffer(q, clImage, clBuffer, src_origin, region, offset, 0, NULL, NULL) == CL_SUCCESS); + + CV_Assert(clFinish(q) == CL_SUCCESS); + + return; +} // convertFromImage() + + ///////////////////////////////////////////// Utility functions ///////////////////////////////////////////////// static void getDevices(std::vector& devices, cl_platform_id platform) diff --git a/modules/core/src/umatrix.cpp b/modules/core/src/umatrix.cpp index 1b42f1ee1e..48aa86635d 100644 --- a/modules/core/src/umatrix.cpp +++ b/modules/core/src/umatrix.cpp @@ -46,6 +46,13 @@ namespace cv { +// forward decls, implementation is below in this file +void setSize(UMat& m, int _dims, const int* _sz, const size_t* _steps, + bool autoSteps = false); + +void updateContinuityFlag(UMat& m); +void finalizeHdr(UMat& m); + // it should be a prime number for the best hash function enum { UMAT_NLOCKS = 31 }; static Mutex umatLocks[UMAT_NLOCKS]; @@ -123,8 +130,8 @@ void swap( UMat& a, UMat& b ) } -static inline void setSize( UMat& m, int _dims, const int* _sz, - const size_t* _steps, bool autoSteps=false ) +void setSize( UMat& m, int _dims, const int* _sz, + const size_t* _steps, bool autoSteps ) { CV_Assert( 0 <= _dims && _dims <= CV_MAX_DIM ); if( m.dims != _dims ) @@ -176,7 +183,8 @@ static inline void setSize( UMat& m, int _dims, const int* _sz, } } -static void updateContinuityFlag(UMat& m) + +void updateContinuityFlag(UMat& m) { int i, j; for( i = 0; i < m.dims; i++ ) @@ -199,7 +207,7 @@ static void updateContinuityFlag(UMat& m) } -static void finalizeHdr(UMat& m) +void finalizeHdr(UMat& m) { updateContinuityFlag(m); int d = m.dims; @@ -207,6 +215,7 @@ static void finalizeHdr(UMat& m) m.rows = m.cols = -1; } + UMat Mat::getUMat(int accessFlags, UMatUsageFlags usageFlags) const { UMat hdr; diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 467ca162a7..ef6cd772f4 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -66,6 +66,8 @@ endif() add_subdirectory(cpp) # FIXIT: can't use cvconfig.h in samples: add_subdirectory(gpu) +add_subdirectory(opencl) + if(WIN32) add_subdirectory(directx) endif() diff --git a/samples/opencl/CMakeLists.txt b/samples/opencl/CMakeLists.txt new file mode 100644 index 0000000000..a4525650e1 --- /dev/null +++ b/samples/opencl/CMakeLists.txt @@ -0,0 +1,68 @@ +# cmake 3.1 needed for find_package(OpenCL) + +if(CMAKE_VERSION VERSION_LESS "3.1") + message(STATUS "OpenCL samples require CMakes 3.1+") + return() +endif() + +set( + OPENCV_OPENCL_SAMPLES_REQUIRED_DEPS + opencv_core + opencv_imgproc + opencv_video + opencv_imgcodecs + opencv_videoio + opencv_highgui) + +ocv_check_dependencies(${OPENCV_OPENCL_SAMPLES_REQUIRED_DEPS}) + +if(BUILD_EXAMPLES AND OCV_DEPENDENCIES_FOUND) + + find_package(OpenCL 1.2 REQUIRED) + + set(project "opencl") + string(TOUPPER "${project}" project_upper) + + project("${project}_samples") + + ocv_include_modules_recurse(${OPENCV_OPENCL_SAMPLES_REQUIRED_DEPS}) + + include_directories(${OpenCL_INCLUDE_DIR}) + + # --------------------------------------------- + # Define executable targets + # --------------------------------------------- + MACRO(OPENCV_DEFINE_OPENCL_EXAMPLE name srcs) + set(the_target "example_${project}_${name}") + add_executable(${the_target} ${srcs}) + + ocv_target_link_libraries( + ${the_target} + ${OPENCV_LINKER_LIBS} + ${OPENCV_OPENCL_SAMPLES_REQUIRED_DEPS} + ${OpenCL_LIBRARY}) + + set_target_properties(${the_target} PROPERTIES + OUTPUT_NAME "${project}-example-${name}" + PROJECT_LABEL "(EXAMPLE_${project_upper}) ${name}") + + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${the_target} PROPERTIES FOLDER "samples//${project}") + endif() + + if(WIN32) + if(MSVC AND NOT BUILD_SHARED_LIBS) + set_target_properties(${the_target} PROPERTIES LINK_FLAGS "/NODEFAULTLIB:atlthunk.lib /NODEFAULTLIB:atlsd.lib /DEBUG") + endif() + install(TARGETS ${the_target} RUNTIME DESTINATION "${OPENCV_SAMPLES_BIN_INSTALL_PATH}/${project}" COMPONENT main) + endif() + ENDMACRO() + + file(GLOB all_samples RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp) + + foreach(sample_filename ${all_samples}) + get_filename_component(sample ${sample_filename} NAME_WE) + file(GLOB sample_srcs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${sample}.*) + OPENCV_DEFINE_OPENCL_EXAMPLE(${sample} ${sample_srcs}) + endforeach() +endif() diff --git a/samples/opencl/opencl-opencv-interop.cpp b/samples/opencl/opencl-opencv-interop.cpp new file mode 100644 index 0000000000..37a902e0dd --- /dev/null +++ b/samples/opencl/opencl-opencv-interop.cpp @@ -0,0 +1,966 @@ +/* +// The example of interoperability between OpenCL and OpenCV. +// This will loop through frames of video either from input media file +// or camera device and do processing of these data in OpenCL and then +// in OpenCV. In OpenCL it does inversion of pixels in half of frame and +// in OpenCV it does bluring the whole frame. +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#if __APPLE__ +#include +#else +#include +#endif + +#include +#include +#include +#include +#include + + +using namespace std; +using namespace cv; + +namespace opencl { + +class PlatformInfo +{ +public: + PlatformInfo() + {} + + ~PlatformInfo() + {} + + cl_int QueryInfo(cl_platform_id id) + { + query_param(id, CL_PLATFORM_PROFILE, m_profile); + query_param(id, CL_PLATFORM_VERSION, m_version); + query_param(id, CL_PLATFORM_NAME, m_name); + query_param(id, CL_PLATFORM_VENDOR, m_vendor); + query_param(id, CL_PLATFORM_EXTENSIONS, m_extensions); + return CL_SUCCESS; + } + + std::string Profile() { return m_profile; } + std::string Version() { return m_version; } + std::string Name() { return m_name; } + std::string Vendor() { return m_vendor; } + std::string Extensions() { return m_extensions; } + +private: + cl_int query_param(cl_platform_id id, cl_platform_info param, std::string& paramStr) + { + cl_int res; + + size_t psize; + cv::AutoBuffer buf; + + res = clGetPlatformInfo(id, param, 0, 0, &psize); + if (CL_SUCCESS != res) + throw std::runtime_error(std::string("clGetPlatformInfo failed")); + + buf.resize(psize); + res = clGetPlatformInfo(id, param, psize, buf, 0); + if (CL_SUCCESS != res) + throw std::runtime_error(std::string("clGetPlatformInfo failed")); + + // just in case, ensure trailing zero for ASCIIZ string + buf[psize] = 0; + + paramStr = buf; + + return CL_SUCCESS; + } + +private: + std::string m_profile; + std::string m_version; + std::string m_name; + std::string m_vendor; + std::string m_extensions; +}; + + +class DeviceInfo +{ +public: + DeviceInfo() + {} + + ~DeviceInfo() + {} + + cl_int QueryInfo(cl_device_id id) + { + query_param(id, CL_DEVICE_TYPE, m_type); + query_param(id, CL_DEVICE_VENDOR_ID, m_vendor_id); + query_param(id, CL_DEVICE_MAX_COMPUTE_UNITS, m_max_compute_units); + query_param(id, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, m_max_work_item_dimensions); + query_param(id, CL_DEVICE_MAX_WORK_ITEM_SIZES, m_max_work_item_sizes); + query_param(id, CL_DEVICE_MAX_WORK_GROUP_SIZE, m_max_work_group_size); + query_param(id, CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, m_preferred_vector_width_char); + query_param(id, CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, m_preferred_vector_width_short); + query_param(id, CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, m_preferred_vector_width_int); + query_param(id, CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, m_preferred_vector_width_long); + query_param(id, CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, m_preferred_vector_width_float); + query_param(id, CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, m_preferred_vector_width_double); +#if defined(CL_VERSION_1_1) + query_param(id, CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF, m_preferred_vector_width_half); + query_param(id, CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR, m_native_vector_width_char); + query_param(id, CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT, m_native_vector_width_short); + query_param(id, CL_DEVICE_NATIVE_VECTOR_WIDTH_INT, m_native_vector_width_int); + query_param(id, CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG, m_native_vector_width_long); + query_param(id, CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT, m_native_vector_width_float); + query_param(id, CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE, m_native_vector_width_double); + query_param(id, CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF, m_native_vector_width_half); +#endif + query_param(id, CL_DEVICE_MAX_CLOCK_FREQUENCY, m_max_clock_frequency); + query_param(id, CL_DEVICE_ADDRESS_BITS, m_address_bits); + query_param(id, CL_DEVICE_MAX_MEM_ALLOC_SIZE, m_max_mem_alloc_size); + query_param(id, CL_DEVICE_IMAGE_SUPPORT, m_image_support); + query_param(id, CL_DEVICE_MAX_READ_IMAGE_ARGS, m_max_read_image_args); + query_param(id, CL_DEVICE_MAX_WRITE_IMAGE_ARGS, m_max_write_image_args); +#if defined(CL_VERSION_2_0) + query_param(id, CL_DEVICE_MAX_READ_WRITE_IMAGE_ARGS, m_max_read_write_image_args); +#endif + query_param(id, CL_DEVICE_IMAGE2D_MAX_WIDTH, m_image2d_max_width); + query_param(id, CL_DEVICE_IMAGE2D_MAX_HEIGHT, m_image2d_max_height); + query_param(id, CL_DEVICE_IMAGE3D_MAX_WIDTH, m_image3d_max_width); + query_param(id, CL_DEVICE_IMAGE3D_MAX_HEIGHT, m_image3d_max_height); + query_param(id, CL_DEVICE_IMAGE3D_MAX_DEPTH, m_image3d_max_depth); +#if defined(CL_VERSION_1_2) + query_param(id, CL_DEVICE_IMAGE_MAX_BUFFER_SIZE, m_image_max_buffer_size); + query_param(id, CL_DEVICE_IMAGE_MAX_ARRAY_SIZE, m_image_max_array_size); +#endif + query_param(id, CL_DEVICE_MAX_SAMPLERS, m_max_samplers); +#if defined(CL_VERSION_1_2) + query_param(id, CL_DEVICE_IMAGE_PITCH_ALIGNMENT, m_image_pitch_alignment); + query_param(id, CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT, m_image_base_address_alignment); +#endif +#if defined(CL_VERSION_2_0) + query_param(id, CL_DEVICE_MAX_PIPE_ARGS, m_max_pipe_args); + query_param(id, CL_DEVICE_PIPE_MAX_ACTIVE_RESERVATIONS, m_pipe_max_active_reservations); + query_param(id, CL_DEVICE_PIPE_MAX_PACKET_SIZE, m_pipe_max_packet_size); +#endif + query_param(id, CL_DEVICE_MAX_PARAMETER_SIZE, m_max_parameter_size); + query_param(id, CL_DEVICE_MEM_BASE_ADDR_ALIGN, m_mem_base_addr_align); + query_param(id, CL_DEVICE_SINGLE_FP_CONFIG, m_single_fp_config); +#if defined(CL_VERSION_1_2) + query_param(id, CL_DEVICE_DOUBLE_FP_CONFIG, m_double_fp_config); +#endif + query_param(id, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, m_global_mem_cache_type); + query_param(id, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, m_global_mem_cacheline_size); + query_param(id, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, m_global_mem_cache_size); + query_param(id, CL_DEVICE_GLOBAL_MEM_SIZE, m_global_mem_size); + query_param(id, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, m_max_constant_buffer_size); + query_param(id, CL_DEVICE_MAX_CONSTANT_ARGS, m_max_constant_args); +#if defined(CL_VERSION_2_0) + query_param(id, CL_DEVICE_MAX_GLOBAL_VARIABLE_SIZE, m_max_global_variable_size); + query_param(id, CL_DEVICE_GLOBAL_VARIABLE_PREFERRED_TOTAL_SIZE, m_global_variable_preferred_total_size); +#endif + query_param(id, CL_DEVICE_LOCAL_MEM_TYPE, m_local_mem_type); + query_param(id, CL_DEVICE_LOCAL_MEM_SIZE, m_local_mem_size); + query_param(id, CL_DEVICE_ERROR_CORRECTION_SUPPORT, m_error_correction_support); +#if defined(CL_VERSION_1_1) + query_param(id, CL_DEVICE_HOST_UNIFIED_MEMORY, m_host_unified_memory); +#endif + query_param(id, CL_DEVICE_PROFILING_TIMER_RESOLUTION, m_profiling_timer_resolution); + query_param(id, CL_DEVICE_ENDIAN_LITTLE, m_endian_little); + query_param(id, CL_DEVICE_AVAILABLE, m_available); + query_param(id, CL_DEVICE_COMPILER_AVAILABLE, m_compiler_available); +#if defined(CL_VERSION_1_2) + query_param(id, CL_DEVICE_LINKER_AVAILABLE, m_linker_available); +#endif + query_param(id, CL_DEVICE_EXECUTION_CAPABILITIES, m_execution_capabilities); + query_param(id, CL_DEVICE_QUEUE_PROPERTIES, m_queue_properties); +#if defined(CL_VERSION_2_0) + query_param(id, CL_DEVICE_QUEUE_ON_HOST_PROPERTIES, m_queue_on_host_properties); + query_param(id, CL_DEVICE_QUEUE_ON_DEVICE_PROPERTIES, m_queue_on_device_properties); + query_param(id, CL_DEVICE_QUEUE_ON_DEVICE_PREFERRED_SIZE, m_queue_on_device_preferred_size); + query_param(id, CL_DEVICE_QUEUE_ON_DEVICE_MAX_SIZE, m_queue_on_device_max_size); + query_param(id, CL_DEVICE_MAX_ON_DEVICE_QUEUES, m_max_on_device_queues); + query_param(id, CL_DEVICE_MAX_ON_DEVICE_EVENTS, m_max_on_device_events); +#endif +#if defined(CL_VERSION_1_2) + query_param(id, CL_DEVICE_BUILT_IN_KERNELS, m_built_in_kernels); +#endif + query_param(id, CL_DEVICE_PLATFORM, m_platform); + query_param(id, CL_DEVICE_NAME, m_name); + query_param(id, CL_DEVICE_VENDOR, m_vendor); + query_param(id, CL_DRIVER_VERSION, m_driver_version); + query_param(id, CL_DEVICE_PROFILE, m_profile); + query_param(id, CL_DEVICE_VERSION, m_version); +#if defined(CL_VERSION_1_1) + query_param(id, CL_DEVICE_OPENCL_C_VERSION, m_opencl_c_version); +#endif + query_param(id, CL_DEVICE_EXTENSIONS, m_extensions); +#if defined(CL_VERSION_1_2) + query_param(id, CL_DEVICE_PRINTF_BUFFER_SIZE, m_printf_buffer_size); + query_param(id, CL_DEVICE_PREFERRED_INTEROP_USER_SYNC, m_preferred_interop_user_sync); + query_param(id, CL_DEVICE_PARENT_DEVICE, m_parent_device); + query_param(id, CL_DEVICE_PARTITION_MAX_SUB_DEVICES, m_partition_max_sub_devices); + query_param(id, CL_DEVICE_PARTITION_PROPERTIES, m_partition_properties); + query_param(id, CL_DEVICE_PARTITION_AFFINITY_DOMAIN, m_partition_affinity_domain); + query_param(id, CL_DEVICE_PARTITION_TYPE, m_partition_type); + query_param(id, CL_DEVICE_REFERENCE_COUNT, m_reference_count); +#endif + return CL_SUCCESS; + } + + std::string Name() { return m_name; } + +private: + template + cl_int query_param(cl_device_id id, cl_device_info param, T& value) + { + cl_int res; + size_t size = 0; + + res = clGetDeviceInfo(id, param, 0, 0, &size); + if (CL_SUCCESS != res && size != 0) + throw std::runtime_error(std::string("clGetDeviceInfo failed")); + + if (0 == size) + return CL_SUCCESS; + + if (sizeof(T) != size) + throw std::runtime_error(std::string("clGetDeviceInfo: param size mismatch")); + + res = clGetDeviceInfo(id, param, size, &value, 0); + if (CL_SUCCESS != res) + throw std::runtime_error(std::string("clGetDeviceInfo failed")); + + return CL_SUCCESS; + } + + template + cl_int query_param(cl_device_id id, cl_device_info param, std::vector& value) + { + cl_int res; + size_t size; + + res = clGetDeviceInfo(id, param, 0, 0, &size); + if (CL_SUCCESS != res) + throw std::runtime_error(std::string("clGetDeviceInfo failed")); + + if (0 == size) + return CL_SUCCESS; + + value.resize(size / sizeof(T)); + + res = clGetDeviceInfo(id, param, size, &value[0], 0); + if (CL_SUCCESS != res) + throw std::runtime_error(std::string("clGetDeviceInfo failed")); + + return CL_SUCCESS; + } + + cl_int query_param(cl_device_id id, cl_device_info param, std::string& value) + { + cl_int res; + size_t size; + + res = clGetDeviceInfo(id, param, 0, 0, &size); + if (CL_SUCCESS != res) + throw std::runtime_error(std::string("clGetDeviceInfo failed")); + + value.resize(size + 1); + + res = clGetDeviceInfo(id, param, size, &value[0], 0); + if (CL_SUCCESS != res) + throw std::runtime_error(std::string("clGetDeviceInfo failed")); + + // just in case, ensure trailing zero for ASCIIZ string + value[size] = 0; + + return CL_SUCCESS; + } + +private: + cl_device_type m_type; + cl_uint m_vendor_id; + cl_uint m_max_compute_units; + cl_uint m_max_work_item_dimensions; + std::vector m_max_work_item_sizes; + size_t m_max_work_group_size; + cl_uint m_preferred_vector_width_char; + cl_uint m_preferred_vector_width_short; + cl_uint m_preferred_vector_width_int; + cl_uint m_preferred_vector_width_long; + cl_uint m_preferred_vector_width_float; + cl_uint m_preferred_vector_width_double; +#if defined(CL_VERSION_1_1) + cl_uint m_preferred_vector_width_half; + cl_uint m_native_vector_width_char; + cl_uint m_native_vector_width_short; + cl_uint m_native_vector_width_int; + cl_uint m_native_vector_width_long; + cl_uint m_native_vector_width_float; + cl_uint m_native_vector_width_double; + cl_uint m_native_vector_width_half; +#endif + cl_uint m_max_clock_frequency; + cl_uint m_address_bits; + cl_ulong m_max_mem_alloc_size; + cl_bool m_image_support; + cl_uint m_max_read_image_args; + cl_uint m_max_write_image_args; +#if defined(CL_VERSION_2_0) + cl_uint m_max_read_write_image_args; +#endif + size_t m_image2d_max_width; + size_t m_image2d_max_height; + size_t m_image3d_max_width; + size_t m_image3d_max_height; + size_t m_image3d_max_depth; +#if defined(CL_VERSION_1_2) + size_t m_image_max_buffer_size; + size_t m_image_max_array_size; +#endif + cl_uint m_max_samplers; +#if defined(CL_VERSION_1_2) + cl_uint m_image_pitch_alignment; + cl_uint m_image_base_address_alignment; +#endif +#if defined(CL_VERSION_2_0) + cl_uint m_max_pipe_args; + cl_uint m_pipe_max_active_reservations; + cl_uint m_pipe_max_packet_size; +#endif + size_t m_max_parameter_size; + cl_uint m_mem_base_addr_align; + cl_device_fp_config m_single_fp_config; +#if defined(CL_VERSION_1_2) + cl_device_fp_config m_double_fp_config; +#endif + cl_device_mem_cache_type m_global_mem_cache_type; + cl_uint m_global_mem_cacheline_size; + cl_ulong m_global_mem_cache_size; + cl_ulong m_global_mem_size; + cl_ulong m_max_constant_buffer_size; + cl_uint m_max_constant_args; +#if defined(CL_VERSION_2_0) + size_t m_max_global_variable_size; + size_t m_global_variable_preferred_total_size; +#endif + cl_device_local_mem_type m_local_mem_type; + cl_ulong m_local_mem_size; + cl_bool m_error_correction_support; +#if defined(CL_VERSION_1_1) + cl_bool m_host_unified_memory; +#endif + size_t m_profiling_timer_resolution; + cl_bool m_endian_little; + cl_bool m_available; + cl_bool m_compiler_available; +#if defined(CL_VERSION_1_2) + cl_bool m_linker_available; +#endif + cl_device_exec_capabilities m_execution_capabilities; + cl_command_queue_properties m_queue_properties; +#if defined(CL_VERSION_2_0) + cl_command_queue_properties m_queue_on_host_properties; + cl_command_queue_properties m_queue_on_device_properties; + cl_uint m_queue_on_device_preferred_size; + cl_uint m_queue_on_device_max_size; + cl_uint m_max_on_device_queues; + cl_uint m_max_on_device_events; +#endif +#if defined(CL_VERSION_1_2) + std::string m_built_in_kernels; +#endif + cl_platform_id m_platform; + std::string m_name; + std::string m_vendor; + std::string m_driver_version; + std::string m_profile; + std::string m_version; +#if defined(CL_VERSION_1_1) + std::string m_opencl_c_version; +#endif + std::string m_extensions; +#if defined(CL_VERSION_1_2) + size_t m_printf_buffer_size; + cl_bool m_preferred_interop_user_sync; + cl_device_id m_parent_device; + cl_uint m_partition_max_sub_devices; + std::vector m_partition_properties; + cl_device_affinity_domain m_partition_affinity_domain; + std::vector m_partition_type; + cl_uint m_reference_count; +#endif +}; + +} // namespace opencl + + +class App +{ +public: + App(CommandLineParser& cmd); + ~App(); + + int initOpenCL(); + int initVideoSource(); + + int process_frame_with_open_cl(cv::Mat& frame, bool use_buffer, cl_mem* cl_buffer); + int process_cl_buffer_with_opencv(cl_mem buffer, size_t step, int rows, int cols, int type, cv::UMat& u); + int process_cl_image_with_opencv(cl_mem image, cv::UMat& u); + + int run(); + + bool isRunning() { return m_running; } + bool doProcess() { return m_process; } + bool useBuffer() { return m_use_buffer; } + + void setRunning(bool running) { m_running = running; } + void setDoProcess(bool process) { m_process = process; } + void setUseBuffer(bool use_buffer) { m_use_buffer = use_buffer; } + +protected: + bool nextFrame(cv::Mat& frame) { return m_cap.read(frame); } + void handleKey(char key); + void timerStart(); + void timerEnd(); + std::string fpsStr() const; + std::string message() const; + +private: + bool m_running; + bool m_process; + bool m_use_buffer; + + int64 m_t0; + int64 m_t1; + double m_fps; + + string m_file_name; + int m_camera_id; + cv::VideoCapture m_cap; + cv::Mat m_frame; + cv::Mat m_frameGray; + + opencl::PlatformInfo m_platformInfo; + opencl::DeviceInfo m_deviceInfo; + std::vector m_platform_ids; + cl_context m_context; + cl_device_id m_device_id; + cl_command_queue m_queue; + cl_program m_program; + cl_kernel m_kernelBuf; + cl_kernel m_kernelImg; + cl_mem m_mem_obj; + cl_event m_event; +}; + + +App::App(CommandLineParser& cmd) +{ + cout << "\nPress ESC to exit\n" << endl; + cout << "\n 'p' to toggle ON/OFF processing\n" << endl; + cout << "\n SPACE to switch between OpenCL buffer/image\n" << endl; + + m_camera_id = cmd.get("camera"); + m_file_name = cmd.get("video"); + + m_running = false; + m_process = false; + m_use_buffer = false; + + m_context = 0; + m_device_id = 0; + m_queue = 0; + m_program = 0; + m_kernelBuf = 0; + m_kernelImg = 0; + m_mem_obj = 0; + m_event = 0; +} // ctor + + +App::~App() +{ + if (m_queue) + { + clFinish(m_queue); + clReleaseCommandQueue(m_queue); + m_queue = 0; + } + + if (m_program) + { + clReleaseProgram(m_program); + m_program = 0; + } + + if (m_mem_obj) + { + clReleaseMemObject(m_mem_obj); + m_mem_obj = 0; + } + + if (m_event) + { + clReleaseEvent(m_event); + } + + if (m_kernelBuf) + { + clReleaseKernel(m_kernelBuf); + m_kernelBuf = 0; + } + + if (m_kernelImg) + { + clReleaseKernel(m_kernelImg); + m_kernelImg = 0; + } + + if (m_device_id) + { + clReleaseDevice(m_device_id); + m_device_id = 0; + } + + if (m_context) + { + clReleaseContext(m_context); + m_context = 0; + } +} // dtor + + +int App::initOpenCL() +{ + cl_int res = CL_SUCCESS; + cl_uint num_entries = 0; + + res = clGetPlatformIDs(0, 0, &num_entries); + if (CL_SUCCESS != res) + return -1; + + m_platform_ids.resize(num_entries); + + res = clGetPlatformIDs(num_entries, &m_platform_ids[0], 0); + if (CL_SUCCESS != res) + return -1; + + unsigned int i; + + // create context from first platform with GPU device + for (i = 0; i < m_platform_ids.size(); i++) + { + cl_context_properties props[] = + { + CL_CONTEXT_PLATFORM, + (cl_context_properties)(m_platform_ids[i]), + 0 + }; + + m_context = clCreateContextFromType(props, CL_DEVICE_TYPE_GPU, 0, 0, &res); + if (0 == m_context || CL_SUCCESS != res) + continue; + + res = clGetContextInfo(m_context, CL_CONTEXT_DEVICES, sizeof(cl_device_id), &m_device_id, 0); + if (CL_SUCCESS != res) + return -1; + + m_queue = clCreateCommandQueue(m_context, m_device_id, 0, &res); + if (0 == m_queue || CL_SUCCESS != res) + return -1; + + const char* kernelSrc = + "__kernel " + "void bitwise_inv_buf_8uC1(" + " __global unsigned char* pSrcDst," + " int srcDstStep," + " int rows," + " int cols)" + "{" + " int x = get_global_id(0);" + " int y = get_global_id(1);" + " int idx = mad24(y, srcDstStep, x);" + " pSrcDst[idx] = ~pSrcDst[idx];" + "}" + "__kernel " + "void bitwise_inv_img_8uC1(" + " read_only image2d_t srcImg," + " write_only image2d_t dstImg)" + "{" + " int x = get_global_id(0);" + " int y = get_global_id(1);" + " int2 coord = (int2)(x, y);" + " uint4 val = read_imageui(srcImg, coord);" + " val.x = (~val.x) & 0x000000FF;" + " write_imageui(dstImg, coord, val);" + "}"; + size_t len = strlen(kernelSrc); + m_program = clCreateProgramWithSource(m_context, 1, &kernelSrc, &len, &res); + if (0 == m_program || CL_SUCCESS != res) + return -1; + + res = clBuildProgram(m_program, 1, &m_device_id, 0, 0, 0); + if (CL_SUCCESS != res) + return -1; + + m_kernelBuf = clCreateKernel(m_program, "bitwise_inv_buf_8uC1", &res); + if (0 == m_kernelBuf || CL_SUCCESS != res) + return -1; + + m_kernelImg = clCreateKernel(m_program, "bitwise_inv_img_8uC1", &res); + if (0 == m_kernelImg || CL_SUCCESS != res) + return -1; + + m_platformInfo.QueryInfo(m_platform_ids[i]); + m_deviceInfo.QueryInfo(m_device_id); + + // attach OpenCL context to OpenCV + cv::ocl::attachContext(m_platformInfo.Name(), m_platform_ids[i], m_context, m_device_id); + + break; + } + + return m_context != 0 ? CL_SUCCESS : -1; +} // initOpenCL() + + +int App::initVideoSource() +{ + try + { + if (!m_file_name.empty() && m_camera_id == -1) + { + m_cap.open(m_file_name.c_str()); + if (!m_cap.isOpened()) + throw std::runtime_error(std::string("can't open video file: " + m_file_name)); + } + else if (m_camera_id != -1) + { + m_cap.open(m_camera_id); + if (!m_cap.isOpened()) + { + std::stringstream msg; + msg << "can't open camera: " << m_camera_id; + throw std::runtime_error(msg.str()); + } + } + else + throw std::runtime_error(std::string("specify video source")); + } + + catch (std::exception e) + { + cerr << "ERROR: " << e.what() << std::endl; + return -1; + } + + return 0; +} // initVideoSource() + + +// this function is an example of "typical" OpenCL processing pipeline +// It creates OpenCL buffer or image, depending on use_buffer flag, +// from input media frame and process these data +// (inverts each pixel value in half of frame) with OpenCL kernel +int App::process_frame_with_open_cl(cv::Mat& frame, bool use_buffer, cl_mem* mem_obj) +{ + cl_int res = CL_SUCCESS; + + CV_Assert(mem_obj); + + cl_kernel kernel = 0; + cl_mem mem = mem_obj[0]; + + if (0 == mem) + { + // first time initialization + + cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR; + if (use_buffer) + { + // allocate OpenCL memory to keep single frame, + // reuse this memory for subsecuent frames + // memory will be deallocated at dtor + mem = clCreateBuffer(m_context, flags, frame.total(), frame.ptr(), &res); + if (0 == mem || CL_SUCCESS != res) + return -1; + + res = clSetKernelArg(m_kernelBuf, 0, sizeof(cl_mem), &mem); + if (CL_SUCCESS != res) + return -1; + + res = clSetKernelArg(m_kernelBuf, 1, sizeof(int), &frame.step[0]); + if (CL_SUCCESS != res) + return -1; + + res = clSetKernelArg(m_kernelBuf, 2, sizeof(int), &frame.rows); + if (CL_SUCCESS != res) + return -1; + + int cols2 = frame.cols / 2; + res = clSetKernelArg(m_kernelBuf, 3, sizeof(int), &cols2); + if (CL_SUCCESS != res) + return -1; + + kernel = m_kernelBuf; + } + else + { + cl_image_format fmt; + fmt.image_channel_order = CL_R; + fmt.image_channel_data_type = CL_UNSIGNED_INT8; + + cl_image_desc desc; + desc.image_type = CL_MEM_OBJECT_IMAGE2D; + desc.image_width = frame.cols; + desc.image_height = frame.rows; + desc.image_depth = 0; + desc.image_array_size = 0; + desc.image_row_pitch = frame.step[0]; + desc.image_slice_pitch = 0; + desc.num_mip_levels = 0; + desc.num_samples = 0; + desc.buffer = 0; + mem = clCreateImage(m_context, flags, &fmt, &desc, frame.ptr(), &res); + if (0 == mem || CL_SUCCESS != res) + return -1; + + res = clSetKernelArg(m_kernelImg, 0, sizeof(cl_mem), &mem); + if (CL_SUCCESS != res) + return -1; + + res = clSetKernelArg(m_kernelImg, 1, sizeof(cl_mem), &mem); + if (CL_SUCCESS != res) + return -1; + + kernel = m_kernelImg; + } + } + + m_event = clCreateUserEvent(m_context, &res); + if (0 == m_event || CL_SUCCESS != res) + return -1; + + // process left half of frame in OpenCL + size_t size[] = { frame.cols / 2, frame.rows }; + res = clEnqueueNDRangeKernel(m_queue, kernel, 2, 0, size, 0, 0, 0, &m_event); + if (CL_SUCCESS != res) + return -1; + + res = clWaitForEvents(1, &m_event); + if (CL_SUCCESS != res) + return - 1; + + mem_obj[0] = mem; + + return 0; +} + + +// this function is an example of interoperability between OpenCL buffer +// and OpenCV UMat objects. It converts (without copying data) OpenCL buffer +// to OpenCV UMat and then do blur on these data +int App::process_cl_buffer_with_opencv(cl_mem buffer, size_t step, int rows, int cols, int type, cv::UMat& u) +{ + cv::ocl::convertFromBuffer(buffer, step, rows, cols, type, u); + + // process right half of frame in OpenCV + cv::Point pt(u.cols / 2, 0); + cv::Size sz(u.cols / 2, u.rows); + cv::Rect roi(pt, sz); + cv::UMat uroi(u, roi); + cv::blur(uroi, uroi, cv::Size(7, 7), cv::Point(-3, -3)); + + if (buffer) + clReleaseMemObject(buffer); + m_mem_obj = 0; + + return 0; +} + + +// this function is an example of interoperability between OpenCL image +// and OpenCV UMat objects. It converts OpenCL image +// to OpenCV UMat and then do blur on these data +int App::process_cl_image_with_opencv(cl_mem image, cv::UMat& u) +{ + cv::ocl::convertFromImage(image, u); + + // process right half of frame in OpenCV + cv::Point pt(u.cols / 2, 0); + cv::Size sz(u.cols / 2, u.rows); + cv::Rect roi(pt, sz); + cv::UMat uroi(u, roi); + cv::blur(uroi, uroi, cv::Size(7, 7), cv::Point(-3, -3)); + + if (image) + clReleaseMemObject(image); + m_mem_obj = 0; + + return 0; +} + + +int App::run() +{ + if (0 != initOpenCL()) + return -1; + + if (0 != initVideoSource()) + return -1; + + Mat img_to_show; + + // set running state until ESC pressed + setRunning(true); + // set process flag to show some data processing + // can be toggled on/off by 'p' button + setDoProcess(true); + // set use buffer flag, + // when it is set to true, will demo interop opencl buffer and cv::Umat, + // otherwise demo interop opencl image and cv::UMat + // can be switched on/of by SPACE button + setUseBuffer(true); + + // Iterate over all frames + while (isRunning() && nextFrame(m_frame)) + { + cv::cvtColor(m_frame, m_frameGray, COLOR_BGR2GRAY); + + UMat uframe; + + // work + timerStart(); + + if (doProcess()) + { + process_frame_with_open_cl(m_frameGray, useBuffer(), &m_mem_obj); + + if (useBuffer()) + process_cl_buffer_with_opencv( + m_mem_obj, m_frameGray.step[0], m_frameGray.rows, m_frameGray.cols, m_frameGray.type(), uframe); + else + process_cl_image_with_opencv(m_mem_obj, uframe); + } + else + { + m_frameGray.copyTo(uframe); + } + + timerEnd(); + + uframe.copyTo(img_to_show); + + putText(img_to_show, "Version : " + m_platformInfo.Version(), Point(5, 30), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2); + putText(img_to_show, "Name : " + m_platformInfo.Name(), Point(5, 60), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2); + putText(img_to_show, "Device : " + m_deviceInfo.Name(), Point(5, 90), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2); + cv::String memtype = useBuffer() ? "buffer" : "image"; + putText(img_to_show, "interop with OpenCL " + memtype, Point(5, 120), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2); + putText(img_to_show, "FPS : " + fpsStr(), Point(5, 150), FONT_HERSHEY_SIMPLEX, 1., Scalar(255, 100, 0), 2); + + imshow("opencl_interop", img_to_show); + + handleKey((char)waitKey(3)); + } + + return 0; +} + + +void App::handleKey(char key) +{ + switch (key) + { + case 27: + setRunning(false); + break; + + case ' ': + setUseBuffer(!useBuffer()); + break; + + case 'p': + case 'P': + setDoProcess( !doProcess() ); + break; + + default: + break; + } +} + + +inline void App::timerStart() +{ + m_t0 = getTickCount(); +} + + +inline void App::timerEnd() +{ + m_t1 = getTickCount(); + int64 delta = m_t1 - m_t0; + double freq = getTickFrequency(); + m_fps = freq / delta; +} + + +inline string App::fpsStr() const +{ + stringstream ss; + ss << std::fixed << std::setprecision(1) << m_fps; + return ss.str(); +} + + +int main(int argc, char** argv) +{ + const char* keys = + "{ help h ? | | print help message }" + "{ camera c | -1 | use camera as input }" + "{ video v | | use video as input }"; + + CommandLineParser cmd(argc, argv, keys); + if (cmd.has("help")) + { + cmd.printMessage(); + return EXIT_SUCCESS; + } + + App app(cmd); + + try + { + app.run(); + } + + catch (const cv::Exception& e) + { + cout << "error: " << e.what() << endl; + return 1; + } + + catch (const std::exception& e) + { + cout << "error: " << e.what() << endl; + return 1; + } + + catch (...) + { + cout << "unknown exception" << endl; + return 1; + } + + return EXIT_SUCCESS; +} // main() From 83b2621de4a53506afcd47b69fac210534304fe3 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Fri, 19 Jun 2015 18:53:45 +0300 Subject: [PATCH 048/133] Android: renamed default library name for static and dynamic fallback load; fixed libz import for 64-bit platforms --- cmake/OpenCVFindLibsGrfmt.cmake | 3 ++- .../java/generator/src/java/android+AsyncServiceHelper.java | 2 +- modules/java/generator/src/java/android+StaticHelper.java | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cmake/OpenCVFindLibsGrfmt.cmake b/cmake/OpenCVFindLibsGrfmt.cmake index b5f38279e3..614f844c7b 100644 --- a/cmake/OpenCVFindLibsGrfmt.cmake +++ b/cmake/OpenCVFindLibsGrfmt.cmake @@ -8,7 +8,8 @@ if(BUILD_ZLIB) else() find_package(ZLIB "${MIN_VER_ZLIB}") if(ZLIB_FOUND AND ANDROID) - if(ZLIB_LIBRARIES STREQUAL "${ANDROID_SYSROOT}/usr/lib/libz.so") + if(ZLIB_LIBRARIES STREQUAL "${ANDROID_SYSROOT}/usr/lib/libz.so" OR + ZLIB_LIBRARIES STREQUAL "${ANDROID_SYSROOT}/usr/lib64/libz.so") set(ZLIB_LIBRARIES z) endif() endif() diff --git a/modules/java/generator/src/java/android+AsyncServiceHelper.java b/modules/java/generator/src/java/android+AsyncServiceHelper.java index e18d5a5001..4d9d115389 100644 --- a/modules/java/generator/src/java/android+AsyncServiceHelper.java +++ b/modules/java/generator/src/java/android+AsyncServiceHelper.java @@ -376,7 +376,7 @@ class AsyncServiceHelper else { // If the dependencies list is not defined or empty. - String AbsLibraryPath = Path + File.separator + "libopencv_java.so"; + String AbsLibraryPath = Path + File.separator + "libopencv_java3.so"; result &= loadLibrary(AbsLibraryPath); } diff --git a/modules/java/generator/src/java/android+StaticHelper.java b/modules/java/generator/src/java/android+StaticHelper.java index 10442c904d..36f9f6f64a 100644 --- a/modules/java/generator/src/java/android+StaticHelper.java +++ b/modules/java/generator/src/java/android+StaticHelper.java @@ -92,7 +92,7 @@ class StaticHelper { else { // If dependencies list is not defined or empty. - result &= loadLibrary("opencv_java"); + result &= loadLibrary("opencv_java3"); } return result; From 2ff614dfabc872261969b46e655aa15223ab66d8 Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Fri, 19 Jun 2015 05:30:05 +0200 Subject: [PATCH 049/133] spatialGradient: Per row in outer loop --- modules/imgproc/src/spatialgradient.cpp | 99 ++++++++++--------------- 1 file changed, 41 insertions(+), 58 deletions(-) diff --git a/modules/imgproc/src/spatialgradient.cpp b/modules/imgproc/src/spatialgradient.cpp index 7afcda52b2..e87869a4ea 100644 --- a/modules/imgproc/src/spatialgradient.cpp +++ b/modules/imgproc/src/spatialgradient.cpp @@ -186,39 +186,40 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksi v_two = v_setall_s16(2), v_sdx1, v_sdx2, v_sdy1, v_sdy2; - // Go through 16-column chunks at a time - for ( j = 1; j < W - 1 - 15; j += 16 ) + for ( i = 1; i < H - 1; i++ ) { - // Load top two rows for 3x3 Sobel filter - idx = W + j; - v_um = v_load(&p_src[idx - W - 1]); - v_un = v_load(&p_src[idx - W]); - v_up = v_load(&p_src[idx - W + 1]); - v_expand(v_um, v_um1, v_um2); - v_expand(v_un, v_un1, v_un2); - v_expand(v_up, v_up1, v_up2); - v_smm1 = v_reinterpret_as_s16(v_um1); - v_smm2 = v_reinterpret_as_s16(v_um2); - v_smn1 = v_reinterpret_as_s16(v_un1); - v_smn2 = v_reinterpret_as_s16(v_un2); - v_smp1 = v_reinterpret_as_s16(v_up1); - v_smp2 = v_reinterpret_as_s16(v_up2); - - v_um = v_load(&p_src[idx - 1]); - v_un = v_load(&p_src[idx]); - v_up = v_load(&p_src[idx + 1]); - v_expand(v_um, v_um1, v_um2); - v_expand(v_un, v_un1, v_un2); - v_expand(v_up, v_up1, v_up2); - v_snm1 = v_reinterpret_as_s16(v_um1); - v_snm2 = v_reinterpret_as_s16(v_um2); - v_snn1 = v_reinterpret_as_s16(v_un1); - v_snn2 = v_reinterpret_as_s16(v_un2); - v_snp1 = v_reinterpret_as_s16(v_up1); - v_snp2 = v_reinterpret_as_s16(v_up2); - - for ( i = 1; i < H - 1; i++ ) + // 16-column chunks at a time + for ( j = 1; j < W - 1 - 15; j += 16 ) { + // Load top row for 3x3 Sobel filter + idx = i*W + j; + v_um = v_load(&p_src[idx - W - 1]); + v_un = v_load(&p_src[idx - W]); + v_up = v_load(&p_src[idx - W + 1]); + v_expand(v_um, v_um1, v_um2); + v_expand(v_un, v_un1, v_un2); + v_expand(v_up, v_up1, v_up2); + v_smm1 = v_reinterpret_as_s16(v_um1); + v_smm2 = v_reinterpret_as_s16(v_um2); + v_smn1 = v_reinterpret_as_s16(v_un1); + v_smn2 = v_reinterpret_as_s16(v_un2); + v_smp1 = v_reinterpret_as_s16(v_up1); + v_smp2 = v_reinterpret_as_s16(v_up2); + + // Load second row for 3x3 Sobel filter + v_um = v_load(&p_src[idx - 1]); + v_un = v_load(&p_src[idx]); + v_up = v_load(&p_src[idx + 1]); + v_expand(v_um, v_um1, v_um2); + v_expand(v_un, v_un1, v_un2); + v_expand(v_up, v_up1, v_up2); + v_snm1 = v_reinterpret_as_s16(v_um1); + v_snm2 = v_reinterpret_as_s16(v_um2); + v_snn1 = v_reinterpret_as_s16(v_un1); + v_snn2 = v_reinterpret_as_s16(v_un2); + v_snp1 = v_reinterpret_as_s16(v_up1); + v_snp2 = v_reinterpret_as_s16(v_up2); + // Load last row for 3x3 Sobel filter v_um = v_load(&p_src[idx + W - 1]); v_un = v_load(&p_src[idx + W]); @@ -246,35 +247,17 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksi v_store(&p_dx[idx+8], v_sdx2); v_store(&p_dy[idx], v_sdy1); v_store(&p_dy[idx+8], v_sdy2); - - // Shift loaded rows up one - v_smm1 = v_snm1; - v_smm2 = v_snm2; - v_smn1 = v_snn1; - v_smn2 = v_snn2; - v_smp1 = v_snp1; - v_smp2 = v_snp2; - v_snm1 = v_spm1; - v_snm2 = v_spm2; - v_snn1 = v_spn1; - v_snn2 = v_spn2; - v_snp1 = v_spp1; - v_snp2 = v_spp2; - - idx += W; } - } - // Cleanup - int end_j = j; - for ( i = 1; i < H - 1; i++ ) - for ( j = end_j; j < W - 1; j++ ) - { - idx = i*W + j; - p_dx[idx] = -(p_src[idx-W-1] + 2*p_src[idx-1] + p_src[idx+W-1]) + - (p_src[idx-W+1] + 2*p_src[idx+1] + p_src[idx+W+1]); - p_dy[idx] = -(p_src[idx-W-1] + 2*p_src[idx-W] + p_src[idx-W+1]) + - (p_src[idx+W-1] + 2*p_src[idx+W] + p_src[idx+W+1]); + // Cleanup + for ( ; j < W - 1; j++ ) + { + idx = i*W + j; + p_dx[idx] = -(p_src[idx-W-1] + 2*p_src[idx-1] + p_src[idx+W-1]) + + (p_src[idx-W+1] + 2*p_src[idx+1] + p_src[idx+W+1]); + p_dy[idx] = -(p_src[idx-W-1] + 2*p_src[idx-W] + p_src[idx-W+1]) + + (p_src[idx+W-1] + 2*p_src[idx+W] + p_src[idx+W+1]); + } } #else for ( i = 1; i < H - 1; i++ ) From 902e381264173badef5c78154f7aeeca497526d6 Mon Sep 17 00:00:00 2001 From: Dikay900 Date: Sat, 20 Jun 2015 10:17:34 +0200 Subject: [PATCH 050/133] fix one dict parameter in ffmpeg implementation (introduced while porting) --- modules/videoio/src/cap_ffmpeg_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp index 7cc5483d53..93c730a1b2 100644 --- a/modules/videoio/src/cap_ffmpeg_impl.hpp +++ b/modules/videoio/src/cap_ffmpeg_impl.hpp @@ -568,7 +568,7 @@ bool CvCapture_FFMPEG::open( const char* _filename ) #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0) av_dict_set(&dict, "rtsp_transport", "tcp", 0); - int err = avformat_open_input(&ic, _filename, NULL, NULL); + int err = avformat_open_input(&ic, _filename, NULL, &dict); #else int err = av_open_input_file(&ic, _filename, NULL, 0, NULL); #endif From d64c8aad9912b22584a757c88495dd1da5d9900a Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Sat, 20 Jun 2015 11:18:47 +0300 Subject: [PATCH 051/133] Update mat.hpp spelling corrections --- modules/core/include/opencv2/core/mat.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index 45f3cef0ce..278f7af896 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -1796,7 +1796,7 @@ public: /** @brief Invoke with arguments functor, and runs the functor over all matrix element. - The methos runs operation in parallel. Operation is passed by arguments. Operation have to be a + The methods runs operation in parallel. Operation is passed by arguments. Operation have to be a function pointer, a function object or a lambda(C++11). All of below operation is equal. Put 0xFF to first channel of all matrix elements: @@ -3240,7 +3240,7 @@ Here are examples of matrix expressions: // sharpen image using "unsharp mask" algorithm Mat blurred; double sigma = 1, threshold = 5, amount = 1; GaussianBlur(img, blurred, Size(), sigma, sigma); - Mat lowConstrastMask = abs(img - blurred) < threshold; + Mat lowContrastMask = abs(img - blurred) < threshold; Mat sharpened = img*(1+amount) + blurred*(-amount); img.copyTo(sharpened, lowContrastMask); @endcode From 0f59bbbee6f51dacb520cea92a42c5de72982dbe Mon Sep 17 00:00:00 2001 From: jayceelock Date: Mon, 22 Jun 2015 08:41:44 +0200 Subject: [PATCH 052/133] Fixed gen_pattern.py file permission back to 755 --- doc/pattern_tools/gen_pattern.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 doc/pattern_tools/gen_pattern.py diff --git a/doc/pattern_tools/gen_pattern.py b/doc/pattern_tools/gen_pattern.py old mode 100644 new mode 100755 From 2f8ef61618bcb9087b0a21a53be63e8cccff0759 Mon Sep 17 00:00:00 2001 From: LaurentBerger Date: Mon, 22 Jun 2015 17:51:01 +0200 Subject: [PATCH 053/133] move data/detect_blob.png in samples/data --- data/detect_blob.png | Bin 34115 -> 0 bytes samples/data/detect_blob.png | Bin 0 -> 57892 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 data/detect_blob.png create mode 100644 samples/data/detect_blob.png diff --git a/data/detect_blob.png b/data/detect_blob.png deleted file mode 100644 index fe8537ab0fe1511232e07d7ce1769e959136397e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34115 zcmdqJX*`r|_&m)5 zuHHLz3cmODkzGhGCNGvOyvD_@N4<}_&p3;}R~KEy6lQs(7rM7!wM1@NGq-p7l<&uK#& zicokk z^LcFPNGxK5^7XX{x?xGREff!;EI)Nj0m~_T;t+KMe<+U+Ya2i%y;3KHFGy@uv>8)K2-|0cxo)Ds$CnMJ%L+dDg*sj+>-<(M?zybNSyF!Mjd2a0 z3sfF6l;M6Ms4FH^FFMz(&LcySte<0?z>h5qNMQY}I{4|(gCOBURlCBie6 zCI_&mXV3vr3qrz;-3k@45l>P4Utrz^vjviRf7Qu%#d7?J2HA| zc(DK3Ng3Fg+@5o_t+)k6jq?3sm=rWa_b<7fjBo$r7h;EetsvuZnQJ2uvnlzY;(0-4prjp*F8< zMW?YzWg*SBuUg!-kDn^f0Oj_Aq3JfYae6Py3LVlHGFTt=_UfG1k4Anbw(f>ftSa%1 zHQG(adAUKIOlNzzqmgmMzH;ME0c1Xuqcyg2#D*tATabk5y?x3*5;JzrXkhRc! zlTi2o<^;k#O~$yWIG4b$oQlY^OSk5Nnd=q5b)`V zE0M|oI%4BFq%B!}5cVOSXDg8RVL#>Kp|KI4>0S0JQLQlDx?KLKF@6x4sXPmA?Py25ktxUmO@qe@aORz^1wL$JWMHP@JDdMAt+q6cbIZ77|Z^3 zQD)oioTq0$Hv|>BqoQ%my_S5o5Zs6$!ymFB1Uxh)027mYazInaCeEtfgV+k@KR=wn zPHHeo;FD$fmYXi&&kr-6)KG$;d*4u)7{9X3rVD}0-8r7oAU?J9W>Pq@N<-*xby{(` zX7HRwyeYl4h{A$#P#L~ehAUOaTrz_h=O!Mc1_QT$NsAzAaUFg{ezs(0;L(fzu57y@ zb{G`?Y*`I)yWBD9F9y8Y8;Rq#9bwB(4<-VAGQV7jVaxI!lo^q>tm_+fx_iYraTRE~lfSLwjuqM-j|T8~l)j*ANr!XmscVM^`aHcC+?s0)aePcS1XT zN)`d>jjtqcR_ND-uwgMpmbNF;VVqniiz$r|Yv>tPK zFDGp7Frtg+|LwFsS39@-&zKijJ|@m?IwbWd*NIe()^X&zhy46L5nNBiFMnoV~l0L><_njiL897v(rrUFjLTUp{;3n54OkzcA}puoaRO=B08cA9Yu3+wgT_ zpDJ1pl|b}sUTwQh zf8iO8ShKUACDdfpc3FQqcU5ZgK0HWC%U6@(cJ?oRYXFXkr?zx8Tl22K;X|xpQm>%3 z#R!3JaX#wW6-_QWAA@dFw1-zM%a7Q!KXcw^)4mmVlL%)b%q06x3pO6PzzLHU`PIh; z{>tm~Fx9ATg@@Egs`K5gO;f3PTr+kbg7XJ=rKR9*jl{S7F`%K%}DZ(%pZ$EVr1kpLX9_3v9FLY_4;3@UvRVP88ICM6=4u{D<$}v_jvp}^e2bVUvdZZzIn-RQ5)?2!9@A!NazBZhF9l+f6Ep>#*?}#Ojjeqi5T|w zv5C@4C+xzOMdu>}$WCWwax>gr)QbD%bgr?lyfouVfcO`Ho2| zrln?Y{2}1V`Ba_P>l?F4Y6PvXFEs3eCx#|c4$yq;v@imHmNC>}MJh)ANLRJ3moFR) z^K)P9Rls@Lp)kCEdc>b?D4E$V?H1onJrXA!vb2!EDWvgBujv9D?|SgQTuVi9ZIw0N z)9{R_kYV{UVB_yS#E(}8kC=G2Yt!6Er4m99(=A|mH|bW+&F=zK!})+!9U3}z!P!f^ z#;Q8u4{woyw6)gbX_=0fT`vNScEuS{RsL+4581o+3g#G znW9JVgf;E$-7gfGa^az$NSZHd^Pkr~jUmDq*_8Y!WAAJB&dUGj&X+V3HK{L~+_Ng* zRv$)dwzG{PV*bz)TlUVh@&--jets{k)B4`adROd*=3!g91Hfe*Mx4WRteiML61cFR zcI56iXT7^M-F~cpHoqI8J9gRRWI=b?i}}!^jnKo!e}-{V#-?q!O=Tm6qN9BOVHOKP za1LEY05bze0~BAIZ_&?WbgG+puM&epo{wOmCoQyPL4l(Ucv^y zY>Eti)$~Uyt(#UQ-hn~fmHrF5YV%6T!>jDHEgiu>w6+&jdRV02++fNzNBd0s@K#ZI z$Nt#3L#US$>WNCJsQaJ&PbF_7=WLd{WU!jf-0y=syEOk<_by|@Sj#E*i8(-{v{-PkYSVtKl~lUsQgn9B}&%$XQvNaoY(_Vd|D{%`YB;z#=b-C#Vr=9CN?+ z4gy7MJZ2UnePLO~JErxQ_E}U7g>aCB{+*XX*@GATrB?KCl#K({O~2e6J$@;S>28Lc!;k3H(}=%m4A*uKAM!l7Gm$pgqg983#jWDJok9kJtMDyP`x^b}&0jTykzb zoNelVyzrcy@~`1lYqDQ5<2@QYU!Sx={+F(NXwY%I#qUeNAN~<`ZM?K2<4X%3 zrm@Y5{vBFT`WA-#CwLNDuyk#q@8BS5B)2wBXds76467GNJv4y&J0Rm?iwi}K(=rhw zA@!WU94cLe7w+i3MK@(%Dfwr{NKK#4XKggwRhdwy#>xy8E!8V4^Yg)xDt}@)LFdFv zH8G6?Z)+fYV5R2GiVaN>p+79m?^60uVLTs@N{MN(>9l)5oW_s;V}(G(sOFBU?WPGe zS=%vN7LuU!;m-&Gs)mmGADu9jx1nGfmSpZYMd3{rrfaogez_OA_-3i_DX88Z|EWf(G{VQkg>+Wv%KM(|EWd71RmkF$J~uhj9pmM+|Rhs2ZHN~TH1}cx*xRr8VQzEI^c@I?Xd>Iabi%` z_Pec|e2s_tb|M|uc-sXxxz(hbAOAo@2uke@p-BCb6) z%65!)QCb+0_FtKFA7&_6(MpB_5||(>J&Yc*(y<;u$>aX&3iXvueQgD`sslH%fu90y zojXxL>#F7g$)omJKtw^ktI=Y?iK*H7GJg$!Q|~24;cuNE2Rr0^jq>7kOF-f;Bmk9x z)%zJ<_?y&ievO*IB%yFvR`cgA)O{D zud;yN;JWl28wrj>)G@oK%^3&>hgwp83@_8V)w=&|`^Bl~mJO@%E}CRm2;>A{ue_C0 z5QvLC;>k4`bG`4PJBH4=I%R(^IWb4otncWl=(n%AAS6G~jrd@1i-;S)N$5FUOOCbH!x(mn$&ej>STOfg*7D^K6xF7A&nlc+%ah zu50A#H*1sZq5@LVB5lQKH&Rrd+{X~U2S9`ayYxg4BW4|z%m&uZ``od(dXtuOcF*OT zTEJs19pJy!(Ih79wrxMYRq5Mh@O~-x=axpiALPUZ;Qj!Y_uJ}-ZkDQ*(bAs7U8%7R zYr;IN5uTn-Y*Dl&e{~=Gb;bFh?Xd|p9)Z|+|MW1|9iB*I9c2$m+o9PMW6SqNX*q9o zl^^xsFHrj!wLeaYBFY%Qo+k|NvTBxXt_k|)5jHx#GUA+VYtpIImAbdNVf}31QQ^Az zk2A}t#9SvGYuDPXdkgIzhGK1p4&+EwQ&0cg`ZTp=VyryswT~cOMlNCs zPhC+!lzl!oxX)MFWH|1KVE0os??+nU8hiIg#>gDqdr&`)76Mtiy`{>#xqPN-QR`ja z&*g3gS5po(&i!C0%d#V7SAFB;kKIFM^gln^JGvgUF5o)I%iT2XeE``3Zyw!JHtH6$ zS)+N8SREr4LZzWk`NE`Z&U2g>4)QmOIhGxJgATWo!P1=jcbJL#w_!(ti z(0a3bLkO?S8$_*lj_6f>7*_O%UM0Bksg~09d?S;es#_*qUDV96&wufW*7{cdjMA27 zjB{E!I)8Jc%%J|d>d}4LjiB~`STVi6FF+XY)`NpSCLIu3%He}m^TyIf%1(uuzpqEd zflO7JVvM0QZkdjUgttsEG+y|yDP~**5B$D%o?ClzB<>|QfWsEgLY|E!HB!SCfp5Q`Wg z#W)4T`pNfPas#9=LTD%X>Nk~`vpwR~ZBJ+#(ImVpI}>BqeHDU3>4K(QO)LqwCU*m4 zOc2_8MCom9$NFt#_}tl2W_MOAAJOFMt}|?o^^t zh?xp+_6|4WxGS|+1dXW0+_#ZQ)gcv;idO2TRLbV>n6fW_Vjs~``SPvV?S6q>f1&r~ z+Slz{87bB`N!c9et%9zNuJ0$^946|dw(=71oTi3u>MqqjLF&p#Z(45os|6NK*a)r^RXjmpES5dQB6{!+EwT4`K&QYKL6J{>=%8i7t%x^r z%rQ9k7)eC$V;S=GYt{qjpA|eL`=EB7*bnSW$UGEUZ*R;A8|J)(abifPInpBW4h0Jt zYU`&(h1?9wozCdf>=%R%bm{7$l$#F1LTvQ-i&HR~&Oy3yXHN`&KFMY@I-`Q=^}N#Y zdy?h3bHyiNrwVk_iPN~yo}96+|BgF9i6oZ@fM3_m$TU?2;SnFsbhOA@dB z&#cS7gD=@byH)p*efB`@cYcXk_k>@NM?l|9(`gtqsc{|DcIjlw0s(4f7(P#zL@$OHPpc15F17?0g061E^bws3mB~K4p)0s10a(bPxhoGC zq3{btl8BA)@4s)oCO8z#EIKh01R-c&6Ab1}m>Ju$;18KwEk#85hqznE)vt$M!Cbed%I|{0Z_Oj4Dd@0L=W$3rz0oVj**E$9jZ&Gs z9qN`c1T|$3lDM3z^(z}aj%Y4xxGNN-!>iv9xrI6}*J(WL+3D*Xb~NXw_U(eYGD7uG zS!qogj?c+f8}~`?qTc3~H{(BG(c`BHulXhq5q@?RE!6Qq(8t+JnB9mTha}T*r>i=o zugy(zvI6N6>+vL`X&L1W0cx}SA1}_MFgm!+x2GOV= z5LD(8N)Qc_Y}ajxi*cs7%(w)NutMSMTrg8B$51qYv5|GTbrm!XPiRrLX=&0y-o~gBU zAX2d{PB7DjF-zi`8`Brp@Ydv8Ibkv~Q8N7Hm(Qw|5;Hvp&nJ8c&R8Uh?^I6q8Bc%X zwi_uwPMo(IoUGNNTauo>nefHJaX#cog!7{_GsP-4O_)=XwqIj;>(Tq1S})M&cEZ~Z zAho`i_k}!oyKzx4gmg$dD2!l(VaG6%xE^AsoW~<|9xiJv*AiQy6oRH|AwSN}P+9OV8aYO1gjW==xSAnNu`HR1d`sAInN)mSz72XC}b-YrAG% z+4mt|LwC6LA*H&G58n`Jis?;qy5klhqSs{xx~B?FvIMCNWWVc*ULBrNB_y&IR34V; zIw;E*8I$V--=8PlrN;uof|Jcu4=eav# z_;RG4*~up+w{b@hR{jqI+X7B??GEwK*h4MRH>}yc%pizVAab~7CBTmnmQKU>;>%By zGdT{Q*F((?2u`vr^ATHlM7E@<_6MdKZUyF;a$@J5?Z4?qkvK3AiS#_lFg-5;cQ)Zv zLUV9@VwbsjbJX^u=@5u<$xu{SOZoaCY9|NhUwFbre*xKD16i$O8Z9h08|5N*NzFO? z6p^e>P&@Z`ox~Fq-DD3-$6-!AyRBPeLgcW3E_dsE6Xtu%$OXvuNemLAGVCKY@(~^I zW?q@BueB%0mF(bt7pEB36d?Q(s(AWUg%l8$5&nn^hV5C7%#|myhVBF7xgw+8qnh#O zuc)Nk6SS}2VdN4;@cUN>_6G27@=;1=}*Ee$szuDCs)iM3Lz1WB+k=`qF zL1tB+PMp-#uH3k;%#02)BB8?jj^(Sn&-5oyH`S? z1i9N*>&C|H9zxe@=nf<76bAmyyJyPnduvW>e(N`lXk6ErS%`t8&T^&hFlU@v&k^L_ zeOvkGdSlY@mWf_<_Haf}wT4Sf6}-BZ=$4-T(0!xe;#M+$I_7zmXGoW%-s{JLshndv z_FTIx%#MMfb)V87D59sgw&}RWsond)_^m6Al#*}q!JRp%W#zV{c+F)+34B3SGb`h) zo*Y(AalfCHxVNsg)98soEBU+_T+5TI8E`U_CNk({P&?V@G2^;Gp3FY06Z>!LsX4yY z*XQNNUk#1SzgHY_rs4UB==v7^8Q0dfJ=>mQkXfe8wtr};dDCi;a53Rau#5r9AlCF% z8|C%^+xkGK$o-S|SxKYS=@U7p)E3SGdhiXqvl;y;< zFXq&1xw|qKs;}IYv8|3_YG%zun#7hI*&SCT$aX$-4t~*mlV-1{^Y#HM%lF{uRyX1> zRfCBR3FK}1%g^Vwe%IjrbR$x{C6+rvUnvz`#*N+kpg8V?QnM*j0!>|i`XaX-uXw$m zRL5>SkO}(XOm`4YJHawxIM|r-l-@C30`dWeD@{QMmW;?zB!^xHTE+ngN)kcvm$U`& zuE=!#+pv(eAVaaqYMs{r$B)N#F`o)0l>-HGMU}%CA-x1jzRe}x#642SnV<_1|NEiO z_=xoQ)$@?HVhnCz*r8eo7Q{~WVT7*S)I-f>oFq3B&n^oRr}sc_e#YReeCPaD8{6C5 z=D`(!BZwR2y%;(+o6edZ(?EekO0e>ch`@#b-bERbPnXX^hA0G$gQDB#$??t)D&SC3 zP5@6Zw;8G0UmU>%$*F+{VAw~<`av?kFp0Vzs=)RnnTZ&;Y)zbIf!+iFqF%~%f@(Fr zPmnR$hY7mktcP;z<*P0H-Ea8`ae6QGrW^FcY#}|a4gF;MwTx0SxP&o$?LTAO708h_ zAO6#S=sJyMdI9MhDzFJ?mRr*cb@(^))AN0lvj9d@kcYgEiDHJ}#2`hiyy6pQ@xAP+a zhfq8c^9i4T%3>Cb1Q3=TkkK0N-!RYkAG$Q%liB_=6*0~?GvM#V-b*V3Oo<+54ss8| zk$sqGZkHZM1{JB>O?!Seuvqc9id&(oXq{%yd4t6pz(!3)gN%LSR<|0$_TvbqD2 z)lZmGHKQ5?GL`Vr7~Fd2%~Ehia_Kr zhcxk`dw~-Ft$ElbO$!F#LBxP62LSgqtJ&qVl%c>s9a6mO2f(g}5jPf67JjMRl*V;< z$Ho<=uc8RNHs;gVsox6%&3Ah8?{dvR^1nz2IkgHk;$V0JC1w|}gpdE{=D~jqgy2PH zmV|^!TugWZPe@&@MrC;d$m;%VL*LHBqtk7UF!nEDUiSmL19<0UdVIS7H_asj6vY*p zA)DOkr_=w7ZhR5VZnRF)bm8BPFUSmpeAT#A_V(Z1c5 zq`m(#Gi`DzaNOoijX|eA-3$JA(a%0DHR1k=8F4g0692y=UTFTh#zXqgrUzV!tO2|; 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
  • xr~OpRXqwGl}ahui3V?Wn2VzLz<9O0@;Kb49)@N z*+8Pw+;UKo2dON@6I)^`ILy}+^;gO0p6EmV!yr@9@!U+VT|HxtQAMQoPw@{TI5aXW zh0J0S7I65ZX0;s+@I8;8J zu>x+<9K)EMq?S7Hq~g~O^puN!aL7wM>XE9Son5YI<#3>Pl)IBjd^-s1IKz*xdD~$h z{5xnXzvO!lBmaOYfQcrIeNZq`c3gZf zOF-tKi?MT?@tR!&kADb}ogHnI2}OA;>Ka4O#zg)|V2-wNLxtDwy!|k9; zOvHNixru>_i0KpLmm?~R{r5l-#*r20@g^0p!gE^K?)JoLRWoTR}k_Z@ZOE0Lr;FeRUZ&T51@7HOnos<(TuF> z^0Dl`OQF=|KX!jjZ4MrTwQrE`D14CpE7X8&u9(h`{(D7f@A0~3l2$5|5c6gI%svc! zWvi^b0;e{N4F4^RvGZ{_XS8U%J2!nriyIR9*``m<05U44{R$-zj$4L$!Q6sIxN|dz z@hzcmDt}XZRtnMm+fARvx{JY=K`{spUlitTjv^f~Z3aaHcr^euF1fEL7Qm~RQcHJ< zb(fdo)P^elR)R5f9F9}K+0!B%;AG(F63>l%u^I#Lb+yVYe!HpB;BG?fla-7XVHdZ32w%?kWxw+8y1j7lY*lUg1}Q z$|-0^r+nSeWxS)vH$jIJzOoQ1?8`?dp3qk@uzs*j=!I?iOU-N36Uv%(m%QI!w@W5Y zFMY{x-l#A0SY(L9^&Es%rg;e6b1kN*Yz#7ABQ4mm8_WD`Xo$m)?~TJHL{fhCG$xs7 z;wdU8t6K2m3Y{;F3xnN#7pszS#+o}H9PSJ3ELy}Rib}h#8nJ5yqG$^%Um|&6mtPJR zd;UNZ)E|yz9Jh+WCYp%{Iz4dEb(c@KGk8u3r~_8=INJGPMMh?a&&-#{FuL91Y^_R0 zD%!aGvcqOkG(+;j-P-%UsWMfL)QREDVYNW&G_4Ik9>+@ZiqdbjTr-Hqj}zet_7X~$ zL90X@PPdfuv$64h!d8iXt1b_WLpV{t)q$0?P{oTgF1&=<)aQYvh5A}AHt+aZFHVN3 zlegDWCgO|ck_F5Bsy0$KkR&d&!;{M$9K*ZPBwLVP7?5P5O@Y~nw?y6iasjAFJyJjv zHgG$zD6sm6Ps~Vh&4Ax;W-C|NuD-uYB~+M%&gs@1_o%(vIq@`M{^4fJ!{B2lJggrh zQ5KokV!nov4#qp{YEn@@s4Qi2z=El&s z3GDRuN5mV0m#RB+IAn?`VmMa=fVe@Cg*6VY^nHf|sl-i>bW#9502{5lFR;7Sz2=xQ z-KBgZmVTGpn?C7CaCUgg1dgPS>j6lbXjRc@-nLydBC=M_1~!KXqfIo$ts0QMpXs+w z2IrC%a3+O@WG`PK*xK^@XUcPFSd19LU0T1@pWU!yi`>w*7K;-Wx^Mk75w~G}Oqfj+ zrlp~d#hGZ{)LVRp0>-eFqTgx;#}eME82A>u$9lC6huD21QgyU)m%o-wddn z=<6KR2<&01IpI;)j~{=fkyL3@a%pi@9nl*S1eB2nMkN`vvTYpA3(c0RMU%W*=9rHP zuRqGY#BR`Gju{F$Cyrn@(p=vVL9n0IOvGWFL}n75G1h6Q=-f-1cJVknaRg3SuhnhG zc`u2JG9szD4qxQLi#A2=BJc@{C>@P=-3KtA7)Y)zH$^Rg64}>x^tveT3mp5_$BVon z)FnK7wdYJ>MRRMGX~Ha4GxNmbc~Y}K`L^zJV%qnovDC{PzoI2OC@8LtA?9(+rP0K8TQ^z z%gJnH1ci{2bG4+wdSOuU!#AD7k7LRvpBTKcNbxJfVfSC4a<^-ygAk zXuVpq=J$n}8y0;mv#aBveru;5HGu(Kre z=XBc4>Z8FWfB^~yLE`e%OnjA4>HfRm6o>(^j!cOH7~qOT0z1}^f}RR-jF+^&g0gWs z$eBcL)J-tT99}n-9}5}ozj#nsOL1?hWzn^sub#!?AaO%|@EdYTn|S9x_W-wB(0?G- z^}%?%O;1EMIbhy>CJ3kO@?adGfSVf8Jc$wra?Z>_eN^bXzGrqr%36d*yy!^PVm4;_ zOenIIRyy~JcUw$&rkL(YTem4cFhJ6KAlcfRO1mK!wWnzdZy)9nF-|YrURfn?7$) zwVmT|f7VVDQ8gpG1Q>zRto5F)ziep832R7`9T*NVLg!Hcc2iY3ZdoY;kRUkN2q^Iy zFRaPEahJVn;KG_R#WVY}>FmIf*^tiDimC@MBzx`BbGgiYBd9L6U~x7}QO>Avjxzcw z_LphmD?Y!lP5fRtrPyXl9ZZT3~KE*Sk)~f@iJ6Sa#_nM*f9EA)XaB=5Yp8jiVaZ;7>?g#-c`f=G&X^nwcXs zG7F~PvF`zq5deh&nHPyTXM%27?^BmJaKnlK0vrz)!Ch4tt;>UT?bqT+J7X!KRF~CN zEPPNvdBX3dBtlQ^EU^>kJmaypetFF$p0;)T^Jfz7NbT{xB_oRVb7!*p?6R|GKjx<& zS@$<8@OkWL^%C5r1hz;TSXI1yg1J60(0fA7|BMN@hF`71`-M@Pr1EJ?jMD?rVTl^K z@ZkNah8FPjH`~wVzx{#fNY4>~6^yF)F9zWg?A}djp@i(LsE% zCySXl0Zn-A?$8g8fRtMJhtGLVa0_I1eg;q960%|LN|{qoMBq|KYkSij0(o z#tbnnlC`WOD$EQ**|Mc#lx?hyed)>`W+YTdGGo_RvSrJK3@OVPy9`3cmMkS(ey?$T z?(gsWy?^K2=iGnY=iGmBn0dcn>+|(o9*@Th_uQ_128ytk5}xY%tGg1|nB_ij^uIJ2 zC|~XfbV5_$p(`o`adY)@@=y4Tnwg@NduEI_uWrYtehf(je!*PJKMxxJ=h2}UZIQRE zt{^k8Z2KMfW}vEO^4)S9u_Q-_v`8_q;%$$SDS|?ppmuu(N_2g(+#2DVA?J#}Rd0)4 zRx!Q?l!1hmnA4c!1dpvZU_)if56R6n_A9E{mPt1SxF#5V?p0|gHy7AQ>G1NgQvu}C zxxK^LYZ5AA`y=~2Yu(VE^P4^&F)AHj6jVeJQ(cOK-Qw8y&Zi|$@=)MI5(O(N^5vdR zpc{rHg42}MV8&fkI#TU%4;e@ICtX67y_#s_Gda>NlvmMAA(I0&JP<(@{R z!1DWLw=E~sB6NBcB#f1sjgq)89GBq*XOPgh}J`uqoxu zn)P+~dXKKZ_`ss%OD+Fqktq;hY2^nLniCw)>!;f;!_&Vk z%Q!zRnmxG6g+WEz^769tXrK_1m}98n4idLWtdd?7an3Udi%p0*`qOVpEyA&9yK*c4 z+*5uKw7sJi%L7}3%1AW>mwzkchb zNm9&Fuae#$^=m=46y<7$ZzqKdw)C#~b9L*@@#SNT(ut19-LPHjs=d3oB7qMP3tAi* zGO@kxdYtGv$&eTlOMUgpV_f*76~mAOJg6YOVY?N?`S0Q_?`Iy}6#)tol~(SPH8&gS z(}O+HuFKD^Q@37qOt*Sd!}5|EZVnRo9N)j1X#To14TV2Ul@hC47a@JZpzhz&LdEvQ z!2P?ONBYZLqDdaMnvJa(KLRq0mOB6vSr;S~3)@X$2$#`-dp9>zQT=2SG?LSlIw6R@ zn&aG<>BHL>LQfZxa(DUafeB&nZF=>B_ZO7P)`IF8EOl7Lf~o*t{OFyZ$&Eb@w^Ytw zD6y$*s~Rke4w;ZbJ!gA^35DhZC2w8HhLDF&Q(r|yF1-7g>P^k8EbYXq5bOtK%RSo} z;hGI)uaKA1L0y~q;PjnN!xnMu>*g%E?@lV8K&6m)8l%Lndf9;(-67t=T-*{u>U?no zzC0vB!9DGa%Xdh1ND`;wU4KNLx||(Ysq2rc{0bd(pR792 za*6^MfpCUQacj%e`k12HGE!@$<}FnM>H5s>yri?hPwG^6qh|~cM&>2et4ObRKl}uZ zXWG&;e!>~AI_0FL9Ec0>xYB1bB5v%|u~A7p2Ub{UDjp=K!a&t?;2p+$@br3Fo+OGO z`f}CngK6%>mq?}Lf$yEyc3&Q7(WMwIysX4bkCN3qhaYm8iyAe+Bml3XKLe)5k?w{!< zXn|czYFWD3Fr4MAMBun5yY&e}^+XdCjtN{4g6Tqlc6lb%<8l$w0byckvTaUa6!UbjJ}pjYTbd}MTU1Z1cCAT}6^eHZ#BM3W)m zc0Ck1N&;G0H^0vwM*I$EFuVTdhNL$>OyhM=BvgD5bYNG2w0I4LMQaZ>Iq*A;c{aX}PxM+)d%n-y>kI6%3n{tIv%oCV%9eB5brLBQ&69RvgQJ;N zRdd>HBkDlQfptg9x^z+lj6`srza1V<2{^mhwkTGxSzyPI;373MB0VD0`w>^ZEe>Nr=Pk#J3tN9J%aE;qV89; zhz{SY%#?XEc(5e?RegM%Yi4Dh8J;yJlhzc!Xr$Bc9)_lvEu?X@+zp#p**x6mky&HP z4{vU!te3S7`rKnkOgkn{aB`j%06(xG(0#xHAsRTVpz!*(j&JYQ)8A4$-L}`fF)Dva zz5B@w%?|vBjAK;;WQ@o61S8=Q@LB+2(hKW)H}q#vXykX;p%Ra*b@L>8#==t<$y2@4 z#p-c&E%LD#u37c{coXf-3Yd+wox?#R+*i?1{GTa)zi;Y zbtx2Lb7m@W%^X9wf4)|J#A?xDbI+GGM-Q?;r4qa4GqB5g&oujORl3Zu-AhyUCF7v2es-811-;?7-a(yNqBn*F7!ZT zpU2At4y^YX?7O^Svg*e_8QG@GW4{^5r`HD$Y#Kx1WO9*JzSXob>an z_!rk>nzdAvp;G+iuR%?LVmonRbLY7!0?|F$6JAl{A7_&o6Z@U-YfxQb*U*WySNT7> z$VSwquAXO@E73b?!3IU~&>_X3`e!OyyPZjiSijyw?z*O7H?LrR(!YCZh@(bX@ohY@ zF2N@eqtyLVXogG=Z>sMuRd($N!MX1-_ZMz?Gy{M+-`iZRE}OSxG*eP;u9qCZs#87D z9+v2{aew@RH#e|xy6W2>3_q67#7|>$u&;8~EN1=@$GQ(mZ36j?5$0M-KVButh>LUt z*37QrvQdH=@kARoCaHU@c&BLKoyq);nJ}XIh@wZ6JTaG5Sj@x;q8UJ#WG-cnD297! zD1?5kf#xGSKVPf9dwOmA;yv8uD^aVqsHR*2a+QpNX+N4Uv2iSuz^)VX7A z?f5luxZu)?>&ZrV2cKwmIVfx)25d8uc^cO;K>LB4u?HaSku?~&(v!m3y;)>5V zYlwZIK>@~4{R76&tc^TyB!+z~!i0Y9FvbQvS|-IdOP;Zg1Y!D0-fKqsS=-busz8EV z{qFd{q9KcuUDd2j)8nY2y%(J_9m?qe;u|~6hoR_MBI)=yPE7BmqCz>Qb7*{D;9WFK zVCE3nlHZbaSU+PjGO%uCsJSE~AXjqU;N$K(OEIWIBjHe?^@@SgWorpXcZaZv8qRO3 zrjRy_a$_TX^dpo6>GRm4kAF<+D2s;1Iay9sZyCIuDbsz|5^W&T>oJWAk|&asu__@e z^9nzhsi?YsGC)uQ)CWKdz+s?dOt90G6b2BuCxUueFB(`8+p5j9W~h+BaGbz7l%5Nq z(BCyx`I-?$SG!%is8A?Dq0Fr^!@}P=w|kwbRfqAN>+YQSU_libcfVj(N8hV=wP_=o z*pT>G=V4iES6{yujIOPTvuUS6OEQq{USnpC1mu_O+iS)r=8Z}QK%Is=2E%!ld*nbb_vVeFh(do3vWUK2gxd60q@ii(T`3iZ>OqJRN|kI}27G z_N^Cu<{%bDeTz!FY3Lp{A~6H11O?{qU$%I;84`DhzLr_q3=3v}i2xcarwnkFAe6-v z4i~=w-9Ze>0`!zE{-v9`E$lJr%2LKcYV{B2;rxh&m>L;=!3`Db&+=jbKb&ui?BWNM zEGm{~PCW|p0lT)Njg{57dn;Y)AP?n)qn(2Zl9wnOZbTwp1bDR=^fijmObFfmhnaFD z^0Lb+_6!dRlr8Iv#=6R)zZZC#FMCMAdgZ2J`_RNk+y zfs^v#0>(WG<68?xKgXPq2{1%Qo4)^b9;crLLRwJI_yBO7M}$P>0Wtffy|=>!R5brd zAVONczxvu6mO=SO6>8_(n?@k=*teBH=t9K;f+G0nVT?gf6fAwuW|lc>+&oWm(F@LXe2@)=HWs0YvfT z#;2{7t5K?6^1P%uDP@it({9Hk{1WT=j%fN#x@!_duV8=m$on<*-YHPp0jIbf-6T3Z zOcm6v`9tZ%TOMX@#q6Lqvtend|IBnsADJSB!rDFid0Nnb+c-x>H48|fYTav5tJGl@ zHPtAdlm2zh@swtK210zXvekE|9iX1%yMMx@wH~yR=VJA^lQog&Htcg(y&Yj^; z;AqL1fsx`Ql)J4ZzQvV}{r-?H94`&G4%}mIt_mR+ryYBZ&g|>lo4%a6I(7-Yneu~d$K#A+Sr(jRVQ(Z&+#f!nmYNHCSpzf`)X z`n$wPNyGFjX|qu)pki0pC*CrFg?Q&521^3h!SI5&WJUg+1!D63lIb^REZtilx#3;q zW>lq65hI?#^DBy;P0+{Buu6@9WugeOVn^Wioz4UnIrk@$iGBbzEj*sLF(#*0aRzdc zVStp{`gwZ|;B<~8HI2TZIy%|Un=@*dH;Ui2dqAF1QuZ8xo{Di(-3a}c3GghS{%|!IfVoJUhUT>#1Qo> zyWTglmJf+$uTABMv`SGM7#i9f0U~GezV{gYEOEs~7Um$&y1T11MRN{Qrr%TJ01b~J z90Np3!>;ymwr*o??RZm-6oi{-E0_kC7!M>~tS@=!UHrVyX)+_@0C|S*n z3$(9vVnh2daE1n15s*LKWcqa{K-b8>9y-xV&^?V9*RG^Ve=$Qhcud?8PL+YHDXPUy zT(d#6)H_->OOGMoQ%77ZelZJB;wX+e0L#S~WSML8v$Kt1pj@H!8I+eo$IRbf8hnyk z!0LinYC~VMA$cFpPF(Fg{5@ZRxx@_rSBwiRCs?6O9^Uj-OoEu#FHV7kypF1AI z!wr#wBVM+|U(W-3ZHKPY~b8d95o{?dd{&}Z< zZPYvbRpgrbsvvvh=Ted^Ov30f==tgImmEYd5TbLv$&(aV^_@09C~QW5DB=UA`XbuO z<`YHe;mh<3`D0xKkj3jPO~4$7qawj3fsMC@P|6f;BH^fSm(q=9+Wg=Yht<}AqS+si z7*dvZR%V9HPW`27Lv=UJF9Wi>KzDJ)jyALiVo1HFfraNK8nN8y)wwg|HxD6|t|AIA zA5!ur)McB*UHMhUH=VKAC6%y|iyMYJZ_@kgc~vhHy6yWH7qZsuJG|c>0Y+|9aUtRM zWXKVLbKJ3|jDK1&7md~%Z-irDLiP8`t`M~Fnf7kJ6rrIs@j)>0)NRR@r zGFznyF9J5-A7Ru7kcBNupCTsh~ zVgJse9AG)n_HHjXtz!-yvXQ$BR9!A=t!aP6Dnw^^WN0Akj|ZtJau}aO&3{6SbS3J( z4@TDG<{KOC6qMG`zQthNdZ@EIx;~`#Y!lZ%0r~~N>(yTdN5`*3-S`Sfz@jOF=}HGs z*Cq|IG>jpBvZ16I#MXxZ_2>heP6thh#Yfp{symQlAlAjlg&4wPSwCUXyu>H0Uu*hA zch-_qPgZnjJHJ6fq3Ve&YIrf~M|(i98aTuyxT0-MGA~2-ArT*2!!9cF!-gIMh|bM> z_AXoA23B{|23j#nkn8HNn5PwmM!LT96I~Cxet8CHUZ|>@_U@NTi|-POgEf^rKrUXN z{(jY`Mdpi&BA&P%=j%49n0JOUp(KgY0DLtgfS%eB2?4Om3jj$7O4+|~2SIiY1P$`a zw^vdo3CBzcFKzS9eMBvdU)Q??>=Zh!rMOr2NjT)^=C$0;8IE1s@{b1lQbRASxE21i zuv0MBZjy3hrbov{3t3~h` zD);``&ijQcpl75ODO4qu8=(Gd!YNTlN6`HN9SN*uPz2n>)VSA9_p%%0IrMzZTCn_} zyuT=~-Oin+ZJQx2RRE8u%D%3iJ#%b9%TGJQCO)G}8;7(i9d8EV=t%MfPeK!LL6#Sa zVIy5UUzW$-m4_<1r8#C>u7jVk=Iaakyzbg92>=E)lL%&6yqH8%A4HN!*jMH4P z_yT9acJ0dAY{psaDWM%nD|q!%r$T-_;!bdHXI$}svZn(+Q`MLsjs&5fRLS~?tyhf? z!bP38LA08aw0669T&gJeCpnK_MzNMoluf#Tx6>>s=`m@$OF*u@M|f-ZO`XS~B=A8Jq(?6z%C#4Rp2poubgVu_StuOr(Rp_+ z6{7&Wrd*8T+Y#|=&9tAe!D4m_B3^hS$?fsNSE<>YthEo|BylH$IC+FthJ7ATBIhZQ zb+>M^VTq|oq0E+w)8yuweV*MZnh|J@?0_uYjx#JfZMFmeA0F~`F9;A`j$>d{RDShX zcfSzVZPNHN-+J|KVE11*$^neW?5P@Ve8qz4nohj4my&rijdAS*=KaN~>@DRL8;X$4 zHfGd2H}>s@nraQOt^5dA{EH((F(H5m0f&Qx6~M+1LwPH*5)aZHtQNw?hg)m&jl@BYj1q8iR#L8ChqlLE>}O$YeA+G% zUHM%q1D$u9BZ=Yww6T)r)`%1KcDNqEO3BEDK8E6ePY_LfUC#O;ZN8#qd<~WUA;1Mg zud}M8M621r<^NCJ)a zQ${!;BEsNk@5tTDmtFCb_HJPe^t1;7*(JBQ8{@Tfp~?62;)y!Do8M=OIS48wQ!f@R z^aCC!kBp*of@T%q)AzlHOexl~D3zaU`qm8j%Zd+_sYik1&|xu(I>{X!=$|S10gZYZ z&kU1_i3R-^L)KuU4`74S6NQN0lrO){CO%+--#T=5G6l^NhVM=FR}OdZg7kV8Mv2WW zyb9QkfK||p76e&y(YepwE?SYUez$wAO=?`9A$yLyRrCD0KcR0nqtGXPlqz~?Ey9rB z!Nw_xo)bk+Nl+OUv%-KNx#;qPetGfkTx|OoF@&}cjb8;e=$Vx%X81%6 zWUSNHPmh_=AtfT8P^F_p_?ULxpV;Hy+0kxGE*(S!Aih0~Yl=Mvh(5JVWk(Skss0I- zsc{wvY;N2H)VO)$$#iUA%IBqn*el?b0dIz+xubg;S!msOxr4C)h|0w{D?J*>d2La}XJsqupT2+y)vXL5>xG&~(lR8xuhm_^E(0|uiZ*~c zD}M$RWMiOmv=s^_gN}~Gq zZ^Oa7*Ws_VxBJ6)L@*cLwze*AS-w<15k_13*uL|G=w#;;dRmc|;V8LSmzO}M_YlSL%DYz%0?T$ zrXL=OTKELq6kej$AC=KxAO)nGaTPD=I6C3uKAMU;2Rx+KyC8FR2E?lqg}Zn-keRt9 zJSfiDGR$QOygEA^e8Ita11&&U^wO{gi~Lyy4brHA6eQ<8y(HV{m|-)k73q8Kf`&t8 zNrqI>OfKzE7;Qh8gZS?OMwNFyQGj0q*b;)kZD8Y>;=qamxvkb+VFYoG7At*cdPasH zG(oznHEs=+aS=GQbBUdG(ufjqqg$-5LV`7>O#^w^7)gwEM=EAtzrS2@KBZ<{(0fkQJWrdA_T$asMHi8^5J`!R>G%H*HSW4-t^Q^<7AnA$-njye+4(kBt z2Q`Q^B3jj~p`l@N_^XDbbO^p}GHeyAf%E?4GfE4(KAX+b&N1R@7;mQK<|W$9vGdjs z1pvj&_*|3igw!uz19>W@X|a@6`(N__UZm( zj-Yhj6NuMYX=2QWjEWlKn&s}eEVTN3pS&$-n4W}vKLj55nkA^nX4C;_D&X6Sf>Bnk zmXly>O|YFwP&d*ChQnL?l5Gl#!gzxFzBlvrIzLF%Bvo<;g47TUu#x1GY}t$t;Mf2(?-4d>+6~mDgviE5m0rTmE(Y>k zkVUc;=Yf`9aKQ14Y8gkC-qV!tr&aqk;AN*;9vg_Fj;MjV0vO-KBjNQ6 z6*0EBXOvHjUB;sOyAmLI5#@?fItU5464n()XhLo6$uB z{&N?ikz!1i_U|eWj6qop7TXjM$D0PO_&K(!Y22qE6a4h3&~Nq->kmNr?#C z37)EZ9XM*@ACAS5z2#2zvg5Z;mcl!!s+ADcGW<)!7#&BK$IxE?xC3b#gx41d-eEos zALg099j@%6JMOuZ5KM@IQJh?`l>;ZhfUN`?Fu~7f4Lmufw*W{sCz#C@PO!9h1HN%K zMpG-A`LR4f)UW*v`6E6K3kS&i(w6dF?tCP7!vg2ycpuzZRD|Q`z)=J<2e1g9Ow1UY zEE3!)h7yXd|ef!Fb`+21Aj%@HmRgsqvse^)h zxyw=(RpbCEM79FN0dy467QnlG>nw0_d3`L0jcixTCsj<}xO+x9F#SQ_(Ht(=g6}`R zqe5v~gx3#lKcsQ3fjd?l?Vqf{D$#TRbFVEIIz}BXerNzFpp90QfCAb-j5Boa5k|Ot z>2fJu68@A4lqma)o?;%AJ)mv=VZlN7GSi?;H2*}vn*Eyh9Uv7@!M#;HAnx`M0fOke z>*N1Y}8I;%|M?{joJM)Ta|YC<9fW{>blntz^pz6A|nR5%x3;; zHS=d9tBah6NpHDpf%XdDuH!6AutSpc?P^;>!1C_0P+csQT%UhXFcKpX!La>ew)n1mGo?bGKCVvz`WGeHMgZp@ z(qwDfQuWPAzf9kEtGhyY&EOZdJUnB4 ztK>A=@=b#%cIbdQ4}?1sSiEJH=VsMaJpW;#5kMsfV|O~LFBv~^sZZCzCEVu`HPt(e z74=|Ij!2nlE+V%kPvZIXt=m1Jhkz38>lFe)X}m=KK%`iE01wV1%zCT^I2{`J!y!l| z{0Io5WUJYnT)=2CuWMJ#8v=yTfHmo2{VtDhpzl~ApA1CB9L87;R!(mpi?;Gr2$vwLnHC_A0_t6vF-Z4G8X!REwaj3_PKLQVpFJ_2~0kmzy~`ifZLzDG}WS6JOu~egaA^O5at^ z`hS5tGKWxIdb22o)_4XV@8UYyNQ={7-}waL$TJ_0?-brque+$`$vUWYahvpS zzZEzWNtt}&z2F6Bd2lz?vE&bn*-h?zqEvx;pEat3T??>}opTwa46Ie+MZAxvL= zSLwKlt0ul*Q>JCL4AY(so^cq8vX%mJo3ct}HCb*kdgZR@wHq^S-xIUoc!8aU6rPgT z;@2sXmX$VgB|WV);KF{b204LN(O-+l;pNy!6NRCSnz#O!MOdU_vlkZqGUB&ti$=Y` zMAreed5NopyWbtCE$GXr4|P#Kk6r!yd&`BI#CMqj*FkVL;k#+Cr1;qgz|nv(j3{Sn zXa%$S54a1lotsu82q`tO^W|59S*U#Sjl;oR7OEZpq9E9 z2_M`}U>zVF>@+!$Lg0vH<5c)UfL6&lwv7hl#BhL^g5V`k@*6+?UXl*;>VicqraF?i z`q5Q+2uVg*uVmTndqbg&;!J;vCO2urvMK0di;sIxS&a0gT)IQPwKD1fMTQ{`8h<8D zHy+UaY}gSMEjJ$Q?u>ya`%3WOr;D8Ao-X#&*ID3U3#BtQa&!mgzR zS79i~cQgksO$r|~eK3D}K(G2A6D;|%6L>?oVu&W-Q-8l<#ZBG^m_<4;kFGu|{bq5< zeHC9lrMry&8$Gq_}JUhlMcU3G_c+ zo37A>Ss1dw3@0-55Ob>X%{?DhezDf|0`x9n#86KF0JkEr((JTqz3bpV!jbP$DnE#y zjB5~OBjksbP0~f4^&A&Atqub(@T(a82pl#jSPsZ1k+zgtZ?N?}@$>A!UyIXE!K*02 z(Z`i_gDP=(uKcE!u^zMDIpkGL;T`6KWmuF zI*iaxf|3r8pY<@EKA9|7`qDU~b5p|gL-s$_be8i?FI=ULBj+di80V5x?2*~W*M%NU z(=CZImrw*Tcr*nD{1Bko6Ag|5E@It-^TV+ao+YJn1%<8yK0`kb14GuXcku#cvETr; zIgK!6!M-Dgq0w!@dm}W4btQszWj}!~6X(j$j@{X+O^Wv|D%sTGhLZmCcaoi?P|_DH zdsy4a)EF_W&(oj6o$usNXa4T$haEq;;o~11&Ao_^tuRpU-FUW)yL$TH-rg1A{#&+M zu0v`XdjMcMfuhtgp#Up~d5hwt6>}kv^rb(wefkY!YOfr{udJ?%ZB%nT{?Q5R6;#M4 zA90-c=4q<_P!d$dH3=vGiYD1u-EC2?{_-9G@F;yQK*VDHo(6vY6&U-W>XH=K%0;zT z*^brOGyBdI2+lOz(BcFNDq=rSF?)R^ngQYf#AQHcr*;5%#twGu!d|(MsN%2IbIRTq zsalbJ{0=UU)~3r{tAF^%ro-uOFMNeu!6X&FvU=;!SFfmfs@(94{T0soQuBgjYx7Qn( z{(LVZ0TTJ_@q}4-L7?;v5-{sLTnU6su}bV!xsw=l*nHw}$z8t&XRh0hwJlaP>iA@8 z_qpuZA6*KwK%?uj35ortJzp8UO%1aqQ&EwH*X@PC&C<}_@jjBS2u*O(J^QOU(cfC0 zOMc-e8epi4VhRnJ{aA3@H`oFv4Xmrit%L|f`o^*FL8HhhN5jGjrqiiu$%!Dfp8q_( z2gqtv>LWpTav%kBW%bf(AW#dkr)wD2J!2MOfw#hb&}|G0 z@q9KmIw5w=>k*7^_x)=Qi5gbuMY$p2{LbNJ8(WY+2gNt%Ycu{6K~k^x?!L6kh3iPr zHwlT=X_c<1h+yWX!WAH)1$wpTO8ftxf07P-iUl;vi*+1K=ZghX-~#5A4*}MHt6XKT zO38Q3OU|nSKEg9qa|%Chr&^4-ypjW|ZJeE{JhT9pzg0lSiv6i}-y-@Yy7O3_Vrg}! z@2K0WAND*z5ba6?ZzB+eq#K_7^Tvh*=HkcIcYgyVo7oTP3ZFsR^@RFCwp0r??RRN} z3dpxLagP5*2Abl>9`?z$fI&iEq;&CVKHjL}<~A_JKXYq}`V0ocZ&H zDqr0~T-%mI{Lg=ZyChR<`%fGLZx%&CciSCBa$UY{ugC5w%Y3(~VyNnm0cmpV&XIBd zwvNR*^Z)iidII2r>OY?as24Ztj)Q}KQR(`7`{<&Nmd3#&wf#OvwXi?`(-L}Q`kHVD zl8mg8jo=>RI41ohH5PL|WLodHg3O6+KN-pk-3r_XdjYsC*Eu^;v#%XffgI>8&jp zy`}o5`Pxpd|MXavmmP?9iPX0AE~}rH{kPLs1t_81hueOu8A<^ih5w9JarVBTD+H5I zn{WR4&tSV?I9Gn;a?$_$r^-$sZi(MG9xP+}-#(~U^qMz{qKUSfgHhWhXIA6TK^L1w!nR-lCXTRPugAKd=ouDfu|ANdj1=Ww z$hk1*V%a@AK{vckhyqEZpn5;DA@^c=9+2z$-(LnM2wc3Hu!h2c$-9AJ+ZZPaRIQi7 zi&y{SZ(^ut$-~6kZlAwnCh9uC`rDhx$F^4~;IIR6-oM`j!3i){ph>$=y$kFdcs<)J zvHk&8>hBA?7LEQ07`Q$6c8wvKePz|2wp~*OqPqWjD!6YNY$0(Z+lKKW*e30NykK5; z!dmj%*=YF bJG`NPnMy;Nx4?}~d-mv{4YUe1?uPs?@I4cQ diff --git a/samples/data/detect_blob.png b/samples/data/detect_blob.png new file mode 100644 index 0000000000000000000000000000000000000000..5e6e9a06663fcdb7a9c84645d15fc847a5466213 GIT binary patch literal 57892 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 literal 0 HcmV?d00001 From c8167c0b999071ddaebf99dbfd36a11af8cf8150 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 22 Jun 2015 20:14:30 +0300 Subject: [PATCH 054/133] fix cyclic deps error (world,shared) --- modules/stitching/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/stitching/CMakeLists.txt b/modules/stitching/CMakeLists.txt index 9a24f43160..76c7bc8489 100644 --- a/modules/stitching/CMakeLists.txt +++ b/modules/stitching/CMakeLists.txt @@ -4,6 +4,10 @@ if(HAVE_CUDA) ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef -Wmissing-declarations -Wshadow) endif() +set(STITCHING_CONTRIB_DEPS "opencv_xfeatures2d") +if(BUILD_SHARED_LIBS AND BUILD_opencv_world) + set(STITCHING_CONTRIB_DEPS "") +endif() ocv_define_module(stitching opencv_imgproc opencv_features2d opencv_calib3d opencv_objdetect - OPTIONAL opencv_cudaarithm opencv_cudafilters opencv_cudafeatures2d opencv_cudalegacy opencv_xfeatures2d + OPTIONAL opencv_cudaarithm opencv_cudafilters opencv_cudafeatures2d opencv_cudalegacy ${STITCHING_CONTRIB_DEPS} WRAP python) From 53fc5440d78fd9ebe727c51243528e1eac3b5e35 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 23 Jun 2015 14:31:01 +0300 Subject: [PATCH 055/133] implement singleton lazy initialization --- modules/core/src/dxt.cpp | 6 +-- modules/core/src/matop.cpp | 3 +- modules/core/src/matrix.cpp | 7 +--- modules/core/src/ocl.cpp | 76 +++++++++++++++++------------------- modules/core/src/precomp.hpp | 16 ++++++++ modules/core/src/system.cpp | 25 ++++++++---- 6 files changed, 72 insertions(+), 61 deletions(-) diff --git a/modules/core/src/dxt.cpp b/modules/core/src/dxt.cpp index e3fd0e4c40..ef01c2139f 100644 --- a/modules/core/src/dxt.cpp +++ b/modules/core/src/dxt.cpp @@ -2026,8 +2026,7 @@ class OCL_FftPlanCache public: static OCL_FftPlanCache & getInstance() { - static OCL_FftPlanCache planCache; - return planCache; + CV_SINGLETON_LAZY_INIT_REF(OCL_FftPlanCache, new OCL_FftPlanCache()) } Ptr getFftPlan(int dft_size, int depth) @@ -2291,8 +2290,7 @@ class PlanCache public: static PlanCache & getInstance() { - static PlanCache planCache; - return planCache; + CV_SINGLETON_LAZY_INIT_REF(PlanCache, new PlanCache()) } clAmdFftPlanHandle getPlanHandle(const Size & dft_size, int src_step, int dst_step, bool doubleFP, diff --git a/modules/core/src/matop.cpp b/modules/core/src/matop.cpp index a0ee4316d9..b512485d16 100644 --- a/modules/core/src/matop.cpp +++ b/modules/core/src/matop.cpp @@ -205,8 +205,7 @@ public: static MatOp_Initializer* getGlobalMatOpInitializer() { - static MatOp_Initializer initializer; - return &initializer; + CV_SINGLETON_LAZY_INIT(MatOp_Initializer, new MatOp_Initializer()) } static inline bool isIdentity(const MatExpr& e) { return e.op == &g_MatOp_Identity; } diff --git a/modules/core/src/matrix.cpp b/modules/core/src/matrix.cpp index 5de7e034fd..9b39fe64d3 100644 --- a/modules/core/src/matrix.cpp +++ b/modules/core/src/matrix.cpp @@ -222,14 +222,9 @@ public: } }; -static StdMatAllocator *mat_allocator = NULL; MatAllocator* Mat::getStdAllocator() { - if (mat_allocator == NULL) - { - mat_allocator = new StdMatAllocator(); - } - return mat_allocator; + CV_SINGLETON_LAZY_INIT(MatAllocator, new StdMatAllocator()) } void swap( Mat& a, Mat& b ) diff --git a/modules/core/src/ocl.cpp b/modules/core/src/ocl.cpp index 9d25eb0732..0378b66c04 100644 --- a/modules/core/src/ocl.cpp +++ b/modules/core/src/ocl.cpp @@ -1510,8 +1510,7 @@ class AmdBlasHelper public: static AmdBlasHelper & getInstance() { - static AmdBlasHelper amdBlas; - return amdBlas; + CV_SINGLETON_LAZY_INIT_REF(AmdBlasHelper, new AmdBlasHelper()) } bool isAvailable() const @@ -1533,35 +1532,36 @@ protected: { if (!g_isAmdBlasInitialized) { - AutoLock lock(m); + AutoLock lock(getInitializationMutex()); - if (!g_isAmdBlasInitialized && haveOpenCL()) + if (!g_isAmdBlasInitialized) { - try + if (haveOpenCL()) { - g_isAmdBlasAvailable = clAmdBlasSetup() == clAmdBlasSuccess; + try + { + g_isAmdBlasAvailable = clAmdBlasSetup() == clAmdBlasSuccess; + } + catch (...) + { + g_isAmdBlasAvailable = false; + } } - catch (...) - { + else g_isAmdBlasAvailable = false; - } - } - else - g_isAmdBlasAvailable = false; - g_isAmdBlasInitialized = true; + g_isAmdBlasInitialized = true; + } } } private: - static Mutex m; static bool g_isAmdBlasInitialized; static bool g_isAmdBlasAvailable; }; bool AmdBlasHelper::g_isAmdBlasAvailable = false; bool AmdBlasHelper::g_isAmdBlasInitialized = false; -Mutex AmdBlasHelper::m; bool haveAmdBlas() { @@ -1584,8 +1584,7 @@ class AmdFftHelper public: static AmdFftHelper & getInstance() { - static AmdFftHelper amdFft; - return amdFft; + CV_SINGLETON_LAZY_INIT_REF(AmdFftHelper, new AmdFftHelper()) } bool isAvailable() const @@ -1607,34 +1606,36 @@ protected: { if (!g_isAmdFftInitialized) { - AutoLock lock(m); + AutoLock lock(getInitializationMutex()); - if (!g_isAmdFftInitialized && haveOpenCL()) + if (!g_isAmdFftInitialized) { - try + if (haveOpenCL()) { - cl_uint major, minor, patch; - CV_Assert(clAmdFftInitSetupData(&setupData) == CLFFT_SUCCESS); + try + { + cl_uint major, minor, patch; + CV_Assert(clAmdFftInitSetupData(&setupData) == CLFFT_SUCCESS); - // it throws exception in case AmdFft binaries are not found - CV_Assert(clAmdFftGetVersion(&major, &minor, &patch) == CLFFT_SUCCESS); - g_isAmdFftAvailable = true; + // it throws exception in case AmdFft binaries are not found + CV_Assert(clAmdFftGetVersion(&major, &minor, &patch) == CLFFT_SUCCESS); + g_isAmdFftAvailable = true; + } + catch (const Exception &) + { + g_isAmdFftAvailable = false; + } } - catch (const Exception &) - { + else g_isAmdFftAvailable = false; - } - } - else - g_isAmdFftAvailable = false; - g_isAmdFftInitialized = true; + g_isAmdFftInitialized = true; + } } } private: static clAmdFftSetupData setupData; - static Mutex m; static bool g_isAmdFftInitialized; static bool g_isAmdFftAvailable; }; @@ -1642,7 +1643,6 @@ private: clAmdFftSetupData AmdFftHelper::setupData; bool AmdFftHelper::g_isAmdFftAvailable = false; bool AmdFftHelper::g_isAmdFftInitialized = false; -Mutex AmdFftHelper::m; bool haveAmdFft() { @@ -5237,15 +5237,9 @@ public: MatAllocator* matStdAllocator; }; -// This line should not force OpenCL runtime initialization! (don't put "new OpenCLAllocator()" here) -static MatAllocator *ocl_allocator = NULL; MatAllocator* getOpenCLAllocator() { - if (ocl_allocator == NULL) - { - ocl_allocator = new OpenCLAllocator(); - } - return ocl_allocator; + CV_SINGLETON_LAZY_INIT(MatAllocator, new OpenCLAllocator()) } }} // namespace cv::ocl diff --git a/modules/core/src/precomp.hpp b/modules/core/src/precomp.hpp index 88b60e4713..d8d7e007ee 100644 --- a/modules/core/src/precomp.hpp +++ b/modules/core/src/precomp.hpp @@ -295,6 +295,22 @@ TLSData& getCoreTlsData(); extern bool __termination; // skip some cleanups, because process is terminating // (for example, if ExitProcess() was already called) +cv::Mutex& getInitializationMutex(); + +// TODO Memory barriers? +#define CV_SINGLETON_LAZY_INIT_(TYPE, INITIALIZER, RET_VALUE) \ + static TYPE* volatile instance = NULL; \ + if (instance == NULL) \ + { \ + cv::AutoLock lock(cv::getInitializationMutex()); \ + if (instance == NULL) \ + instance = INITIALIZER; \ + } \ + return RET_VALUE; + +#define CV_SINGLETON_LAZY_INIT(TYPE, INITIALIZER) CV_SINGLETON_LAZY_INIT_(TYPE, INITIALIZER, instance) +#define CV_SINGLETON_LAZY_INIT_REF(TYPE, INITIALIZER) CV_SINGLETON_LAZY_INIT_(TYPE, INITIALIZER, *instance) + } #include "opencv2/hal/intrin.hpp" diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index 46f41dcca9..6a85c40ee3 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -43,6 +43,20 @@ #include "precomp.hpp" +namespace cv { + +static Mutex* __initialization_mutex = NULL; +Mutex& getInitializationMutex() +{ + if (__initialization_mutex == NULL) + __initialization_mutex = new Mutex(); + return *__initialization_mutex; +} +// force initialization (single-threaded environment) +Mutex* __initialization_mutex_initializer = &getInitializationMutex(); + +} // namespace cv + #ifdef _MSC_VER # if _MSC_VER >= 1700 # pragma warning(disable:4447) // Disable warning 'main' signature found without threading model @@ -1108,8 +1122,7 @@ public: // For more information: http://www.parashift.com/c++-faq/static-init-order-on-first-use.html static TLSContainerStorage& getTLSContainerStorage() { - static TLSContainerStorage *tlsContainerStorage = new TLSContainerStorage(); - return *tlsContainerStorage; + CV_SINGLETON_LAZY_INIT_REF(TLSContainerStorage, new TLSContainerStorage()) } TLSDataContainer::TLSDataContainer() @@ -1153,20 +1166,16 @@ TLSStorage::~TLSStorage() } - TLSData& getCoreTlsData() { - static TLSData *value = new TLSData(); - return *value; + CV_SINGLETON_LAZY_INIT_REF(TLSData, new TLSData()) } - #ifdef CV_COLLECT_IMPL_DATA ImplCollector& getImplData() { - static ImplCollector *value = new ImplCollector(); - return *value; + CV_SINGLETON_LAZY_INIT_REF(ImplCollector, new ImplCollector()) } void setImpl(int flags) From 8439b5942bd01ddb36043187eab807f58ce0a64b Mon Sep 17 00:00:00 2001 From: Pavel Vlasov Date: Wed, 24 Jun 2015 09:21:35 +0300 Subject: [PATCH 056/133] Proper IPP alignment; --- modules/core/include/opencv2/core/private.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/private.hpp b/modules/core/include/opencv2/core/private.hpp index 4f9f487778..72f4ddc02c 100644 --- a/modules/core/include/opencv2/core/private.hpp +++ b/modules/core/include/opencv2/core/private.hpp @@ -195,7 +195,10 @@ CV_EXPORTS void scalarToRawData(const cv::Scalar& s, void* buf, int type, int un # define IPP_VERSION_X100 (IPP_VERSION_MAJOR * 100 + IPP_VERSION_MINOR) -#define IPP_ALIGN 32 // required for AVX optimization +#ifdef CV_MALLOC_ALIGN +#undef CV_MALLOC_ALIGN +#endif +#define CV_MALLOC_ALIGN 32 // required for AVX optimization #define setIppErrorStatus() cv::ipp::setIppStatus(-1, CV_Func, __FILE__, __LINE__) From 101607a7d05dcf6a616e9e5b22e120ae438aefd4 Mon Sep 17 00:00:00 2001 From: Pavel Vlasov Date: Wed, 24 Jun 2015 13:50:17 +0300 Subject: [PATCH 057/133] Imgproc_Hist_MinMaxVal.accuracy fix; Some code style corrections; --- modules/core/src/convert.cpp | 106 ++-- modules/core/src/copy.cpp | 19 +- modules/core/src/matrix.cpp | 11 +- modules/core/src/precomp.hpp | 1 - modules/core/src/stat.cpp | 996 ++++++++++++++++---------------- modules/imgproc/src/imgwarp.cpp | 2 +- modules/imgproc/src/morph.cpp | 16 +- modules/imgproc/src/thresh.cpp | 11 +- 8 files changed, 556 insertions(+), 606 deletions(-) diff --git a/modules/core/src/convert.cpp b/modules/core/src/convert.cpp index cebbb154e2..7e561b6e75 100644 --- a/modules/core/src/convert.cpp +++ b/modules/core/src/convert.cpp @@ -5194,10 +5194,7 @@ dtype* dst, size_t dstep, Size size, double* scale) \ static void cvt##suffix( const stype* src, size_t sstep, const uchar*, size_t, \ dtype* dst, size_t dstep, Size size, double*) \ { \ - if (src && dst)\ - {\ - CV_IPP_RUN(true, ippiConvert_##ippFavor(src, (int)sstep, dst, (int)dstep, ippiSize(size.width, size.height)) >= 0)\ - }\ + CV_IPP_RUN(src && dst, ippiConvert_##ippFavor(src, (int)sstep, dst, (int)dstep, ippiSize(size.width, size.height)) >= 0)\ cvt_(src, sstep, dst, dstep, size); \ } @@ -5205,10 +5202,7 @@ static void cvt##suffix( const stype* src, size_t sstep, const uchar*, size_t, \ static void cvt##suffix( const stype* src, size_t sstep, const uchar*, size_t, \ dtype* dst, size_t dstep, Size size, double*) \ { \ - if (src && dst)\ - {\ - CV_IPP_RUN(true, ippiConvert_##ippFavor(src, (int)sstep, dst, (int)dstep, ippiSize(size.width, size.height), ippRndFinancial, 0) >= 0)\ - }\ + CV_IPP_RUN(src && dst, ippiConvert_##ippFavor(src, (int)sstep, dst, (int)dstep, ippiSize(size.width, size.height), ippRndFinancial, 0) >= 0)\ cvt_(src, sstep, dst, dstep, size); \ } #else @@ -5844,6 +5838,46 @@ private: IppLUTParallelBody_LUTCN& operator=(const IppLUTParallelBody_LUTCN&); }; } // namespace ipp + +static bool ipp_lut(Mat &src, Mat &lut, Mat &dst) +{ + int cn = src.channels(); + int lutcn = lut.channels(); + + if(src.dims > 2) + return false; + + bool ok = false; + Ptr body; + + size_t elemSize1 = CV_ELEM_SIZE1(dst.depth()); +#if 0 // there are no performance benefits (PR #2653) + if (lutcn == 1) + { + ParallelLoopBody* p = new ipp::IppLUTParallelBody_LUTC1(src, lut, dst, &ok); + body.reset(p); + } + else +#endif + if ((lutcn == 3 || lutcn == 4) && elemSize1 == 1) + { + ParallelLoopBody* p = new ipp::IppLUTParallelBody_LUTCN(src, lut, dst, &ok); + body.reset(p); + } + + if (body != NULL && ok) + { + Range all(0, dst.rows); + if (dst.total()>>18) + parallel_for_(all, *body, (double)std::max((size_t)1, dst.total()>>16)); + else + (*body)(all); + if (ok) + return true; + } + + return false; +} #endif // IPP class LUTParallelBody : public ParallelLoopBody @@ -5891,55 +5925,6 @@ private: } -namespace cv -{ -#if defined(HAVE_IPP) -static bool ipp_lut(InputArray _src, InputArray _lut, OutputArray _dst) -{ - int cn = _src.channels(); - int lutcn = _lut.channels(); - - Mat src = _src.getMat(), lut = _lut.getMat(); - _dst.create(src.dims, src.size, CV_MAKETYPE(_lut.depth(), cn)); - Mat dst = _dst.getMat(); - - if (_src.dims() <= 2) - { - bool ok = false; - Ptr body; - - size_t elemSize1 = CV_ELEM_SIZE1(dst.depth()); -#if 0 // there are no performance benefits (PR #2653) - if (lutcn == 1) - { - ParallelLoopBody* p = new ipp::IppLUTParallelBody_LUTC1(src, lut, dst, &ok); - body.reset(p); - } - else -#endif - if ((lutcn == 3 || lutcn == 4) && elemSize1 == 1) - { - ParallelLoopBody* p = new ipp::IppLUTParallelBody_LUTCN(src, lut, dst, &ok); - body.reset(p); - } - - if (body != NULL && ok) - { - Range all(0, dst.rows); - if (dst.total()>>18) - parallel_for_(all, *body, (double)std::max((size_t)1, dst.total()>>16)); - else - (*body)(all); - if (ok) - return true; - } - } - return false; -} -#endif -} - - void cv::LUT( InputArray _src, InputArray _lut, OutputArray _dst ) { int cn = _src.channels(), depth = _src.depth(); @@ -5952,18 +5937,17 @@ void cv::LUT( InputArray _src, InputArray _lut, OutputArray _dst ) CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2, ocl_LUT(_src, _lut, _dst)) - CV_IPP_RUN((_src.dims() <= 2 && ((lutcn == 1 || lutcn == 3 || lutcn == 4) && CV_ELEM_SIZE1(_dst.depth()) == 1) && lutcn != 1), //lutcn == 1 ipp implementation switched off - ipp_lut(_src, _lut, _dst)); - - Mat src = _src.getMat(), lut = _lut.getMat(); _dst.create(src.dims, src.size, CV_MAKETYPE(_lut.depth(), cn)); Mat dst = _dst.getMat(); + CV_IPP_RUN(_src.dims() <= 2, ipp_lut(src, lut, dst)); + if (_src.dims() <= 2) { bool ok = false; Ptr body; + if (body == NULL || ok == false) { ok = false; diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index ba09b54e6d..27e8c00d5c 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -424,9 +424,8 @@ Mat& Mat::operator = (const Scalar& s) } #if defined HAVE_IPP -static bool ipp_Mat_setTo(Mat *src, InputArray _value, InputArray _mask) +static bool ipp_Mat_setTo(Mat *src, Mat &value, Mat &mask) { - Mat value = _value.getMat(), mask = _mask.getMat(); int cn = src->channels(), depth0 = src->depth(); if (!mask.empty() && (src->dims <= 2 || (src->isContinuous() && mask.isContinuous())) && @@ -515,8 +514,7 @@ Mat& Mat::setTo(InputArray _value, InputArray _mask) CV_Assert( checkScalar(value, type(), _value.kind(), _InputArray::MAT )); CV_Assert( mask.empty() || (mask.type() == CV_8U && size == mask.size) ); - CV_IPP_RUN(true, ipp_Mat_setTo((cv::Mat*)this, _value, _mask), *this) - + CV_IPP_RUN(true, ipp_Mat_setTo((cv::Mat*)this, value, mask), *this) size_t esz = elemSize(); BinaryFunc copymask = getCopyMaskFunc(esz); @@ -691,13 +689,10 @@ static bool ocl_flip(InputArray _src, OutputArray _dst, int flipCode ) #endif #if defined HAVE_IPP -static bool ipp_flip( InputArray _src, OutputArray _dst, int flip_mode ) +static bool ipp_flip( Mat &src, Mat &dst, int flip_mode ) { - Size size = _src.size(); - Mat src = _src.getMat(); + Size size = src.size(); int type = src.type(); - _dst.create( size, type ); - Mat dst = _dst.getMat(); typedef IppStatus (CV_STDCALL * ippiMirror)(const void * pSrc, int srcStep, void * pDst, int dstStep, IppiSize roiSize, IppiAxis flip); typedef IppStatus (CV_STDCALL * ippiMirrorI)(const void * pSrcDst, int srcDstStep, IppiSize roiSize, IppiAxis flip); @@ -786,13 +781,13 @@ void flip( InputArray _src, OutputArray _dst, int flip_mode ) CV_OCL_RUN( _dst.isUMat(), ocl_flip(_src, _dst, flip_mode)) - CV_IPP_RUN(true, ipp_flip(_src, _dst, flip_mode)); - - Mat src = _src.getMat(); int type = src.type(); _dst.create( size, type ); Mat dst = _dst.getMat(); + + CV_IPP_RUN(true, ipp_flip(src, dst, flip_mode)); + size_t esz = CV_ELEM_SIZE(type); if( flip_mode <= 0 ) diff --git a/modules/core/src/matrix.cpp b/modules/core/src/matrix.cpp index ea82d68f1c..b03aa614d9 100644 --- a/modules/core/src/matrix.cpp +++ b/modules/core/src/matrix.cpp @@ -3088,19 +3088,15 @@ static bool ocl_transpose( InputArray _src, OutputArray _dst ) #endif - #ifdef HAVE_IPP -static bool ipp_transpose( InputArray _src, OutputArray _dst ) +static bool ipp_transpose( Mat &src, Mat &dst ) { - int type = _src.type(); + int type = src.type(); typedef IppStatus (CV_STDCALL * ippiTranspose)(const void * pSrc, int srcStep, void * pDst, int dstStep, IppiSize roiSize); typedef IppStatus (CV_STDCALL * ippiTransposeI)(const void * pSrcDst, int srcDstStep, IppiSize roiSize); ippiTranspose ippFunc = 0; ippiTransposeI ippFuncI = 0; - Mat dst = _dst.getMat(); - Mat src = _src.getMat(); - if (dst.data == src.data && dst.cols == dst.rows) { CV_SUPPRESS_DEPRECATED_START @@ -3186,8 +3182,7 @@ void cv::transpose( InputArray _src, OutputArray _dst ) return; } - CV_IPP_RUN(true, ipp_transpose(_src, _dst)) - + CV_IPP_RUN(true, ipp_transpose(src, dst)) if( dst.data == src.data ) { diff --git a/modules/core/src/precomp.hpp b/modules/core/src/precomp.hpp index 81b9674c75..88b60e4713 100644 --- a/modules/core/src/precomp.hpp +++ b/modules/core/src/precomp.hpp @@ -72,7 +72,6 @@ #define GET_OPTIMIZED(func) (func) #endif - namespace cv { diff --git a/modules/core/src/stat.cpp b/modules/core/src/stat.cpp index 811fa2cca6..d47688ba1c 100644 --- a/modules/core/src/stat.cpp +++ b/modules/core/src/stat.cpp @@ -1138,23 +1138,19 @@ static bool ocl_sum( InputArray _src, Scalar & res, int sum_op, InputArray _mask #endif -} - -#if defined (HAVE_IPP) -namespace cv +#ifdef HAVE_IPP +static bool ipp_sum(Mat &src, Scalar &_res) { - static bool ipp_sum(Mat src, Scalar & _res) -{ -#if (IPP_VERSION_MAJOR >= 7) +#if IPP_VERSION_MAJOR >= 7 int cn = src.channels(); size_t total_size = src.total(); - int rows = src.size[0], cols = rows ? (int)(total_size / rows) : 0; - if (src.dims == 2 || (src.isContinuous() && cols > 0 && (size_t)rows*cols == total_size)) + int rows = src.size[0], cols = rows ? (int)(total_size/rows) : 0; + if( src.dims == 2 || (src.isContinuous() && cols > 0 && (size_t)rows*cols == total_size) ) { IppiSize sz = { cols, rows }; int type = src.type(); - typedef IppStatus(CV_STDCALL* ippiSumFuncHint)(const void*, int, IppiSize, double *, IppHintAlgorithm); - typedef IppStatus(CV_STDCALL* ippiSumFuncNoHint)(const void*, int, IppiSize, double *); + typedef IppStatus (CV_STDCALL* ippiSumFuncHint)(const void*, int, IppiSize, double *, IppHintAlgorithm); + typedef IppStatus (CV_STDCALL* ippiSumFuncNoHint)(const void*, int, IppiSize, double *); ippiSumFuncHint ippFuncHint = type == CV_32FC1 ? (ippiSumFuncHint)ippiSum_32f_C1R : type == CV_32FC3 ? (ippiSumFuncHint)ippiSum_32f_C3R : @@ -1172,49 +1168,45 @@ namespace cv type == CV_16SC4 ? (ippiSumFuncNoHint)ippiSum_16s_C4R : 0; CV_Assert(!ippFuncHint || !ippFuncNoHint); - if (ippFuncHint || ippFuncNoHint) + if( ippFuncHint || ippFuncNoHint ) { Ipp64f res[4]; IppStatus ret = ippFuncHint ? ippFuncHint(src.ptr(), (int)src.step[0], sz, res, ippAlgHintAccurate) : - ippFuncNoHint(src.ptr(), (int)src.step[0], sz, res); - CV_Assert(cn <= 4); - cn = min(cn, 4); - if (ret >= 0) + ippFuncNoHint(src.ptr(), (int)src.step[0], sz, res); + if( ret >= 0 ) { for( int i = 0; i < cn; i++ ) _res[i] = res[i]; return true; } - return false; } } +#else + CV_UNUSED(src); CV_UNUSED(_res); #endif return false; } -} #endif +} + cv::Scalar cv::sum( InputArray _src ) { +#if defined HAVE_OPENCL || defined HAVE_IPP Scalar _res; +#endif + #ifdef HAVE_OPENCL CV_OCL_RUN_(OCL_PERFORMANCE_CHECK(_src.isUMat()) && _src.dims() <= 2, ocl_sum(_src, _res, OCL_OP_SUM), _res) #endif -#ifdef HAVE_IPP - size_t total_size = _src.total(); - int rows = _src.rows(); - int cols = rows ? (int)(total_size / rows) : 0; -#endif Mat src = _src.getMat(); - CV_IPP_RUN((_src.dims() == 2 || (_src.isContinuous() && cols > 0 && (size_t)rows*cols == total_size)) && IPP_VERSION_MAJOR >= 7, - ipp_sum(src, _res), _res); + CV_IPP_RUN(IPP_VERSION_MAJOR >= 7, ipp_sum(src, _res), _res); + int k, cn = src.channels(), depth = src.depth(); - SumFunc func = getSumFunc(depth); - CV_Assert( cn <= 4 && func != 0 ); const Mat* arrays[] = {&src, 0}; @@ -1262,7 +1254,6 @@ cv::Scalar cv::sum( InputArray _src ) return s; } - #ifdef HAVE_OPENCL namespace cv { @@ -1310,7 +1301,7 @@ static bool ocl_countNonZero( InputArray _src, int & res ) #if defined HAVE_IPP namespace cv { -static bool ipp_countNonZero( Mat src, int & res ) +static bool ipp_countNonZero( Mat &src, int &res ) { #if !defined HAVE_IPP_ICV_ONLY Ipp32s count = 0; @@ -1644,110 +1635,113 @@ static bool ocl_meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv #endif -#if defined (HAVE_IPP) +#ifdef HAVE_IPP namespace cv { static bool ipp_meanStdDev(Mat& src, OutputArray _mean, OutputArray _sdv, Mat& mask) { -#if (IPP_VERSION_MAJOR >= 7) +#if IPP_VERSION_MAJOR >= 7 int cn = src.channels(); size_t total_size = src.total(); - int rows = src.size[0], cols = rows ? (int)(total_size / rows) : 0; - - Ipp64f mean_temp[3]; - Ipp64f stddev_temp[3]; - Ipp64f *pmean = &mean_temp[0]; - Ipp64f *pstddev = &stddev_temp[0]; - Mat mean, stddev; - int dcn_mean = -1; - if (_mean.needed()) + int rows = src.size[0], cols = rows ? (int)(total_size/rows) : 0; + if( src.dims == 2 || (src.isContinuous() && mask.isContinuous() && cols > 0 && (size_t)rows*cols == total_size) ) { - if (!_mean.fixedSize()) - _mean.create(cn, 1, CV_64F, -1, true); - mean = _mean.getMat(); - dcn_mean = (int)mean.total(); - pmean = mean.ptr(); - } - int dcn_stddev = -1; - if (_sdv.needed()) - { - if (!_sdv.fixedSize()) - _sdv.create(cn, 1, CV_64F, -1, true); - stddev = _sdv.getMat(); - dcn_stddev = (int)stddev.total(); - pstddev = stddev.ptr(); - } - for (int c = cn; c < dcn_mean; c++) - pmean[c] = 0; - for (int c = cn; c < dcn_stddev; c++) - pstddev[c] = 0; - IppiSize sz = { cols, rows }; - int type = src.type(); - - if (!mask.empty()) - { - typedef IppStatus(CV_STDCALL* ippiMaskMeanStdDevFuncC1)(const void *, int, const void *, int, IppiSize, Ipp64f *, Ipp64f *); - ippiMaskMeanStdDevFuncC1 ippFuncC1 = - type == CV_8UC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_8u_C1MR : + Ipp64f mean_temp[3]; + Ipp64f stddev_temp[3]; + Ipp64f *pmean = &mean_temp[0]; + Ipp64f *pstddev = &stddev_temp[0]; + Mat mean, stddev; + int dcn_mean = -1; + if( _mean.needed() ) + { + if( !_mean.fixedSize() ) + _mean.create(cn, 1, CV_64F, -1, true); + mean = _mean.getMat(); + dcn_mean = (int)mean.total(); + pmean = mean.ptr(); + } + int dcn_stddev = -1; + if( _sdv.needed() ) + { + if( !_sdv.fixedSize() ) + _sdv.create(cn, 1, CV_64F, -1, true); + stddev = _sdv.getMat(); + dcn_stddev = (int)stddev.total(); + pstddev = stddev.ptr(); + } + for( int c = cn; c < dcn_mean; c++ ) + pmean[c] = 0; + for( int c = cn; c < dcn_stddev; c++ ) + pstddev[c] = 0; + IppiSize sz = { cols, rows }; + int type = src.type(); + if( !mask.empty() ) + { + typedef IppStatus (CV_STDCALL* ippiMaskMeanStdDevFuncC1)(const void *, int, const void *, int, IppiSize, Ipp64f *, Ipp64f *); + ippiMaskMeanStdDevFuncC1 ippFuncC1 = + type == CV_8UC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_8u_C1MR : type == CV_16UC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_16u_C1MR : type == CV_32FC1 ? (ippiMaskMeanStdDevFuncC1)ippiMean_StdDev_32f_C1MR : 0; - if (ippFuncC1) - { - if (ippFuncC1(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, pmean, pstddev) >= 0) + if( ippFuncC1 ) { - return true; + if( ippFuncC1(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, pmean, pstddev) >= 0 ) + { + return true; + } } - } - typedef IppStatus(CV_STDCALL* ippiMaskMeanStdDevFuncC3)(const void *, int, const void *, int, IppiSize, int, Ipp64f *, Ipp64f *); - ippiMaskMeanStdDevFuncC3 ippFuncC3 = - type == CV_8UC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_8u_C3CMR : + typedef IppStatus (CV_STDCALL* ippiMaskMeanStdDevFuncC3)(const void *, int, const void *, int, IppiSize, int, Ipp64f *, Ipp64f *); + ippiMaskMeanStdDevFuncC3 ippFuncC3 = + type == CV_8UC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_8u_C3CMR : type == CV_16UC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_16u_C3CMR : type == CV_32FC3 ? (ippiMaskMeanStdDevFuncC3)ippiMean_StdDev_32f_C3CMR : 0; - if (ippFuncC3) - { - if (ippFuncC3(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 1, &pmean[0], &pstddev[0]) >= 0 && - ippFuncC3(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 2, &pmean[1], &pstddev[1]) >= 0 && - ippFuncC3(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 3, &pmean[2], &pstddev[2]) >= 0) + if( ippFuncC3 ) { - return true; + if( ippFuncC3(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 1, &pmean[0], &pstddev[0]) >= 0 && + ippFuncC3(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 2, &pmean[1], &pstddev[1]) >= 0 && + ippFuncC3(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, 3, &pmean[2], &pstddev[2]) >= 0 ) + { + return true; + } } } - } - else - { - typedef IppStatus(CV_STDCALL* ippiMeanStdDevFuncC1)(const void *, int, IppiSize, Ipp64f *, Ipp64f *); - ippiMeanStdDevFuncC1 ippFuncC1 = - type == CV_8UC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_8u_C1R : + else + { + typedef IppStatus (CV_STDCALL* ippiMeanStdDevFuncC1)(const void *, int, IppiSize, Ipp64f *, Ipp64f *); + ippiMeanStdDevFuncC1 ippFuncC1 = + type == CV_8UC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_8u_C1R : type == CV_16UC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_16u_C1R : #if (IPP_VERSION_X100 >= 801) type == CV_32FC1 ? (ippiMeanStdDevFuncC1)ippiMean_StdDev_32f_C1R ://Aug 2013: bug in IPP 7.1, 8.0 #endif 0; - if (ippFuncC1) - { - if (ippFuncC1(src.ptr(), (int)src.step[0], sz, pmean, pstddev) >= 0) + if( ippFuncC1 ) { - return true; + if( ippFuncC1(src.ptr(), (int)src.step[0], sz, pmean, pstddev) >= 0 ) + { + return true; + } } - } - typedef IppStatus(CV_STDCALL* ippiMeanStdDevFuncC3)(const void *, int, IppiSize, int, Ipp64f *, Ipp64f *); - ippiMeanStdDevFuncC3 ippFuncC3 = - type == CV_8UC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_8u_C3CR : + typedef IppStatus (CV_STDCALL* ippiMeanStdDevFuncC3)(const void *, int, IppiSize, int, Ipp64f *, Ipp64f *); + ippiMeanStdDevFuncC3 ippFuncC3 = + type == CV_8UC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_8u_C3CR : type == CV_16UC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_16u_C3CR : type == CV_32FC3 ? (ippiMeanStdDevFuncC3)ippiMean_StdDev_32f_C3CR : 0; - if (ippFuncC3) - { - if (ippFuncC3(src.ptr(), (int)src.step[0], sz, 1, &pmean[0], &pstddev[0]) >= 0 && - ippFuncC3(src.ptr(), (int)src.step[0], sz, 2, &pmean[1], &pstddev[1]) >= 0 && - ippFuncC3(src.ptr(), (int)src.step[0], sz, 3, &pmean[2], &pstddev[2]) >= 0) + if( ippFuncC3 ) { - return true; + if( ippFuncC3(src.ptr(), (int)src.step[0], sz, 1, &pmean[0], &pstddev[0]) >= 0 && + ippFuncC3(src.ptr(), (int)src.step[0], sz, 2, &pmean[1], &pstddev[1]) >= 0 && + ippFuncC3(src.ptr(), (int)src.step[0], sz, 3, &pmean[2], &pstddev[2]) >= 0 ) + { + return true; + } } } } +#else + CV_UNUSED(src); CV_UNUSED(_mean); CV_UNUSED(_sdv); CV_UNUSED(mask); #endif return false; } @@ -1761,20 +1755,11 @@ void cv::meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv, Input Mat src = _src.getMat(), mask = _mask.getMat(); CV_Assert( mask.empty() || mask.type() == CV_8UC1 ); -#ifdef HAVE_IPP - Size sz = _src.dims() <= 2 ? _src.size() : Size(); - size_t total_size = _src.total(); - int rows = sz.height; - int cols = rows ? (int)(total_size / rows) : 0; -#endif - CV_IPP_RUN(IPP_VERSION_MAJOR >= 7 && (_src.dims() == 2 || (_src.isContinuous() && _mask.isContinuous() && cols > 0 && (size_t)rows*cols == total_size)), - ipp_meanStdDev(src, _mean, _sdv, mask)); + CV_IPP_RUN(IPP_VERSION_MAJOR >= 7, ipp_meanStdDev(src, _mean, _sdv, mask)); int k, cn = src.channels(), depth = src.depth(); - - SumSqrFunc func = getSumSqrTab(depth); CV_Assert( func != 0 ); @@ -1864,7 +1849,6 @@ void cv::meanStdDev( InputArray _src, OutputArray _mean, OutputArray _sdv, Input } } - /****************************************************************************************\ * minMaxLoc * \****************************************************************************************/ @@ -2216,93 +2200,99 @@ static bool ocl_minMaxIdx( InputArray _src, double* minVal, double* maxVal, int* #endif -#if defined (HAVE_IPP) -static bool ipp_minMaxIdx( InputArray _src, double* minVal, double* maxVal, int* minIdx, int* maxIdx, InputArray _mask) +#ifdef HAVE_IPP +static bool ipp_minMaxIdx( Mat &src, double* minVal, double* maxVal, int* minIdx, int* maxIdx, Mat &mask) { -#if (IPP_VERSION_MAJOR >= 7) - int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); - Mat src = _src.getMat(), mask = _mask.getMat(); - +#if IPP_VERSION_MAJOR >= 7 + int type = src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); size_t total_size = src.total(); int rows = src.size[0], cols = rows ? (int)(total_size/rows) : 0; - IppiSize sz = { cols * cn, rows }; - - if( !mask.empty() ) + if( src.dims == 2 || (src.isContinuous() && mask.isContinuous() && cols > 0 && (size_t)rows*cols == total_size) ) { - typedef IppStatus (CV_STDCALL* ippiMaskMinMaxIndxFuncC1)(const void *, int, const void *, int, - IppiSize, Ipp32f *, Ipp32f *, IppiPoint *, IppiPoint *); + IppiSize sz = { cols * cn, rows }; - CV_SUPPRESS_DEPRECATED_START - ippiMaskMinMaxIndxFuncC1 ippFuncC1 = - type == CV_8UC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_8u_C1MR : - type == CV_8SC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_8s_C1MR : - type == CV_16UC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_16u_C1MR : - type == CV_32FC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_32f_C1MR : 0; - CV_SUPPRESS_DEPRECATED_END - - if( ippFuncC1 ) + if( !mask.empty() ) { - Ipp32f min, max; - IppiPoint minp, maxp; - if( ippFuncC1(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, &min, &max, &minp, &maxp) >= 0 ) + typedef IppStatus (CV_STDCALL* ippiMaskMinMaxIndxFuncC1)(const void *, int, const void *, int, + IppiSize, Ipp32f *, Ipp32f *, IppiPoint *, IppiPoint *); + + CV_SUPPRESS_DEPRECATED_START + ippiMaskMinMaxIndxFuncC1 ippFuncC1 = + type == CV_8UC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_8u_C1MR : + type == CV_8SC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_8s_C1MR : + type == CV_16UC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_16u_C1MR : + type == CV_32FC1 ? (ippiMaskMinMaxIndxFuncC1)ippiMinMaxIndx_32f_C1MR : 0; + CV_SUPPRESS_DEPRECATED_END + + if( ippFuncC1 ) { - if( minVal ) - *minVal = (double)min; - if( maxVal ) - *maxVal = (double)max; - if( !minp.x && !minp.y && !maxp.x && !maxp.y && !mask.ptr()[0] ) - minp.x = maxp.x = -1; - if( minIdx ) + Ipp32f min, max; + IppiPoint minp, maxp; + if( ippFuncC1(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, &min, &max, &minp, &maxp) >= 0 ) { - size_t minidx = minp.y * cols + minp.x + 1; - ofs2idx(src, minidx, minIdx); + if( minVal ) + *minVal = (double)min; + if( maxVal ) + *maxVal = (double)max; + if( !minp.x && !minp.y && !maxp.x && !maxp.y && !mask.ptr()[0] ) + minp.x = maxp.x = -1; + if( minIdx ) + { + size_t minidx = minp.y * cols + minp.x + 1; + ofs2idx(src, minidx, minIdx); + } + if( maxIdx ) + { + size_t maxidx = maxp.y * cols + maxp.x + 1; + ofs2idx(src, maxidx, maxIdx); + } + return true; } - if( maxIdx ) - { - size_t maxidx = maxp.y * cols + maxp.x + 1; - ofs2idx(src, maxidx, maxIdx); - } - return true; } } - } - else - { - typedef IppStatus (CV_STDCALL* ippiMinMaxIndxFuncC1)(const void *, int, IppiSize, Ipp32f *, Ipp32f *, IppiPoint *, IppiPoint *); - - CV_SUPPRESS_DEPRECATED_START - ippiMinMaxIndxFuncC1 ippFuncC1 = - depth == CV_8U ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_8u_C1R : - depth == CV_8S ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_8s_C1R : - depth == CV_16U ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_16u_C1R : - depth == CV_32F ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_32f_C1R : 0; - CV_SUPPRESS_DEPRECATED_END - - if( ippFuncC1 ) + else { - Ipp32f min, max; - IppiPoint minp, maxp; - if( ippFuncC1(src.ptr(), (int)src.step[0], sz, &min, &max, &minp, &maxp) >= 0 ) - { - if( minVal ) - *minVal = (double)min; - if( maxVal ) - *maxVal = (double)max; - if( minIdx ) - { - size_t minidx = minp.y * cols + minp.x + 1; - ofs2idx(src, minidx, minIdx); - } - if( maxIdx ) - { - size_t maxidx = maxp.y * cols + maxp.x + 1; - ofs2idx(src, maxidx, maxIdx); - } - return true; - } - } - } + typedef IppStatus (CV_STDCALL* ippiMinMaxIndxFuncC1)(const void *, int, IppiSize, Ipp32f *, Ipp32f *, IppiPoint *, IppiPoint *); + + CV_SUPPRESS_DEPRECATED_START + ippiMinMaxIndxFuncC1 ippFuncC1 = + depth == CV_8U ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_8u_C1R : + depth == CV_8S ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_8s_C1R : + depth == CV_16U ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_16u_C1R : +#if !((defined _MSC_VER && defined _M_IX86) || defined __i386__) + depth == CV_32F ? (ippiMinMaxIndxFuncC1)ippiMinMaxIndx_32f_C1R : #endif + 0; + CV_SUPPRESS_DEPRECATED_END + + if( ippFuncC1 ) + { + Ipp32f min, max; + IppiPoint minp, maxp; + if( ippFuncC1(src.ptr(), (int)src.step[0], sz, &min, &max, &minp, &maxp) >= 0 ) + { + if( minVal ) + *minVal = (double)min; + if( maxVal ) + *maxVal = (double)max; + if( minIdx ) + { + size_t minidx = minp.y * cols + minp.x + 1; + ofs2idx(src, minidx, minIdx); + } + if( maxIdx ) + { + size_t maxidx = maxp.y * cols + maxp.x + 1; + ofs2idx(src, maxidx, maxIdx); + } + return true; + } + } + } + } +#else +#endif + CV_UNUSED(src); CV_UNUSED(minVal); CV_UNUSED(maxVal); CV_UNUSED(minIdx); CV_UNUSED(maxIdx); CV_UNUSED(mask); return false; } #endif @@ -2321,15 +2311,7 @@ void cv::minMaxIdx(InputArray _src, double* minVal, ocl_minMaxIdx(_src, minVal, maxVal, minIdx, maxIdx, _mask)) Mat src = _src.getMat(), mask = _mask.getMat(); -#ifdef HAVE_IPP - Size sz = _src.dims() <= 2 ? _src.size() : Size(); - size_t total_size = _src.total(); - int rows = sz.height; - int cols = rows ? (int)(total_size/rows) : 0; -#endif - CV_IPP_RUN(IPP_VERSION_MAJOR >= 7 && (_src.dims() == 2 || (_src.isContinuous() && _mask.isContinuous() && cols > 0 && (size_t)rows*cols == total_size)), - ipp_minMaxIdx(src, minVal, maxVal, minIdx, maxIdx, mask)) - + CV_IPP_RUN(IPP_VERSION_MAJOR >= 7, ipp_minMaxIdx(src, minVal, maxVal, minIdx, maxIdx, mask)) MinMaxIdxFunc func = getMinmaxTab(depth); CV_Assert( func != 0 ); @@ -2372,7 +2354,6 @@ void cv::minMaxIdx(InputArray _src, double* minVal, ofs2idx(src, maxidx, maxIdx); } - void cv::minMaxLoc( InputArray _img, double* minVal, double* maxVal, Point* minLoc, Point* maxLoc, InputArray mask ) { @@ -2652,180 +2633,174 @@ static bool ocl_norm( InputArray _src, int normType, InputArray _mask, double & #endif -} - -#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - -namespace cv { -static bool ipp_norm(InputArray _src, int normType, InputArray _mask, double & result) +#ifdef HAVE_IPP +static bool ipp_norm(Mat &src, int normType, Mat &mask, double &result) { - Mat src = _src.getMat(), mask = _mask.getMat(); +#if IPP_VERSION_MAJOR >= 7 int cn = src.channels(); size_t total_size = src.total(); - int rows = src.size[0], cols = rows ? (int)(total_size / rows) : 0; - if ((src.dims == 2 || (src.isContinuous() && mask.isContinuous())) + int rows = src.size[0], cols = rows ? (int)(total_size/rows) : 0; + + if( (src.dims == 2 || (src.isContinuous() && mask.isContinuous())) && cols > 0 && (size_t)rows*cols == total_size && (normType == NORM_INF || normType == NORM_L1 || - normType == NORM_L2 || normType == NORM_L2SQR)) + normType == NORM_L2 || normType == NORM_L2SQR) ) { IppiSize sz = { cols, rows }; int type = src.type(); - if (!mask.empty()) + if( !mask.empty() ) + { + typedef IppStatus (CV_STDCALL* ippiMaskNormFuncC1)(const void *, int, const void *, int, IppiSize, Ipp64f *); + ippiMaskNormFuncC1 ippFuncC1 = + normType == NORM_INF ? + (type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_8u_C1MR : + type == CV_8SC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_8s_C1MR : +// type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_32f_C1MR : + 0) : + normType == NORM_L1 ? + (type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_8u_C1MR : + type == CV_8SC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_8s_C1MR : + type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_32f_C1MR : + 0) : + normType == NORM_L2 || normType == NORM_L2SQR ? + (type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_8u_C1MR : + type == CV_8SC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_8s_C1MR : + type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_32f_C1MR : + 0) : 0; + if( ippFuncC1 ) { - typedef IppStatus(CV_STDCALL* ippiMaskNormFuncC1)(const void *, int, const void *, int, IppiSize, Ipp64f *); - ippiMaskNormFuncC1 ippFuncC1 = - normType == NORM_INF ? - (type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_8u_C1MR : - type == CV_8SC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_8s_C1MR : - // type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_32f_C1MR : - 0) : - normType == NORM_L1 ? - (type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_8u_C1MR : - type == CV_8SC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_8s_C1MR : - type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_L1_32f_C1MR : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_8u_C1MR : - type == CV_8SC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_8s_C1MR : - type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_L2_32f_C1MR : - 0) : 0; - if (ippFuncC1) + Ipp64f norm; + if( ippFuncC1(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, &norm) >= 0 ) { - Ipp64f norm; - if (ippFuncC1(src.ptr(), (int)src.step[0], mask.ptr(), (int)mask.step[0], sz, &norm) >= 0) - { - result = normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm; - return true; - } - return false; + result = (normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm); + return true; } - /*typedef IppStatus (CV_STDCALL* ippiMaskNormFuncC3)(const void *, int, const void *, int, IppiSize, int, Ipp64f *); - ippiMaskNormFuncC3 ippFuncC3 = + } + /*typedef IppStatus (CV_STDCALL* ippiMaskNormFuncC3)(const void *, int, const void *, int, IppiSize, int, Ipp64f *); + ippiMaskNormFuncC3 ippFuncC3 = normType == NORM_INF ? (type == CV_8UC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_8u_C3CMR : type == CV_8SC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_8s_C3CMR : type == CV_16UC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_16u_C3CMR : type == CV_32FC3 ? (ippiMaskNormFuncC3)ippiNorm_Inf_32f_C3CMR : 0) : - normType == NORM_L1 ? + normType == NORM_L1 ? (type == CV_8UC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_8u_C3CMR : type == CV_8SC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_8s_C3CMR : type == CV_16UC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_16u_C3CMR : type == CV_32FC3 ? (ippiMaskNormFuncC3)ippiNorm_L1_32f_C3CMR : 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? + normType == NORM_L2 || normType == NORM_L2SQR ? (type == CV_8UC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_8u_C3CMR : type == CV_8SC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_8s_C3CMR : type == CV_16UC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_16u_C3CMR : type == CV_32FC3 ? (ippiMaskNormFuncC3)ippiNorm_L2_32f_C3CMR : 0) : 0; - if( ippFuncC3 ) - { + if( ippFuncC3 ) + { Ipp64f norm1, norm2, norm3; if( ippFuncC3(src.data, (int)src.step[0], mask.data, (int)mask.step[0], sz, 1, &norm1) >= 0 && - ippFuncC3(src.data, (int)src.step[0], mask.data, (int)mask.step[0], sz, 2, &norm2) >= 0 && - ippFuncC3(src.data, (int)src.step[0], mask.data, (int)mask.step[0], sz, 3, &norm3) >= 0) + ippFuncC3(src.data, (int)src.step[0], mask.data, (int)mask.step[0], sz, 2, &norm2) >= 0 && + ippFuncC3(src.data, (int)src.step[0], mask.data, (int)mask.step[0], sz, 3, &norm3) >= 0) { - Ipp64f norm = - normType == NORM_INF ? std::max(std::max(norm1, norm2), norm3) : - normType == NORM_L1 ? norm1 + norm2 + norm3 : - normType == NORM_L2 || normType == NORM_L2SQR ? std::sqrt(norm1 * norm1 + norm2 * norm2 + norm3 * norm3) : - 0; - CV_IMPL_ADD(CV_IMPL_IPP); - return normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm; + Ipp64f norm = + normType == NORM_INF ? std::max(std::max(norm1, norm2), norm3) : + normType == NORM_L1 ? norm1 + norm2 + norm3 : + normType == NORM_L2 || normType == NORM_L2SQR ? std::sqrt(norm1 * norm1 + norm2 * norm2 + norm3 * norm3) : + 0; + result = (normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm); + return true; } - setIppErrorStatus(); - }*/ - } - else - { - typedef IppStatus(CV_STDCALL* ippiNormFuncHint)(const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); - typedef IppStatus(CV_STDCALL* ippiNormFuncNoHint)(const void *, int, IppiSize, Ipp64f *); - ippiNormFuncHint ippFuncHint = - normType == NORM_L1 ? - (type == CV_32FC1 ? (ippiNormFuncHint)ippiNorm_L1_32f_C1R : - type == CV_32FC3 ? (ippiNormFuncHint)ippiNorm_L1_32f_C3R : - type == CV_32FC4 ? (ippiNormFuncHint)ippiNorm_L1_32f_C4R : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_32FC1 ? (ippiNormFuncHint)ippiNorm_L2_32f_C1R : - type == CV_32FC3 ? (ippiNormFuncHint)ippiNorm_L2_32f_C3R : - type == CV_32FC4 ? (ippiNormFuncHint)ippiNorm_L2_32f_C4R : - 0) : 0; - ippiNormFuncNoHint ippFuncNoHint = - normType == NORM_INF ? - (type == CV_8UC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_8u_C1R : - type == CV_8UC3 ? (ippiNormFuncNoHint)ippiNorm_Inf_8u_C3R : - type == CV_8UC4 ? (ippiNormFuncNoHint)ippiNorm_Inf_8u_C4R : - type == CV_16UC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_16u_C1R : - type == CV_16UC3 ? (ippiNormFuncNoHint)ippiNorm_Inf_16u_C3R : - type == CV_16UC4 ? (ippiNormFuncNoHint)ippiNorm_Inf_16u_C4R : - type == CV_16SC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_16s_C1R : + }*/ + } + else + { + typedef IppStatus (CV_STDCALL* ippiNormFuncHint)(const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); + typedef IppStatus (CV_STDCALL* ippiNormFuncNoHint)(const void *, int, IppiSize, Ipp64f *); + ippiNormFuncHint ippFuncHint = + normType == NORM_L1 ? + (type == CV_32FC1 ? (ippiNormFuncHint)ippiNorm_L1_32f_C1R : + type == CV_32FC3 ? (ippiNormFuncHint)ippiNorm_L1_32f_C3R : + type == CV_32FC4 ? (ippiNormFuncHint)ippiNorm_L1_32f_C4R : + 0) : + normType == NORM_L2 || normType == NORM_L2SQR ? + (type == CV_32FC1 ? (ippiNormFuncHint)ippiNorm_L2_32f_C1R : + type == CV_32FC3 ? (ippiNormFuncHint)ippiNorm_L2_32f_C3R : + type == CV_32FC4 ? (ippiNormFuncHint)ippiNorm_L2_32f_C4R : + 0) : 0; + ippiNormFuncNoHint ippFuncNoHint = + normType == NORM_INF ? + (type == CV_8UC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_8u_C1R : + type == CV_8UC3 ? (ippiNormFuncNoHint)ippiNorm_Inf_8u_C3R : + type == CV_8UC4 ? (ippiNormFuncNoHint)ippiNorm_Inf_8u_C4R : + type == CV_16UC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_16u_C1R : + type == CV_16UC3 ? (ippiNormFuncNoHint)ippiNorm_Inf_16u_C3R : + type == CV_16UC4 ? (ippiNormFuncNoHint)ippiNorm_Inf_16u_C4R : + type == CV_16SC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_16s_C1R : #if (IPP_VERSION_X100 >= 801) - type == CV_16SC3 ? (ippiNormFuncNoHint)ippiNorm_Inf_16s_C3R : //Aug 2013: problem in IPP 7.1, 8.0 : -32768 - type == CV_16SC4 ? (ippiNormFuncNoHint)ippiNorm_Inf_16s_C4R : //Aug 2013: problem in IPP 7.1, 8.0 : -32768 + type == CV_16SC3 ? (ippiNormFuncNoHint)ippiNorm_Inf_16s_C3R : //Aug 2013: problem in IPP 7.1, 8.0 : -32768 + type == CV_16SC4 ? (ippiNormFuncNoHint)ippiNorm_Inf_16s_C4R : //Aug 2013: problem in IPP 7.1, 8.0 : -32768 #endif - type == CV_32FC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_32f_C1R : - type == CV_32FC3 ? (ippiNormFuncNoHint)ippiNorm_Inf_32f_C3R : - type == CV_32FC4 ? (ippiNormFuncNoHint)ippiNorm_Inf_32f_C4R : - 0) : - normType == NORM_L1 ? - (type == CV_8UC1 ? (ippiNormFuncNoHint)ippiNorm_L1_8u_C1R : - type == CV_8UC3 ? (ippiNormFuncNoHint)ippiNorm_L1_8u_C3R : - type == CV_8UC4 ? (ippiNormFuncNoHint)ippiNorm_L1_8u_C4R : - type == CV_16UC1 ? (ippiNormFuncNoHint)ippiNorm_L1_16u_C1R : - type == CV_16UC3 ? (ippiNormFuncNoHint)ippiNorm_L1_16u_C3R : - type == CV_16UC4 ? (ippiNormFuncNoHint)ippiNorm_L1_16u_C4R : - type == CV_16SC1 ? (ippiNormFuncNoHint)ippiNorm_L1_16s_C1R : - type == CV_16SC3 ? (ippiNormFuncNoHint)ippiNorm_L1_16s_C3R : - type == CV_16SC4 ? (ippiNormFuncNoHint)ippiNorm_L1_16s_C4R : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_8UC1 ? (ippiNormFuncNoHint)ippiNorm_L2_8u_C1R : - type == CV_8UC3 ? (ippiNormFuncNoHint)ippiNorm_L2_8u_C3R : - type == CV_8UC4 ? (ippiNormFuncNoHint)ippiNorm_L2_8u_C4R : - type == CV_16UC1 ? (ippiNormFuncNoHint)ippiNorm_L2_16u_C1R : - type == CV_16UC3 ? (ippiNormFuncNoHint)ippiNorm_L2_16u_C3R : - type == CV_16UC4 ? (ippiNormFuncNoHint)ippiNorm_L2_16u_C4R : - type == CV_16SC1 ? (ippiNormFuncNoHint)ippiNorm_L2_16s_C1R : - type == CV_16SC3 ? (ippiNormFuncNoHint)ippiNorm_L2_16s_C3R : - type == CV_16SC4 ? (ippiNormFuncNoHint)ippiNorm_L2_16s_C4R : - 0) : 0; - // Make sure only zero or one version of the function pointer is valid - CV_Assert(!ippFuncHint || !ippFuncNoHint); - if (ippFuncHint || ippFuncNoHint) + type == CV_32FC1 ? (ippiNormFuncNoHint)ippiNorm_Inf_32f_C1R : + type == CV_32FC3 ? (ippiNormFuncNoHint)ippiNorm_Inf_32f_C3R : + type == CV_32FC4 ? (ippiNormFuncNoHint)ippiNorm_Inf_32f_C4R : + 0) : + normType == NORM_L1 ? + (type == CV_8UC1 ? (ippiNormFuncNoHint)ippiNorm_L1_8u_C1R : + type == CV_8UC3 ? (ippiNormFuncNoHint)ippiNorm_L1_8u_C3R : + type == CV_8UC4 ? (ippiNormFuncNoHint)ippiNorm_L1_8u_C4R : + type == CV_16UC1 ? (ippiNormFuncNoHint)ippiNorm_L1_16u_C1R : + type == CV_16UC3 ? (ippiNormFuncNoHint)ippiNorm_L1_16u_C3R : + type == CV_16UC4 ? (ippiNormFuncNoHint)ippiNorm_L1_16u_C4R : + type == CV_16SC1 ? (ippiNormFuncNoHint)ippiNorm_L1_16s_C1R : + type == CV_16SC3 ? (ippiNormFuncNoHint)ippiNorm_L1_16s_C3R : + type == CV_16SC4 ? (ippiNormFuncNoHint)ippiNorm_L1_16s_C4R : + 0) : + normType == NORM_L2 || normType == NORM_L2SQR ? + (type == CV_8UC1 ? (ippiNormFuncNoHint)ippiNorm_L2_8u_C1R : + type == CV_8UC3 ? (ippiNormFuncNoHint)ippiNorm_L2_8u_C3R : + type == CV_8UC4 ? (ippiNormFuncNoHint)ippiNorm_L2_8u_C4R : + type == CV_16UC1 ? (ippiNormFuncNoHint)ippiNorm_L2_16u_C1R : + type == CV_16UC3 ? (ippiNormFuncNoHint)ippiNorm_L2_16u_C3R : + type == CV_16UC4 ? (ippiNormFuncNoHint)ippiNorm_L2_16u_C4R : + type == CV_16SC1 ? (ippiNormFuncNoHint)ippiNorm_L2_16s_C1R : + type == CV_16SC3 ? (ippiNormFuncNoHint)ippiNorm_L2_16s_C3R : + type == CV_16SC4 ? (ippiNormFuncNoHint)ippiNorm_L2_16s_C4R : + 0) : 0; + // Make sure only zero or one version of the function pointer is valid + CV_Assert(!ippFuncHint || !ippFuncNoHint); + if( ippFuncHint || ippFuncNoHint ) + { + Ipp64f norm_array[4]; + IppStatus ret = ippFuncHint ? ippFuncHint(src.ptr(), (int)src.step[0], sz, norm_array, ippAlgHintAccurate) : + ippFuncNoHint(src.ptr(), (int)src.step[0], sz, norm_array); + if( ret >= 0 ) { - Ipp64f norm_array[4]; - IppStatus ret = ippFuncHint ? ippFuncHint(src.ptr(), (int)src.step[0], sz, norm_array, ippAlgHintAccurate) : - ippFuncNoHint(src.ptr(), (int)src.step[0], sz, norm_array); - if (ret >= 0) + Ipp64f norm = (normType == NORM_L2 || normType == NORM_L2SQR) ? norm_array[0] * norm_array[0] : norm_array[0]; + for( int i = 1; i < cn; i++ ) { - Ipp64f norm = (normType == NORM_L2 || normType == NORM_L2SQR) ? norm_array[0] * norm_array[0] : norm_array[0]; - CV_Assert(cn <= 4); - cn = min(cn, 4); - for (int i = 1; i < cn; i++) - { - norm = - normType == NORM_INF ? std::max(norm, norm_array[i]) : - normType == NORM_L1 ? norm + norm_array[i] : - normType == NORM_L2 || normType == NORM_L2SQR ? norm + norm_array[i] * norm_array[i] : - 0; - } - result = normType == NORM_L2 ? (double)std::sqrt(norm) : (double)norm; - return true; + norm = + normType == NORM_INF ? std::max(norm, norm_array[i]) : + normType == NORM_L1 ? norm + norm_array[i] : + normType == NORM_L2 || normType == NORM_L2SQR ? norm + norm_array[i] * norm_array[i] : + 0; } - return false; + result = (normType == NORM_L2 ? (double)std::sqrt(norm) : (double)norm); + return true; } } } - return false; } +#else + CV_UNUSED(src); CV_UNUSED(normType); CV_UNUSED(mask); CV_UNUSED(result); +#endif + return false; } #endif - +} double cv::norm( InputArray _src, int normType, InputArray _mask ) { @@ -2845,9 +2820,9 @@ double cv::norm( InputArray _src, int normType, InputArray _mask ) #endif Mat src = _src.getMat(), mask = _mask.getMat(); - int depth = src.depth(), cn = src.channels(); - CV_IPP_RUN(true, ipp_norm(_src, normType, _mask, _result), _result); + CV_IPP_RUN(IPP_VERSION_MAJOR >= 7, ipp_norm(src, normType, mask, _result), _result); + int depth = src.depth(), cn = src.channels(); if( src.isContinuous() && mask.empty() ) { size_t len = src.total()*cn; @@ -3044,28 +3019,31 @@ static bool ocl_norm( InputArray _src1, InputArray _src2, int normType, InputArr #endif +#ifdef HAVE_IPP namespace cv { -#if defined (HAVE_IPP) && (IPP_VERSION_MAJOR >= 7) - static bool ipp_norm_rel(InputArray _src1, InputArray _src2, int normType, InputArray _mask, double & result) - { - Mat src1 = _src1.getMat(), src2 = _src2.getMat(), mask = _mask.getMat(); +static bool ipp_norm(InputArray _src1, InputArray _src2, int normType, InputArray _mask, double &result) +{ +#if IPP_VERSION_MAJOR >= 7 + Mat src1 = _src1.getMat(), src2 = _src2.getMat(), mask = _mask.getMat(); + if( normType & CV_RELATIVE ) + { normType &= NORM_TYPE_MASK; - CV_Assert(normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 || normType == NORM_L2SQR || - ((normType == NORM_HAMMING || normType == NORM_HAMMING2) && src1.type() == CV_8U)); + CV_Assert( normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 || normType == NORM_L2SQR || + ((normType == NORM_HAMMING || normType == NORM_HAMMING2) && src1.type() == CV_8U) ); size_t total_size = src1.total(); - int rows = src1.size[0], cols = rows ? (int)(total_size / rows) : 0; - if ((src1.dims == 2 || (src1.isContinuous() && src2.isContinuous() && mask.isContinuous())) + int rows = src1.size[0], cols = rows ? (int)(total_size/rows) : 0; + if( (src1.dims == 2 || (src1.isContinuous() && src2.isContinuous() && mask.isContinuous())) && cols > 0 && (size_t)rows*cols == total_size && (normType == NORM_INF || normType == NORM_L1 || - normType == NORM_L2 || normType == NORM_L2SQR)) + normType == NORM_L2 || normType == NORM_L2SQR) ) { IppiSize sz = { cols, rows }; int type = src1.type(); - if (!mask.empty()) + if( !mask.empty() ) { - typedef IppStatus(CV_STDCALL* ippiMaskNormRelFuncC1)(const void *, int, const void *, int, const void *, int, IppiSize, Ipp64f *); + typedef IppStatus (CV_STDCALL* ippiMaskNormRelFuncC1)(const void *, int, const void *, int, const void *, int, IppiSize, Ipp64f *); ippiMaskNormRelFuncC1 ippFuncC1 = normType == NORM_INF ? (type == CV_8UC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_Inf_8u_C1MR : @@ -3089,21 +3067,20 @@ namespace cv type == CV_16UC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L2_16u_C1MR : type == CV_32FC1 ? (ippiMaskNormRelFuncC1)ippiNormRel_L2_32f_C1MR : 0) : 0; - if (ippFuncC1) + if( ippFuncC1 ) { Ipp64f norm; - if (ippFuncC1(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], mask.ptr(), (int)mask.step[0], sz, &norm) >= 0) + if( ippFuncC1(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], mask.ptr(), (int)mask.step[0], sz, &norm) >= 0 ) { - result = normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm; + result = (normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm); return true; } - return false; } } else { - typedef IppStatus(CV_STDCALL* ippiNormRelFuncNoHint)(const void *, int, const void *, int, IppiSize, Ipp64f *); - typedef IppStatus(CV_STDCALL* ippiNormRelFuncHint)(const void *, int, const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); + typedef IppStatus (CV_STDCALL* ippiNormRelFuncNoHint)(const void *, int, const void *, int, IppiSize, Ipp64f *); + typedef IppStatus (CV_STDCALL* ippiNormRelFuncHint)(const void *, int, const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); ippiNormRelFuncNoHint ippFuncNoHint = normType == NORM_INF ? (type == CV_8UC1 ? (ippiNormRelFuncNoHint)ippiNormRel_Inf_8u_C1R : @@ -3131,200 +3108,199 @@ namespace cv if (ippFuncNoHint) { Ipp64f norm; - if (ippFuncNoHint(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, &norm) >= 0) + if( ippFuncNoHint(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, &norm) >= 0 ) { result = (double)norm; return true; } - return false; } if (ippFuncHint) { Ipp64f norm; - if (ippFuncHint(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, &norm, ippAlgHintAccurate) >= 0) + if( ippFuncHint(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, &norm, ippAlgHintAccurate) >= 0 ) { result = (double)norm; return true; } - return false; } } } return false; } - static bool ipp_norm_dif(InputArray _src1, InputArray _src2, int normType, InputArray _mask, double & result) + + normType &= 7; + CV_Assert( normType == NORM_INF || normType == NORM_L1 || + normType == NORM_L2 || normType == NORM_L2SQR || + ((normType == NORM_HAMMING || normType == NORM_HAMMING2) && src1.type() == CV_8U) ); + + size_t total_size = src1.total(); + int rows = src1.size[0], cols = rows ? (int)(total_size/rows) : 0; + if( (src1.dims == 2 || (src1.isContinuous() && src2.isContinuous() && mask.isContinuous())) + && cols > 0 && (size_t)rows*cols == total_size + && (normType == NORM_INF || normType == NORM_L1 || + normType == NORM_L2 || normType == NORM_L2SQR) ) { - Mat src1 = _src1.getMat(), src2 = _src2.getMat(), mask = _mask.getMat(); - size_t total_size = src1.total(); - int rows = src1.size[0], cols = rows ? (int)(total_size / rows) : 0; - if ((src1.dims == 2 || (src1.isContinuous() && src2.isContinuous() && mask.isContinuous())) - && cols > 0 && (size_t)rows*cols == total_size - && (normType == NORM_INF || normType == NORM_L1 || - normType == NORM_L2 || normType == NORM_L2SQR)) + IppiSize sz = { cols, rows }; + int type = src1.type(); + if( !mask.empty() ) { - IppiSize sz = { cols, rows }; - int type = src1.type(); - if (!mask.empty()) + typedef IppStatus (CV_STDCALL* ippiMaskNormDiffFuncC1)(const void *, int, const void *, int, const void *, int, IppiSize, Ipp64f *); + ippiMaskNormDiffFuncC1 ippFuncC1 = + normType == NORM_INF ? + (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_8u_C1MR : + type == CV_8SC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_8s_C1MR : + type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_32f_C1MR : + 0) : + normType == NORM_L1 ? + (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L1_8u_C1MR : +#ifndef __APPLE__ + type == CV_8SC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L1_8s_C1MR : +#endif + type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L1_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L1_32f_C1MR : + 0) : + normType == NORM_L2 || normType == NORM_L2SQR ? + (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_8u_C1MR : + type == CV_8SC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_8s_C1MR : + type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_16u_C1MR : + type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_32f_C1MR : + 0) : 0; + if( ippFuncC1 ) { - typedef IppStatus(CV_STDCALL* ippiMaskNormDiffFuncC1)(const void *, int, const void *, int, const void *, int, IppiSize, Ipp64f *); - ippiMaskNormDiffFuncC1 ippFuncC1 = - normType == NORM_INF ? - (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_8u_C1MR : - type == CV_8SC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_8s_C1MR : - type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_32f_C1MR : - 0) : - normType == NORM_L1 ? - (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L1_8u_C1MR : -#ifndef __APPLE__ - type == CV_8SC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L1_8s_C1MR : -#endif - type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L1_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L1_32f_C1MR : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_8u_C1MR : - type == CV_8SC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_8s_C1MR : - type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_16u_C1MR : - type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_L2_32f_C1MR : - 0) : 0; - if (ippFuncC1) + Ipp64f norm; + if( ippFuncC1(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], mask.ptr(), (int)mask.step[0], sz, &norm) >= 0 ) { - Ipp64f norm; - if (ippFuncC1(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], mask.ptr(), (int)mask.step[0], sz, &norm) >= 0) - { - result = normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm; - return true; - } - return false; + result = (normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm); + return true; } -#ifndef __APPLE__ - typedef IppStatus(CV_STDCALL* ippiMaskNormDiffFuncC3)(const void *, int, const void *, int, const void *, int, IppiSize, int, Ipp64f *); - ippiMaskNormDiffFuncC3 ippFuncC3 = - normType == NORM_INF ? - (type == CV_8UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_Inf_8u_C3CMR : - type == CV_8SC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_Inf_8s_C3CMR : - type == CV_16UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_Inf_16u_C3CMR : - type == CV_32FC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_Inf_32f_C3CMR : - 0) : - normType == NORM_L1 ? - (type == CV_8UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L1_8u_C3CMR : - type == CV_8SC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L1_8s_C3CMR : - type == CV_16UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L1_16u_C3CMR : - type == CV_32FC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L1_32f_C3CMR : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_8UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_8u_C3CMR : - type == CV_8SC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_8s_C3CMR : - type == CV_16UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_16u_C3CMR : - type == CV_32FC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_32f_C3CMR : - 0) : 0; - if (ippFuncC3) - { - Ipp64f norm1, norm2, norm3; - if (ippFuncC3(src1.data, (int)src1.step[0], src2.data, (int)src2.step[0], mask.data, (int)mask.step[0], sz, 1, &norm1) >= 0 && - ippFuncC3(src1.data, (int)src1.step[0], src2.data, (int)src2.step[0], mask.data, (int)mask.step[0], sz, 2, &norm2) >= 0 && - ippFuncC3(src1.data, (int)src1.step[0], src2.data, (int)src2.step[0], mask.data, (int)mask.step[0], sz, 3, &norm3) >= 0) - { - Ipp64f norm = - normType == NORM_INF ? std::max(std::max(norm1, norm2), norm3) : - normType == NORM_L1 ? norm1 + norm2 + norm3 : - normType == NORM_L2 || normType == NORM_L2SQR ? std::sqrt(norm1 * norm1 + norm2 * norm2 + norm3 * norm3) : - 0; - result = normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm; - return true; - } - return false; - } -#endif } - else +#ifndef __APPLE__ + typedef IppStatus (CV_STDCALL* ippiMaskNormDiffFuncC3)(const void *, int, const void *, int, const void *, int, IppiSize, int, Ipp64f *); + ippiMaskNormDiffFuncC3 ippFuncC3 = + normType == NORM_INF ? + (type == CV_8UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_Inf_8u_C3CMR : + type == CV_8SC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_Inf_8s_C3CMR : + type == CV_16UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_Inf_16u_C3CMR : + type == CV_32FC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_Inf_32f_C3CMR : + 0) : + normType == NORM_L1 ? + (type == CV_8UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L1_8u_C3CMR : + type == CV_8SC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L1_8s_C3CMR : + type == CV_16UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L1_16u_C3CMR : + type == CV_32FC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L1_32f_C3CMR : + 0) : + normType == NORM_L2 || normType == NORM_L2SQR ? + (type == CV_8UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_8u_C3CMR : + type == CV_8SC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_8s_C3CMR : + type == CV_16UC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_16u_C3CMR : + type == CV_32FC3 ? (ippiMaskNormDiffFuncC3)ippiNormDiff_L2_32f_C3CMR : + 0) : 0; + if( ippFuncC3 ) { - typedef IppStatus(CV_STDCALL* ippiNormDiffFuncHint)(const void *, int, const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); - typedef IppStatus(CV_STDCALL* ippiNormDiffFuncNoHint)(const void *, int, const void *, int, IppiSize, Ipp64f *); - ippiNormDiffFuncHint ippFuncHint = - normType == NORM_L1 ? - (type == CV_32FC1 ? (ippiNormDiffFuncHint)ippiNormDiff_L1_32f_C1R : - type == CV_32FC3 ? (ippiNormDiffFuncHint)ippiNormDiff_L1_32f_C3R : - type == CV_32FC4 ? (ippiNormDiffFuncHint)ippiNormDiff_L1_32f_C4R : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_32FC1 ? (ippiNormDiffFuncHint)ippiNormDiff_L2_32f_C1R : - type == CV_32FC3 ? (ippiNormDiffFuncHint)ippiNormDiff_L2_32f_C3R : - type == CV_32FC4 ? (ippiNormDiffFuncHint)ippiNormDiff_L2_32f_C4R : - 0) : 0; - ippiNormDiffFuncNoHint ippFuncNoHint = - normType == NORM_INF ? - (type == CV_8UC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_8u_C1R : - type == CV_8UC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_8u_C3R : - type == CV_8UC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_8u_C4R : - type == CV_16UC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16u_C1R : - type == CV_16UC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16u_C3R : - type == CV_16UC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16u_C4R : - type == CV_16SC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16s_C1R : -#if (IPP_VERSION_X100 >= 801) - type == CV_16SC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16s_C3R : //Aug 2013: problem in IPP 7.1, 8.0 : -32768 - type == CV_16SC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16s_C4R : //Aug 2013: problem in IPP 7.1, 8.0 : -32768 -#endif - type == CV_32FC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_32f_C1R : - type == CV_32FC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_32f_C3R : - type == CV_32FC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_32f_C4R : - 0) : - normType == NORM_L1 ? - (type == CV_8UC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_8u_C1R : - type == CV_8UC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_8u_C3R : - type == CV_8UC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_8u_C4R : - type == CV_16UC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16u_C1R : - type == CV_16UC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16u_C3R : - type == CV_16UC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16u_C4R : -#if !(IPP_VERSION_X100 == 802 && (!defined(IPP_VERSION_UPDATE) || IPP_VERSION_UPDATE <= 1)) // Oct 2014: Accuracy issue with IPP 8.2 / 8.2.1 - type == CV_16SC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16s_C1R : -#endif - type == CV_16SC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16s_C3R : - type == CV_16SC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16s_C4R : - 0) : - normType == NORM_L2 || normType == NORM_L2SQR ? - (type == CV_8UC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_8u_C1R : - type == CV_8UC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_8u_C3R : - type == CV_8UC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_8u_C4R : - type == CV_16UC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16u_C1R : - type == CV_16UC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16u_C3R : - type == CV_16UC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16u_C4R : - type == CV_16SC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16s_C1R : - type == CV_16SC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16s_C3R : - type == CV_16SC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16s_C4R : - 0) : 0; - // Make sure only zero or one version of the function pointer is valid - CV_Assert(!ippFuncHint || !ippFuncNoHint); - if (ippFuncHint || ippFuncNoHint) + Ipp64f norm1, norm2, norm3; + if( ippFuncC3(src1.data, (int)src1.step[0], src2.data, (int)src2.step[0], mask.data, (int)mask.step[0], sz, 1, &norm1) >= 0 && + ippFuncC3(src1.data, (int)src1.step[0], src2.data, (int)src2.step[0], mask.data, (int)mask.step[0], sz, 2, &norm2) >= 0 && + ippFuncC3(src1.data, (int)src1.step[0], src2.data, (int)src2.step[0], mask.data, (int)mask.step[0], sz, 3, &norm3) >= 0) { - Ipp64f norm_array[4]; - IppStatus ret = ippFuncHint ? ippFuncHint(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, norm_array, ippAlgHintAccurate) : - ippFuncNoHint(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, norm_array); - if (ret >= 0) + Ipp64f norm = + normType == NORM_INF ? std::max(std::max(norm1, norm2), norm3) : + normType == NORM_L1 ? norm1 + norm2 + norm3 : + normType == NORM_L2 || normType == NORM_L2SQR ? std::sqrt(norm1 * norm1 + norm2 * norm2 + norm3 * norm3) : + 0; + result = (normType == NORM_L2SQR ? (double)(norm * norm) : (double)norm); + return true; + } + } +#endif + } + else + { + typedef IppStatus (CV_STDCALL* ippiNormDiffFuncHint)(const void *, int, const void *, int, IppiSize, Ipp64f *, IppHintAlgorithm hint); + typedef IppStatus (CV_STDCALL* ippiNormDiffFuncNoHint)(const void *, int, const void *, int, IppiSize, Ipp64f *); + ippiNormDiffFuncHint ippFuncHint = + normType == NORM_L1 ? + (type == CV_32FC1 ? (ippiNormDiffFuncHint)ippiNormDiff_L1_32f_C1R : + type == CV_32FC3 ? (ippiNormDiffFuncHint)ippiNormDiff_L1_32f_C3R : + type == CV_32FC4 ? (ippiNormDiffFuncHint)ippiNormDiff_L1_32f_C4R : + 0) : + normType == NORM_L2 || normType == NORM_L2SQR ? + (type == CV_32FC1 ? (ippiNormDiffFuncHint)ippiNormDiff_L2_32f_C1R : + type == CV_32FC3 ? (ippiNormDiffFuncHint)ippiNormDiff_L2_32f_C3R : + type == CV_32FC4 ? (ippiNormDiffFuncHint)ippiNormDiff_L2_32f_C4R : + 0) : 0; + ippiNormDiffFuncNoHint ippFuncNoHint = + normType == NORM_INF ? + (type == CV_8UC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_8u_C1R : + type == CV_8UC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_8u_C3R : + type == CV_8UC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_8u_C4R : + type == CV_16UC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16u_C1R : + type == CV_16UC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16u_C3R : + type == CV_16UC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16u_C4R : + type == CV_16SC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16s_C1R : +#if (IPP_VERSION_X100 >= 801) + type == CV_16SC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16s_C3R : //Aug 2013: problem in IPP 7.1, 8.0 : -32768 + type == CV_16SC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_16s_C4R : //Aug 2013: problem in IPP 7.1, 8.0 : -32768 +#endif + type == CV_32FC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_32f_C1R : + type == CV_32FC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_32f_C3R : + type == CV_32FC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_Inf_32f_C4R : + 0) : + normType == NORM_L1 ? + (type == CV_8UC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_8u_C1R : + type == CV_8UC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_8u_C3R : + type == CV_8UC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_8u_C4R : + type == CV_16UC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16u_C1R : + type == CV_16UC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16u_C3R : + type == CV_16UC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16u_C4R : +#if !(IPP_VERSION_X100 == 802 && (!defined(IPP_VERSION_UPDATE) || IPP_VERSION_UPDATE <= 1)) // Oct 2014: Accuracy issue with IPP 8.2 / 8.2.1 + type == CV_16SC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16s_C1R : +#endif + type == CV_16SC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16s_C3R : + type == CV_16SC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L1_16s_C4R : + 0) : + normType == NORM_L2 || normType == NORM_L2SQR ? + (type == CV_8UC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_8u_C1R : + type == CV_8UC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_8u_C3R : + type == CV_8UC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_8u_C4R : + type == CV_16UC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16u_C1R : + type == CV_16UC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16u_C3R : + type == CV_16UC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16u_C4R : + type == CV_16SC1 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16s_C1R : + type == CV_16SC3 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16s_C3R : + type == CV_16SC4 ? (ippiNormDiffFuncNoHint)ippiNormDiff_L2_16s_C4R : + 0) : 0; + // Make sure only zero or one version of the function pointer is valid + CV_Assert(!ippFuncHint || !ippFuncNoHint); + if( ippFuncHint || ippFuncNoHint ) + { + Ipp64f norm_array[4]; + IppStatus ret = ippFuncHint ? ippFuncHint(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, norm_array, ippAlgHintAccurate) : + ippFuncNoHint(src1.ptr(), (int)src1.step[0], src2.ptr(), (int)src2.step[0], sz, norm_array); + if( ret >= 0 ) + { + Ipp64f norm = (normType == NORM_L2 || normType == NORM_L2SQR) ? norm_array[0] * norm_array[0] : norm_array[0]; + for( int i = 1; i < src1.channels(); i++ ) { - Ipp64f norm = (normType == NORM_L2 || normType == NORM_L2SQR) ? norm_array[0] * norm_array[0] : norm_array[0]; - CV_Assert(src1.channels() <= 4); - int cn = min(src1.channels(), 4); - for (int i = 1; i < cn; i++) - { - norm = - normType == NORM_INF ? std::max(norm, norm_array[i]) : - normType == NORM_L1 ? norm + norm_array[i] : - normType == NORM_L2 || normType == NORM_L2SQR ? norm + norm_array[i] * norm_array[i] : - 0; - } - result = normType == NORM_L2 ? (double)std::sqrt(norm) : (double)norm; - return true; + norm = + normType == NORM_INF ? std::max(norm, norm_array[i]) : + normType == NORM_L1 ? norm + norm_array[i] : + normType == NORM_L2 || normType == NORM_L2SQR ? norm + norm_array[i] * norm_array[i] : + 0; } - return false; + result = (normType == NORM_L2 ? (double)std::sqrt(norm) : (double)norm); + return true; } } } - return false; } +#else + CV_UNUSED(_src1); CV_UNUSED(_src2); CV_UNUSED(normType); CV_UNUSED(_mask); CV_UNUSED(result); #endif + return false; } +} +#endif double cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _mask ) @@ -3341,9 +3317,10 @@ double cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _m _result) #endif + CV_IPP_RUN(IPP_VERSION_MAJOR >= 7, ipp_norm(_src1, _src2, normType, _mask, _result), _result); + if( normType & CV_RELATIVE ) { - CV_IPP_RUN(true, ipp_norm_rel(_src1, _src2, normType, _mask, _result), _result); return norm(_src1, _src2, normType & ~CV_RELATIVE, _mask)/(norm(_src2, normType, _mask) + DBL_EPSILON); } @@ -3354,7 +3331,6 @@ double cv::norm( InputArray _src1, InputArray _src2, int normType, InputArray _m CV_Assert( normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 || normType == NORM_L2SQR || ((normType == NORM_HAMMING || normType == NORM_HAMMING2) && src1.type() == CV_8U) ); - CV_IPP_RUN(true, ipp_norm_dif(_src1, _src2, normType, _mask, _result), _result); if( src1.isContinuous() && src2.isContinuous() && mask.empty() ) { diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index a78043843f..433e38f704 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -3120,6 +3120,7 @@ static bool ipp_resize_mt( Mat src, Mat dst, ////////////////////////////////////////////////////////////////////////////////////////// + void cv::resize( InputArray _src, OutputArray _dst, Size dsize, double inv_scale_x, double inv_scale_y, int interpolation ) { @@ -3482,7 +3483,6 @@ void cv::resize( InputArray _src, OutputArray _dst, Size dsize, } - /****************************************************************************************\ * General warping (affine, perspective, remap) * \****************************************************************************************/ diff --git a/modules/imgproc/src/morph.cpp b/modules/imgproc/src/morph.cpp index f40ce1c40b..822df8e47d 100644 --- a/modules/imgproc/src/morph.cpp +++ b/modules/imgproc/src/morph.cpp @@ -1136,10 +1136,11 @@ private: Scalar borderValue; }; -#if IPP_VERSION_X100 >= 801 -static bool IPPMorphReplicate(int op, const Mat &src, Mat &dst, const Mat &kernel, +#ifdef HAVE_IPP +static bool ipp_MorphReplicate(int op, const Mat &src, Mat &dst, const Mat &kernel, const Size& ksize, const Point &anchor, bool rectKernel) { +#if IPP_VERSION_X100 >= 801 int type = src.type(); const Mat* _src = &src; Mat temp; @@ -1257,10 +1258,13 @@ static bool IPPMorphReplicate(int op, const Mat &src, Mat &dst, const Mat &kerne } #undef IPP_MORPH_CASE } +#else + CV_UNUSED(op); CV_UNUSED(src); CV_UNUSED(dst); CV_UNUSED(kernel); CV_UNUSED(ksize); CV_UNUSED(anchor); CV_UNUSED(rectKernel); +#endif return false; } -static bool IPPMorphOp(int op, InputArray _src, OutputArray _dst, +static bool ipp_MorphOp(int op, InputArray _src, OutputArray _dst, const Mat& _kernel, Point anchor, int iterations, int borderType, const Scalar &borderValue) { @@ -1331,7 +1335,7 @@ static bool IPPMorphOp(int op, InputArray _src, OutputArray _dst, if( iterations > 1 ) return false; - return IPPMorphReplicate( op, src, dst, kernel, ksize, anchor, rectKernel ); + return ipp_MorphReplicate( op, src, dst, kernel, ksize, anchor, rectKernel ); } #endif @@ -1711,9 +1715,7 @@ static void morphOp( int op, InputArray _src, OutputArray _dst, iterations = 1; } - - CV_IPP_RUN(IPP_VERSION_X100 >= 801, IPPMorphOp(op, _src, _dst, kernel, anchor, iterations, borderType, borderValue)) - + CV_IPP_RUN(IPP_VERSION_X100 >= 801, ipp_MorphOp(op, _src, _dst, kernel, anchor, iterations, borderType, borderValue)) Mat src = _src.getMat(); _dst.create( src.size(), src.type() ); diff --git a/modules/imgproc/src/thresh.cpp b/modules/imgproc/src/thresh.cpp index 1fafeeef09..18d8afe1b7 100644 --- a/modules/imgproc/src/thresh.cpp +++ b/modules/imgproc/src/thresh.cpp @@ -907,22 +907,22 @@ thresh_32f( const Mat& _src, Mat& _dst, float thresh, float maxval, int type ) #ifdef HAVE_IPP static bool ipp_getThreshVal_Otsu_8u( const unsigned char* _src, int step, Size size, unsigned char &thresh) { +#if IPP_VERSION_X100 >= 801 && !HAVE_ICV int ippStatus = -1; -#if IPP_VERSION_X100 >= 801 && !defined(HAVE_IPP_ICV_ONLY) IppiSize srcSize = { size.width, size.height }; CV_SUPPRESS_DEPRECATED_START ippStatus = ippiComputeThreshold_Otsu_8u_C1R(_src, step, srcSize, &thresh); CV_SUPPRESS_DEPRECATED_END + + if(ippStatus >= 0) + return true; #else CV_UNUSED(_src); CV_UNUSED(step); CV_UNUSED(size); CV_UNUSED(thresh); #endif - if(ippStatus >= 0) - return true; return false; } #endif - static double getThreshVal_Otsu_8u( const Mat& _src ) { @@ -937,9 +937,8 @@ getThreshVal_Otsu_8u( const Mat& _src ) #ifdef HAVE_IPP unsigned char thresh; + CV_IPP_RUN(IPP_VERSION_X100 >= 801 && !HAVE_ICV, ipp_getThreshVal_Otsu_8u(_src.ptr(), step, size, thresh), thresh); #endif - CV_IPP_RUN(true, ipp_getThreshVal_Otsu_8u(_src.ptr(), step, size, thresh), thresh); - const int N = 256; int i, j, h[N] = {0}; From e57609836ce9e9d82f838eca4ff3bbd7ffce2433 Mon Sep 17 00:00:00 2001 From: Pavel Vlasov Date: Wed, 24 Jun 2015 14:34:20 +0300 Subject: [PATCH 058/133] Warning fix; --- modules/core/src/convert.cpp | 1 - modules/core/src/copy.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/modules/core/src/convert.cpp b/modules/core/src/convert.cpp index 7e561b6e75..fd07a7fc53 100644 --- a/modules/core/src/convert.cpp +++ b/modules/core/src/convert.cpp @@ -5841,7 +5841,6 @@ private: static bool ipp_lut(Mat &src, Mat &lut, Mat &dst) { - int cn = src.channels(); int lutcn = lut.channels(); if(src.dims > 2) diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index 27e8c00d5c..0d483ede1d 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -691,7 +691,6 @@ static bool ocl_flip(InputArray _src, OutputArray _dst, int flipCode ) #if defined HAVE_IPP static bool ipp_flip( Mat &src, Mat &dst, int flip_mode ) { - Size size = src.size(); int type = src.type(); typedef IppStatus (CV_STDCALL * ippiMirror)(const void * pSrc, int srcStep, void * pDst, int dstStep, IppiSize roiSize, IppiAxis flip); From e02195b3dc38727bbb4e4dc96b7b055382667d7d Mon Sep 17 00:00:00 2001 From: Pavel Vlasov Date: Wed, 24 Jun 2015 14:55:45 +0300 Subject: [PATCH 059/133] Accidentally removed tegra checks were returned; --- modules/imgproc/src/color.cpp | 41 ++++++++++++++--------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/modules/imgproc/src/color.cpp b/modules/imgproc/src/color.cpp index 6f53a120ae..5d4a943daf 100644 --- a/modules/imgproc/src/color.cpp +++ b/modules/imgproc/src/color.cpp @@ -7323,17 +7323,13 @@ static bool ocl_cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) #endif -}//namespace cv - #ifdef HAVE_IPP -namespace cv +static bool ipp_cvtColor( Mat &src, OutputArray _dst, int code, int dcn ) { -static bool ipp_cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) -{ - int stype = _src.type(); + int stype = src.type(); int scn = CV_MAT_CN(stype), depth = CV_MAT_DEPTH(stype); - Mat src = _src.getMat(), dst; + Mat dst; Size sz = src.size(); switch( code ) @@ -7916,8 +7912,8 @@ static bool ipp_cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) return false; } } -} #endif +} ////////////////////////////////////////////////////////////////////////////////////////// // The main function // @@ -7933,8 +7929,9 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) Mat src = _src.getMat(), dst; Size sz = src.size(); + CV_Assert( depth == CV_8U || depth == CV_16U || depth == CV_32F ); - CV_IPP_RUN(true, ipp_cvtColor(_src, _dst, code, dcn)); + CV_IPP_RUN(true, ipp_cvtColor(src, _dst, code, dcn)); switch( code ) { @@ -7947,13 +7944,13 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create( sz, CV_MAKETYPE(depth, dcn)); dst = _dst.getMat(); - if( depth == CV_8U ) { #ifdef HAVE_TEGRA_OPTIMIZATION - if(!tegra::cvtBGR2RGB(src, dst, bidx)) + if(tegra::useTegra() && tegra::cvtBGR2RGB(src, dst, bidx)) + break; #endif - CvtColorLoop(src, dst, RGB2RGB(scn, dcn, bidx)); + CvtColorLoop(src, dst, RGB2RGB(scn, dcn, bidx)); } else if( depth == CV_16U ) CvtColorLoop(src, dst, RGB2RGB(scn, dcn, bidx)); @@ -7967,10 +7964,9 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_8UC2); dst = _dst.getMat(); - #ifdef HAVE_TEGRA_OPTIMIZATION if(code == CV_BGR2BGR565 || code == CV_BGRA2BGR565 || code == CV_RGB2BGR565 || code == CV_RGBA2BGR565) - if(tegra::cvtRGB2RGB565(src, dst, code == CV_RGB2BGR565 || code == CV_RGBA2BGR565 ? 0 : 2)) + if(tegra::useTegra() && tegra::cvtRGB2RGB565(src, dst, code == CV_RGB2BGR565 || code == CV_RGBA2BGR565 ? 0 : 2)) break; #endif @@ -8002,13 +7998,13 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, 1)); dst = _dst.getMat(); - bidx = code == CV_BGR2GRAY || code == CV_BGRA2GRAY ? 0 : 2; if( depth == CV_8U ) { #ifdef HAVE_TEGRA_OPTIMIZATION - if(!tegra::cvtRGB2Gray(src, dst, bidx)) + if(tegra::useTegra() && tegra::cvtRGB2Gray(src, dst, bidx)) + break; #endif CvtColorLoop(src, dst, RGB2Gray(scn, bidx, 0)); } @@ -8032,11 +8028,11 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, dcn)); dst = _dst.getMat(); - if( depth == CV_8U ) { #ifdef HAVE_TEGRA_OPTIMIZATION - if(!tegra::cvtGray2RGB(src, dst)) + if(tegra::useTegra() && tegra::cvtGray2RGB(src, dst)) + break; #endif CvtColorLoop(src, dst, Gray2RGB(dcn)); } @@ -8070,7 +8066,7 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) if( depth == CV_8U ) { #ifdef HAVE_TEGRA_OPTIMIZATION - if((code == CV_RGB2YCrCb || code == CV_BGR2YCrCb) && tegra::cvtRGB2YCrCb(src, dst, bidx)) + if((code == CV_RGB2YCrCb || code == CV_BGR2YCrCb) && tegra::useTegra() && tegra::cvtRGB2YCrCb(src, dst, bidx)) break; #endif CvtColorLoop(src, dst, RGB2YCrCb_i(scn, bidx, coeffs_i)); @@ -8096,7 +8092,6 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, dcn)); dst = _dst.getMat(); - if( depth == CV_8U ) CvtColorLoop(src, dst, YCrCb2RGB_i(dcn, bidx, coeffs_i)); else if( depth == CV_16U ) @@ -8149,12 +8144,11 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, 3)); dst = _dst.getMat(); - if( code == CV_BGR2HSV || code == CV_RGB2HSV || code == CV_BGR2HSV_FULL || code == CV_RGB2HSV_FULL ) { #ifdef HAVE_TEGRA_OPTIMIZATION - if(tegra::cvtRGB2HSV(src, dst, bidx, hrange)) + if(tegra::useTegra() && tegra::cvtRGB2HSV(src, dst, bidx, hrange)) break; #endif if( depth == CV_8U ) @@ -8185,7 +8179,6 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, dcn)); dst = _dst.getMat(); - if( code == CV_HSV2BGR || code == CV_HSV2RGB || code == CV_HSV2BGR_FULL || code == CV_HSV2RGB_FULL ) { @@ -8216,7 +8209,6 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, 3)); dst = _dst.getMat(); - if( code == CV_BGR2Lab || code == CV_RGB2Lab || code == CV_LBGR2Lab || code == CV_LRGB2Lab ) { @@ -8248,7 +8240,6 @@ void cv::cvtColor( InputArray _src, OutputArray _dst, int code, int dcn ) _dst.create(sz, CV_MAKETYPE(depth, dcn)); dst = _dst.getMat(); - if( code == CV_Lab2BGR || code == CV_Lab2RGB || code == CV_Lab2LBGR || code == CV_Lab2LRGB ) { From 6eb8d0aa517940b0cac93a6ea42fa3d793df7bc9 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Wed, 24 Jun 2015 16:05:08 +0300 Subject: [PATCH 060/133] Fixed build with CUDA --- modules/cudev/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cudev/CMakeLists.txt b/modules/cudev/CMakeLists.txt index c5520b1e69..257572951e 100644 --- a/modules/cudev/CMakeLists.txt +++ b/modules/cudev/CMakeLists.txt @@ -8,7 +8,7 @@ ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4189 /wd4505 -Wundef -Wmissing-declarati ocv_add_module(cudev) -ocv_module_include_directories(opencv_core) +ocv_module_include_directories(opencv_core opencv_hal) file(GLOB_RECURSE lib_hdrs "include/opencv2/*.hpp") file(GLOB lib_srcs "src/*.cpp") From 851f20e40c2630a09249c25ae3ce95afc69d0337 Mon Sep 17 00:00:00 2001 From: Stefan Cornelius Date: Thu, 25 Jun 2015 00:17:49 +0200 Subject: [PATCH 061/133] Fix processing of SunRaster images with negative maplength --- modules/imgcodecs/src/grfmt_sunras.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgcodecs/src/grfmt_sunras.cpp b/modules/imgcodecs/src/grfmt_sunras.cpp index 6cbefef3c7..34e5c4e837 100644 --- a/modules/imgcodecs/src/grfmt_sunras.cpp +++ b/modules/imgcodecs/src/grfmt_sunras.cpp @@ -96,7 +96,7 @@ bool SunRasterDecoder::readHeader() (m_encoding == RAS_OLD || m_encoding == RAS_STANDARD || (m_type == RAS_BYTE_ENCODED && m_bpp == 8) || m_type == RAS_FORMAT_RGB) && ((m_maptype == RMT_NONE && m_maplength == 0) || - (m_maptype == RMT_EQUAL_RGB && m_maplength <= palSize && m_bpp <= 8))) + (m_maptype == RMT_EQUAL_RGB && m_maplength <= palSize && m_maplength > 0 && m_bpp <= 8))) { memset( m_palette, 0, sizeof(m_palette)); From 7b784fa2ea1b5fedd3a0626c8a6c724eab209360 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Thu, 25 Jun 2015 18:19:23 +0300 Subject: [PATCH 062/133] Fixed transpose intrinsic prefix for NEON --- modules/hal/include/opencv2/hal/intrin_neon.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/hal/include/opencv2/hal/intrin_neon.hpp b/modules/hal/include/opencv2/hal/intrin_neon.hpp index 2418566e62..31c3e30646 100644 --- a/modules/hal/include/opencv2/hal/intrin_neon.hpp +++ b/modules/hal/include/opencv2/hal/intrin_neon.hpp @@ -747,7 +747,7 @@ inline v_int32x4 v_trunc(const v_float32x4& a) { return v_int32x4(vcvtq_s32_f32(a.val)); } #define OPENCV_HAL_IMPL_NEON_TRANSPOSE4x4(_Tpvec, suffix) \ -inline void transpose4x4(const v_##_Tpvec& a0, const v_##_Tpvec& a1, \ +inline void v_transpose4x4(const v_##_Tpvec& a0, const v_##_Tpvec& a1, \ const v_##_Tpvec& a2, const v_##_Tpvec& a3, \ v_##_Tpvec& b0, v_##_Tpvec& b1, \ v_##_Tpvec& b2, v_##_Tpvec& b3) \ From 24e2a008c3a4ace0394fff16fa0c2cf3094d96e1 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 25 Jun 2015 19:59:27 +0300 Subject: [PATCH 063/133] copyTo: fix for big data (4Gb+) --- modules/core/src/copy.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/core/src/copy.cpp b/modules/core/src/copy.cpp index 0d483ede1d..67b81c4521 100644 --- a/modules/core/src/copy.cpp +++ b/modules/core/src/copy.cpp @@ -288,11 +288,17 @@ void Mat::copyTo( OutputArray _dst ) const const uchar* sptr = data; uchar* dptr = dst.data; + CV_IPP_RUN( + (size_t)cols*elemSize() <= (size_t)INT_MAX && + (size_t)step <= (size_t)INT_MAX && + (size_t)dst.step <= (size_t)INT_MAX + , + ippiCopy_8u_C1R(sptr, (int)step, dptr, (int)dst.step, ippiSize((int)(cols*elemSize()), rows)) >= 0 + ) + Size sz = getContinuousSize(*this, dst); size_t len = sz.width*elemSize(); - CV_IPP_RUN(true, ippiCopy_8u_C1R(sptr, (int)step, dptr, (int)dst.step, ippiSize((int)len, sz.height)) >= 0) - for( ; sz.height--; sptr += step, dptr += dst.step ) memcpy( dptr, sptr, len ); } From e66a4a0e124d6cc9da27317635d8d02f62b3a91b Mon Sep 17 00:00:00 2001 From: scorneli Date: Thu, 25 Jun 2015 00:13:38 +0200 Subject: [PATCH 064/133] Fix processing of SunRaster images with negative maplength From ee68d26f9970f1c400d4c5dbe062586d920988b6 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 10 Jun 2015 14:09:24 +0300 Subject: [PATCH 065/133] ocl: update generator scripts --- .../opencv2/core/opencl/runtime/opencl_gl.hpp | 65 +++++++++++++++++++ .../opencl/runtime/opencl_gl_wrappers.hpp | 47 ++++++++++++++ .../src/opencl/runtime/generator/generate.sh | 2 + .../src/opencl/runtime/generator/parser_cl.py | 32 +++++---- .../generator/template/opencl_gl.hpp.in | 17 +++++ .../generator/template/opencl_gl_impl.hpp.in | 11 ++++ .../template/opencl_gl_wrappers.hpp.in | 5 ++ .../core/src/opencl/runtime/opencl_core.cpp | 26 ++++++++ 8 files changed, 194 insertions(+), 11 deletions(-) create mode 100644 modules/core/include/opencv2/core/opencl/runtime/opencl_gl.hpp create mode 100644 modules/core/include/opencv2/core/opencl/runtime/opencl_gl_wrappers.hpp create mode 100644 modules/core/src/opencl/runtime/generator/template/opencl_gl.hpp.in create mode 100644 modules/core/src/opencl/runtime/generator/template/opencl_gl_impl.hpp.in create mode 100644 modules/core/src/opencl/runtime/generator/template/opencl_gl_wrappers.hpp.in diff --git a/modules/core/include/opencv2/core/opencl/runtime/opencl_gl.hpp b/modules/core/include/opencv2/core/opencl/runtime/opencl_gl.hpp new file mode 100644 index 0000000000..7c7a82e9ea --- /dev/null +++ b/modules/core/include/opencv2/core/opencl/runtime/opencl_gl.hpp @@ -0,0 +1,65 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the OpenCV Foundation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_HPP__ +#define __OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_HPP__ + +#if defined HAVE_OPENCL && defined HAVE_OPENGL + +#include "opencl_core.hpp" + +#if defined(HAVE_OPENCL_STATIC) + +#if defined __APPLE__ +#include +#else +#include +#endif + +#else // HAVE_OPENCL_STATIC + +#include "autogenerated/opencl_gl.hpp" + +#endif // HAVE_OPENCL_STATIC + +#endif // defined HAVE_OPENCL && defined HAVE_OPENGL + +#endif // __OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_HPP__ diff --git a/modules/core/include/opencv2/core/opencl/runtime/opencl_gl_wrappers.hpp b/modules/core/include/opencv2/core/opencl/runtime/opencl_gl_wrappers.hpp new file mode 100644 index 0000000000..9327d2ede6 --- /dev/null +++ b/modules/core/include/opencv2/core/opencl/runtime/opencl_gl_wrappers.hpp @@ -0,0 +1,47 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the OpenCV Foundation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_WRAPPERS_HPP__ +#define __OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_WRAPPERS_HPP__ + +#include "autogenerated/opencl_gl_wrappers.hpp" + +#endif // __OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_WRAPPERS_HPP__ diff --git a/modules/core/src/opencl/runtime/generator/generate.sh b/modules/core/src/opencl/runtime/generator/generate.sh index 8649e99843..d9d6f0e3e8 100644 --- a/modules/core/src/opencl/runtime/generator/generate.sh +++ b/modules/core/src/opencl/runtime/generator/generate.sh @@ -3,4 +3,6 @@ echo "Generate files for CL runtime..." python parser_cl.py opencl_core < sources/cl.h python parser_clamdblas.py < sources/clAmdBlas.h python parser_clamdfft.py < sources/clAmdFft.h + +python parser_cl.py opencl_gl < sources/cl_gl.h echo "Generate files for CL runtime... Done" diff --git a/modules/core/src/opencl/runtime/generator/parser_cl.py b/modules/core/src/opencl/runtime/generator/parser_cl.py index 87eeb27236..e6c738bef7 100644 --- a/modules/core/src/opencl/runtime/generator/parser_cl.py +++ b/modules/core/src/opencl/runtime/generator/parser_cl.py @@ -8,9 +8,10 @@ from common import remove_comments, getTokens, getParameters, postProcessParamet try: if len(sys.argv) > 1: - outfile = open('../../../../include/opencv2/core/opencl/runtime/autogenerated/' + sys.argv[1] + '.hpp', 'wb') - outfile_impl = open('../autogenerated/' + sys.argv[1] + '_impl.hpp', 'wb') - outfile_wrappers = open('../../../../include/opencv2/core/opencl/runtime/autogenerated/' + sys.argv[1] + '_wrappers.hpp', 'wb') + module_name = sys.argv[1] + outfile = open('../../../../include/opencv2/core/opencl/runtime/autogenerated/%s.hpp' % module_name, 'wb') + outfile_impl = open('../autogenerated/%s_impl.hpp' % module_name, 'wb') + outfile_wrappers = open('../../../../include/opencv2/core/opencl/runtime/autogenerated/%s_wrappers.hpp' % module_name, 'wb') if len(sys.argv) > 2: f = open(sys.argv[2], "r") else: @@ -95,7 +96,7 @@ pprint(fns) from common import * -filterFileName='./filter/opencl_core_functions.list' +filterFileName = './filter/%s_functions.list' % module_name numEnabled = readFunctionFilter(fns, filterFileName) functionsFilter = generateFilterNames(fns) @@ -108,18 +109,27 @@ ctx['CL_REMAP_DYNAMIC'] = generateRemapDynamic(fns) ctx['CL_FN_DECLARATIONS'] = generateFnDeclaration(fns) sys.stdout = outfile -ProcessTemplate('template/opencl_core.hpp.in', ctx) +ProcessTemplate('template/%s.hpp.in' % module_name, ctx) ctx['CL_FN_INLINE_WRAPPERS'] = generateInlineWrappers(fns) sys.stdout = outfile_wrappers -ProcessTemplate('template/opencl_core_wrappers.hpp.in', ctx) +ProcessTemplate('template/%s_wrappers.hpp.in' % module_name, ctx) -ctx['CL_FN_ENTRY_DEFINITIONS'] = generateStructDefinitions(fns) -ctx['CL_FN_ENTRY_LIST'] = generateListOfDefinitions(fns) -ctx['CL_FN_ENUMS'] = generateEnums(fns) -ctx['CL_FN_SWITCH'] = generateTemplates(15, 'opencl_fn', 'opencl_check_fn', 'CL_API_CALL') +if module_name == 'opencl_core': + ctx['CL_FN_ENTRY_DEFINITIONS'] = generateStructDefinitions(fns) + ctx['CL_FN_ENTRY_LIST'] = generateListOfDefinitions(fns) + ctx['CL_FN_ENUMS'] = generateEnums(fns) + ctx['CL_FN_SWITCH'] = generateTemplates(15, 'opencl_fn', 'opencl_check_fn', 'CL_API_CALL') +else: + lprefix = module_name + '_fn' + enumprefix = module_name.upper() + '_FN' + fn_list_name = module_name + '_fn_list' + ctx['CL_FN_ENTRY_DEFINITIONS'] = generateStructDefinitions(fns, lprefix=lprefix, enumprefix=enumprefix) + ctx['CL_FN_ENTRY_LIST'] = generateListOfDefinitions(fns, fn_list_name) + ctx['CL_FN_ENUMS'] = generateEnums(fns, prefix=enumprefix) + ctx['CL_FN_SWITCH'] = generateTemplates(15, lprefix, '%s_check_fn' % module_name, 'CL_API_CALL') ctx['CL_NUMBER_OF_ENABLED_FUNCTIONS'] = '// number of enabled functions: %d' % (numEnabled) sys.stdout = outfile_impl -ProcessTemplate('template/opencl_core_impl.hpp.in', ctx) +ProcessTemplate('template/%s_impl.hpp.in' % module_name, ctx) diff --git a/modules/core/src/opencl/runtime/generator/template/opencl_gl.hpp.in b/modules/core/src/opencl/runtime/generator/template/opencl_gl.hpp.in new file mode 100644 index 0000000000..28c342f3a1 --- /dev/null +++ b/modules/core/src/opencl/runtime/generator/template/opencl_gl.hpp.in @@ -0,0 +1,17 @@ +#ifndef __OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_HPP__ +#error "Invalid usage" +#endif + +@CL_REMAP_ORIGIN@ + +#if defined __APPLE__ +#include +#include +#else +#include +#include +#endif + +@CL_REMAP_DYNAMIC@ + +@CL_FN_DECLARATIONS@ diff --git a/modules/core/src/opencl/runtime/generator/template/opencl_gl_impl.hpp.in b/modules/core/src/opencl/runtime/generator/template/opencl_gl_impl.hpp.in new file mode 100644 index 0000000000..14586017a4 --- /dev/null +++ b/modules/core/src/opencl/runtime/generator/template/opencl_gl_impl.hpp.in @@ -0,0 +1,11 @@ +@CL_FN_ENUMS@ + +namespace { +@CL_FN_SWITCH@ +} // anonymous namespace + +@CL_FN_ENTRY_DEFINITIONS@ + +@CL_FN_ENTRY_LIST@ + +@CL_NUMBER_OF_ENABLED_FUNCTIONS@ diff --git a/modules/core/src/opencl/runtime/generator/template/opencl_gl_wrappers.hpp.in b/modules/core/src/opencl/runtime/generator/template/opencl_gl_wrappers.hpp.in new file mode 100644 index 0000000000..0aeefb4f44 --- /dev/null +++ b/modules/core/src/opencl/runtime/generator/template/opencl_gl_wrappers.hpp.in @@ -0,0 +1,5 @@ +#ifndef __OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_WRAPPERS_HPP__ +#error "Invalid usage" +#endif + +@CL_FN_INLINE_WRAPPERS@ diff --git a/modules/core/src/opencl/runtime/opencl_core.cpp b/modules/core/src/opencl/runtime/opencl_core.cpp index 43f6b13b6e..971c0770e5 100644 --- a/modules/core/src/opencl/runtime/opencl_core.cpp +++ b/modules/core/src/opencl/runtime/opencl_core.cpp @@ -279,4 +279,30 @@ static void* opencl_check_fn(int ID) return func; } +#ifdef HAVE_OPENGL + +#include "opencv2/core/opencl/runtime/opencl_gl.hpp" + +static void* opencl_gl_check_fn(int ID); + +#include "autogenerated/opencl_gl_impl.hpp" + +static void* opencl_gl_check_fn(int ID) +{ + const struct DynamicFnEntry* e = NULL; + assert(ID >= 0 && ID < (int)(sizeof(opencl_gl_fn_list)/sizeof(opencl_gl_fn_list[0]))); + e = opencl_gl_fn_list[ID]; + void* func = CV_CL_GET_PROC_ADDRESS(e->fnName); + if (!func) + { + throw cv::Exception(cv::Error::OpenCLApiCallError, + cv::format("OpenCL function is not available: [%s]", e->fnName), + CV_Func, __FILE__, __LINE__); + } + *(e->ppFn) = func; + return func; +} + +#endif // HAVE_OPENGL + #endif From 04b2edcc8cb8e897bf1dd347751513c4cd483980 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 10 Jun 2015 14:15:34 +0300 Subject: [PATCH 066/133] ocl: autogenerated files for cl_gl.h --- .../runtime/autogenerated/opencl_gl.hpp | 58 +++++ .../autogenerated/opencl_gl_wrappers.hpp | 38 ++++ .../runtime/autogenerated/opencl_gl_impl.hpp | 198 ++++++++++++++++++ .../generator/filter/opencl_gl_functions.list | 11 + .../generator/template/opencl_gl.hpp.in | 2 - 5 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 modules/core/include/opencv2/core/opencl/runtime/autogenerated/opencl_gl.hpp create mode 100644 modules/core/include/opencv2/core/opencl/runtime/autogenerated/opencl_gl_wrappers.hpp create mode 100644 modules/core/src/opencl/runtime/autogenerated/opencl_gl_impl.hpp create mode 100644 modules/core/src/opencl/runtime/generator/filter/opencl_gl_functions.list diff --git a/modules/core/include/opencv2/core/opencl/runtime/autogenerated/opencl_gl.hpp b/modules/core/include/opencv2/core/opencl/runtime/autogenerated/opencl_gl.hpp new file mode 100644 index 0000000000..f37ad158e1 --- /dev/null +++ b/modules/core/include/opencv2/core/opencl/runtime/autogenerated/opencl_gl.hpp @@ -0,0 +1,58 @@ +// +// AUTOGENERATED, DO NOT EDIT +// +#ifndef __OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_HPP__ +#error "Invalid usage" +#endif + +// generated by parser_cl.py +#define clCreateFromGLBuffer clCreateFromGLBuffer_ +#define clCreateFromGLRenderbuffer clCreateFromGLRenderbuffer_ +#define clCreateFromGLTexture clCreateFromGLTexture_ +#define clCreateFromGLTexture2D clCreateFromGLTexture2D_ +#define clCreateFromGLTexture3D clCreateFromGLTexture3D_ +#define clEnqueueAcquireGLObjects clEnqueueAcquireGLObjects_ +#define clEnqueueReleaseGLObjects clEnqueueReleaseGLObjects_ +#define clGetGLContextInfoKHR clGetGLContextInfoKHR_ +#define clGetGLObjectInfo clGetGLObjectInfo_ +#define clGetGLTextureInfo clGetGLTextureInfo_ + +#if defined __APPLE__ +#include +#else +#include +#endif + +// generated by parser_cl.py +#undef clCreateFromGLBuffer +#define clCreateFromGLBuffer clCreateFromGLBuffer_pfn +#undef clCreateFromGLRenderbuffer +#define clCreateFromGLRenderbuffer clCreateFromGLRenderbuffer_pfn +#undef clCreateFromGLTexture +#define clCreateFromGLTexture clCreateFromGLTexture_pfn +#undef clCreateFromGLTexture2D +#define clCreateFromGLTexture2D clCreateFromGLTexture2D_pfn +#undef clCreateFromGLTexture3D +#define clCreateFromGLTexture3D clCreateFromGLTexture3D_pfn +#undef clEnqueueAcquireGLObjects +#define clEnqueueAcquireGLObjects clEnqueueAcquireGLObjects_pfn +#undef clEnqueueReleaseGLObjects +#define clEnqueueReleaseGLObjects clEnqueueReleaseGLObjects_pfn +#undef clGetGLContextInfoKHR +#define clGetGLContextInfoKHR clGetGLContextInfoKHR_pfn +#undef clGetGLObjectInfo +#define clGetGLObjectInfo clGetGLObjectInfo_pfn +#undef clGetGLTextureInfo +#define clGetGLTextureInfo clGetGLTextureInfo_pfn + +// generated by parser_cl.py +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateFromGLBuffer)(cl_context, cl_mem_flags, cl_GLuint, int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateFromGLRenderbuffer)(cl_context, cl_mem_flags, cl_GLuint, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateFromGLTexture)(cl_context, cl_mem_flags, cl_GLenum, cl_GLint, cl_GLuint, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateFromGLTexture2D)(cl_context, cl_mem_flags, cl_GLenum, cl_GLint, cl_GLuint, cl_int*); +extern CL_RUNTIME_EXPORT cl_mem (CL_API_CALL*clCreateFromGLTexture3D)(cl_context, cl_mem_flags, cl_GLenum, cl_GLint, cl_GLuint, cl_int*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueAcquireGLObjects)(cl_command_queue, cl_uint, const cl_mem*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clEnqueueReleaseGLObjects)(cl_command_queue, cl_uint, const cl_mem*, cl_uint, const cl_event*, cl_event*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetGLContextInfoKHR)(const cl_context_properties*, cl_gl_context_info, size_t, void*, size_t*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetGLObjectInfo)(cl_mem, cl_gl_object_type*, cl_GLuint*); +extern CL_RUNTIME_EXPORT cl_int (CL_API_CALL*clGetGLTextureInfo)(cl_mem, cl_gl_texture_info, size_t, void*, size_t*); diff --git a/modules/core/include/opencv2/core/opencl/runtime/autogenerated/opencl_gl_wrappers.hpp b/modules/core/include/opencv2/core/opencl/runtime/autogenerated/opencl_gl_wrappers.hpp new file mode 100644 index 0000000000..105867f47a --- /dev/null +++ b/modules/core/include/opencv2/core/opencl/runtime/autogenerated/opencl_gl_wrappers.hpp @@ -0,0 +1,38 @@ +// +// AUTOGENERATED, DO NOT EDIT +// +#ifndef __OPENCV_CORE_OCL_RUNTIME_OPENCL_GL_WRAPPERS_HPP__ +#error "Invalid usage" +#endif + +// generated by parser_cl.py +#undef clCreateFromGLBuffer +#define clCreateFromGLBuffer clCreateFromGLBuffer_fn +inline cl_mem clCreateFromGLBuffer(cl_context p0, cl_mem_flags p1, cl_GLuint p2, int* p3) { return clCreateFromGLBuffer_pfn(p0, p1, p2, p3); } +#undef clCreateFromGLRenderbuffer +#define clCreateFromGLRenderbuffer clCreateFromGLRenderbuffer_fn +inline cl_mem clCreateFromGLRenderbuffer(cl_context p0, cl_mem_flags p1, cl_GLuint p2, cl_int* p3) { return clCreateFromGLRenderbuffer_pfn(p0, p1, p2, p3); } +#undef clCreateFromGLTexture +#define clCreateFromGLTexture clCreateFromGLTexture_fn +inline cl_mem clCreateFromGLTexture(cl_context p0, cl_mem_flags p1, cl_GLenum p2, cl_GLint p3, cl_GLuint p4, cl_int* p5) { return clCreateFromGLTexture_pfn(p0, p1, p2, p3, p4, p5); } +#undef clCreateFromGLTexture2D +#define clCreateFromGLTexture2D clCreateFromGLTexture2D_fn +inline cl_mem clCreateFromGLTexture2D(cl_context p0, cl_mem_flags p1, cl_GLenum p2, cl_GLint p3, cl_GLuint p4, cl_int* p5) { return clCreateFromGLTexture2D_pfn(p0, p1, p2, p3, p4, p5); } +#undef clCreateFromGLTexture3D +#define clCreateFromGLTexture3D clCreateFromGLTexture3D_fn +inline cl_mem clCreateFromGLTexture3D(cl_context p0, cl_mem_flags p1, cl_GLenum p2, cl_GLint p3, cl_GLuint p4, cl_int* p5) { return clCreateFromGLTexture3D_pfn(p0, p1, p2, p3, p4, p5); } +#undef clEnqueueAcquireGLObjects +#define clEnqueueAcquireGLObjects clEnqueueAcquireGLObjects_fn +inline cl_int clEnqueueAcquireGLObjects(cl_command_queue p0, cl_uint p1, const cl_mem* p2, cl_uint p3, const cl_event* p4, cl_event* p5) { return clEnqueueAcquireGLObjects_pfn(p0, p1, p2, p3, p4, p5); } +#undef clEnqueueReleaseGLObjects +#define clEnqueueReleaseGLObjects clEnqueueReleaseGLObjects_fn +inline cl_int clEnqueueReleaseGLObjects(cl_command_queue p0, cl_uint p1, const cl_mem* p2, cl_uint p3, const cl_event* p4, cl_event* p5) { return clEnqueueReleaseGLObjects_pfn(p0, p1, p2, p3, p4, p5); } +#undef clGetGLContextInfoKHR +#define clGetGLContextInfoKHR clGetGLContextInfoKHR_fn +inline cl_int clGetGLContextInfoKHR(const cl_context_properties* p0, cl_gl_context_info p1, size_t p2, void* p3, size_t* p4) { return clGetGLContextInfoKHR_pfn(p0, p1, p2, p3, p4); } +#undef clGetGLObjectInfo +#define clGetGLObjectInfo clGetGLObjectInfo_fn +inline cl_int clGetGLObjectInfo(cl_mem p0, cl_gl_object_type* p1, cl_GLuint* p2) { return clGetGLObjectInfo_pfn(p0, p1, p2); } +#undef clGetGLTextureInfo +#define clGetGLTextureInfo clGetGLTextureInfo_fn +inline cl_int clGetGLTextureInfo(cl_mem p0, cl_gl_texture_info p1, size_t p2, void* p3, size_t* p4) { return clGetGLTextureInfo_pfn(p0, p1, p2, p3, p4); } diff --git a/modules/core/src/opencl/runtime/autogenerated/opencl_gl_impl.hpp b/modules/core/src/opencl/runtime/autogenerated/opencl_gl_impl.hpp new file mode 100644 index 0000000000..6d97180508 --- /dev/null +++ b/modules/core/src/opencl/runtime/autogenerated/opencl_gl_impl.hpp @@ -0,0 +1,198 @@ +// +// AUTOGENERATED, DO NOT EDIT +// +// generated by parser_cl.py +enum OPENCL_GL_FN_ID { + OPENCL_GL_FN_clCreateFromGLBuffer = 0, + OPENCL_GL_FN_clCreateFromGLRenderbuffer = 1, + OPENCL_GL_FN_clCreateFromGLTexture = 2, + OPENCL_GL_FN_clCreateFromGLTexture2D = 3, + OPENCL_GL_FN_clCreateFromGLTexture3D = 4, + OPENCL_GL_FN_clEnqueueAcquireGLObjects = 5, + OPENCL_GL_FN_clEnqueueReleaseGLObjects = 6, + OPENCL_GL_FN_clGetGLContextInfoKHR = 7, + OPENCL_GL_FN_clGetGLObjectInfo = 8, + OPENCL_GL_FN_clGetGLTextureInfo = 9, +}; + +namespace { +// generated by parser_cl.py +template +struct opencl_gl_fn0 +{ + typedef _R (CL_API_CALL*FN)(); + static _R CL_API_CALL switch_fn() + { return ((FN)opencl_gl_check_fn(ID))(); } +}; + +template +struct opencl_gl_fn1 +{ + typedef _R (CL_API_CALL*FN)(_T1); + static _R CL_API_CALL switch_fn(_T1 p1) + { return ((FN)opencl_gl_check_fn(ID))(p1); } +}; + +template +struct opencl_gl_fn2 +{ + typedef _R (CL_API_CALL*FN)(_T1, _T2); + static _R CL_API_CALL switch_fn(_T1 p1, _T2 p2) + { return ((FN)opencl_gl_check_fn(ID))(p1, p2); } +}; + +template +struct opencl_gl_fn3 +{ + typedef _R (CL_API_CALL*FN)(_T1, _T2, _T3); + static _R CL_API_CALL switch_fn(_T1 p1, _T2 p2, _T3 p3) + { return ((FN)opencl_gl_check_fn(ID))(p1, p2, p3); } +}; + +template +struct opencl_gl_fn4 +{ + typedef _R (CL_API_CALL*FN)(_T1, _T2, _T3, _T4); + static _R CL_API_CALL switch_fn(_T1 p1, _T2 p2, _T3 p3, _T4 p4) + { return ((FN)opencl_gl_check_fn(ID))(p1, p2, p3, p4); } +}; + +template +struct opencl_gl_fn5 +{ + typedef _R (CL_API_CALL*FN)(_T1, _T2, _T3, _T4, _T5); + static _R CL_API_CALL switch_fn(_T1 p1, _T2 p2, _T3 p3, _T4 p4, _T5 p5) + { return ((FN)opencl_gl_check_fn(ID))(p1, p2, p3, p4, p5); } +}; + +template +struct opencl_gl_fn6 +{ + typedef _R (CL_API_CALL*FN)(_T1, _T2, _T3, _T4, _T5, _T6); + static _R CL_API_CALL switch_fn(_T1 p1, _T2 p2, _T3 p3, _T4 p4, _T5 p5, _T6 p6) + { return ((FN)opencl_gl_check_fn(ID))(p1, p2, p3, p4, p5, p6); } +}; + +template +struct opencl_gl_fn7 +{ + typedef _R (CL_API_CALL*FN)(_T1, _T2, _T3, _T4, _T5, _T6, _T7); + static _R CL_API_CALL switch_fn(_T1 p1, _T2 p2, _T3 p3, _T4 p4, _T5 p5, _T6 p6, _T7 p7) + { return ((FN)opencl_gl_check_fn(ID))(p1, p2, p3, p4, p5, p6, p7); } +}; + +template +struct opencl_gl_fn8 +{ + typedef _R (CL_API_CALL*FN)(_T1, _T2, _T3, _T4, _T5, _T6, _T7, _T8); + static _R CL_API_CALL switch_fn(_T1 p1, _T2 p2, _T3 p3, _T4 p4, _T5 p5, _T6 p6, _T7 p7, _T8 p8) + { return ((FN)opencl_gl_check_fn(ID))(p1, p2, p3, p4, p5, p6, p7, p8); } +}; + +template +struct opencl_gl_fn9 +{ + typedef _R (CL_API_CALL*FN)(_T1, _T2, _T3, _T4, _T5, _T6, _T7, _T8, _T9); + static _R CL_API_CALL switch_fn(_T1 p1, _T2 p2, _T3 p3, _T4 p4, _T5 p5, _T6 p6, _T7 p7, _T8 p8, _T9 p9) + { return ((FN)opencl_gl_check_fn(ID))(p1, p2, p3, p4, p5, p6, p7, p8, p9); } +}; + +template +struct opencl_gl_fn10 +{ + typedef _R (CL_API_CALL*FN)(_T1, _T2, _T3, _T4, _T5, _T6, _T7, _T8, _T9, _T10); + static _R CL_API_CALL switch_fn(_T1 p1, _T2 p2, _T3 p3, _T4 p4, _T5 p5, _T6 p6, _T7 p7, _T8 p8, _T9 p9, _T10 p10) + { return ((FN)opencl_gl_check_fn(ID))(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); } +}; + +template +struct opencl_gl_fn11 +{ + typedef _R (CL_API_CALL*FN)(_T1, _T2, _T3, _T4, _T5, _T6, _T7, _T8, _T9, _T10, _T11); + static _R CL_API_CALL switch_fn(_T1 p1, _T2 p2, _T3 p3, _T4 p4, _T5 p5, _T6 p6, _T7 p7, _T8 p8, _T9 p9, _T10 p10, _T11 p11) + { return ((FN)opencl_gl_check_fn(ID))(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); } +}; + +template +struct opencl_gl_fn12 +{ + typedef _R (CL_API_CALL*FN)(_T1, _T2, _T3, _T4, _T5, _T6, _T7, _T8, _T9, _T10, _T11, _T12); + static _R CL_API_CALL switch_fn(_T1 p1, _T2 p2, _T3 p3, _T4 p4, _T5 p5, _T6 p6, _T7 p7, _T8 p8, _T9 p9, _T10 p10, _T11 p11, _T12 p12) + { return ((FN)opencl_gl_check_fn(ID))(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12); } +}; + +template +struct opencl_gl_fn13 +{ + typedef _R (CL_API_CALL*FN)(_T1, _T2, _T3, _T4, _T5, _T6, _T7, _T8, _T9, _T10, _T11, _T12, _T13); + static _R CL_API_CALL switch_fn(_T1 p1, _T2 p2, _T3 p3, _T4 p4, _T5 p5, _T6 p6, _T7 p7, _T8 p8, _T9 p9, _T10 p10, _T11 p11, _T12 p12, _T13 p13) + { return ((FN)opencl_gl_check_fn(ID))(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13); } +}; + +template +struct opencl_gl_fn14 +{ + typedef _R (CL_API_CALL*FN)(_T1, _T2, _T3, _T4, _T5, _T6, _T7, _T8, _T9, _T10, _T11, _T12, _T13, _T14); + static _R CL_API_CALL switch_fn(_T1 p1, _T2 p2, _T3 p3, _T4 p4, _T5 p5, _T6 p6, _T7 p7, _T8 p8, _T9 p9, _T10 p10, _T11 p11, _T12 p12, _T13 p13, _T14 p14) + { return ((FN)opencl_gl_check_fn(ID))(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14); } +}; + +} // anonymous namespace + +// generated by parser_cl.py +cl_mem (CL_API_CALL*clCreateFromGLBuffer)(cl_context, cl_mem_flags, cl_GLuint, int*) = + opencl_gl_fn4::switch_fn; +static const struct DynamicFnEntry clCreateFromGLBuffer_definition = { "clCreateFromGLBuffer", (void**)&clCreateFromGLBuffer}; + +cl_mem (CL_API_CALL*clCreateFromGLRenderbuffer)(cl_context, cl_mem_flags, cl_GLuint, cl_int*) = + opencl_gl_fn4::switch_fn; +static const struct DynamicFnEntry clCreateFromGLRenderbuffer_definition = { "clCreateFromGLRenderbuffer", (void**)&clCreateFromGLRenderbuffer}; + +cl_mem (CL_API_CALL*clCreateFromGLTexture)(cl_context, cl_mem_flags, cl_GLenum, cl_GLint, cl_GLuint, cl_int*) = + opencl_gl_fn6::switch_fn; +static const struct DynamicFnEntry clCreateFromGLTexture_definition = { "clCreateFromGLTexture", (void**)&clCreateFromGLTexture}; + +cl_mem (CL_API_CALL*clCreateFromGLTexture2D)(cl_context, cl_mem_flags, cl_GLenum, cl_GLint, cl_GLuint, cl_int*) = + opencl_gl_fn6::switch_fn; +static const struct DynamicFnEntry clCreateFromGLTexture2D_definition = { "clCreateFromGLTexture2D", (void**)&clCreateFromGLTexture2D}; + +cl_mem (CL_API_CALL*clCreateFromGLTexture3D)(cl_context, cl_mem_flags, cl_GLenum, cl_GLint, cl_GLuint, cl_int*) = + opencl_gl_fn6::switch_fn; +static const struct DynamicFnEntry clCreateFromGLTexture3D_definition = { "clCreateFromGLTexture3D", (void**)&clCreateFromGLTexture3D}; + +cl_int (CL_API_CALL*clEnqueueAcquireGLObjects)(cl_command_queue, cl_uint, const cl_mem*, cl_uint, const cl_event*, cl_event*) = + opencl_gl_fn6::switch_fn; +static const struct DynamicFnEntry clEnqueueAcquireGLObjects_definition = { "clEnqueueAcquireGLObjects", (void**)&clEnqueueAcquireGLObjects}; + +cl_int (CL_API_CALL*clEnqueueReleaseGLObjects)(cl_command_queue, cl_uint, const cl_mem*, cl_uint, const cl_event*, cl_event*) = + opencl_gl_fn6::switch_fn; +static const struct DynamicFnEntry clEnqueueReleaseGLObjects_definition = { "clEnqueueReleaseGLObjects", (void**)&clEnqueueReleaseGLObjects}; + +cl_int (CL_API_CALL*clGetGLContextInfoKHR)(const cl_context_properties*, cl_gl_context_info, size_t, void*, size_t*) = + opencl_gl_fn5::switch_fn; +static const struct DynamicFnEntry clGetGLContextInfoKHR_definition = { "clGetGLContextInfoKHR", (void**)&clGetGLContextInfoKHR}; + +cl_int (CL_API_CALL*clGetGLObjectInfo)(cl_mem, cl_gl_object_type*, cl_GLuint*) = + opencl_gl_fn3::switch_fn; +static const struct DynamicFnEntry clGetGLObjectInfo_definition = { "clGetGLObjectInfo", (void**)&clGetGLObjectInfo}; + +cl_int (CL_API_CALL*clGetGLTextureInfo)(cl_mem, cl_gl_texture_info, size_t, void*, size_t*) = + opencl_gl_fn5::switch_fn; +static const struct DynamicFnEntry clGetGLTextureInfo_definition = { "clGetGLTextureInfo", (void**)&clGetGLTextureInfo}; + + +// generated by parser_cl.py +static const struct DynamicFnEntry* opencl_gl_fn_list[] = { + &clCreateFromGLBuffer_definition, + &clCreateFromGLRenderbuffer_definition, + &clCreateFromGLTexture_definition, + &clCreateFromGLTexture2D_definition, + &clCreateFromGLTexture3D_definition, + &clEnqueueAcquireGLObjects_definition, + &clEnqueueReleaseGLObjects_definition, + &clGetGLContextInfoKHR_definition, + &clGetGLObjectInfo_definition, + &clGetGLTextureInfo_definition, +}; + +// number of enabled functions: 10 diff --git a/modules/core/src/opencl/runtime/generator/filter/opencl_gl_functions.list b/modules/core/src/opencl/runtime/generator/filter/opencl_gl_functions.list new file mode 100644 index 0000000000..b2f9e621aa --- /dev/null +++ b/modules/core/src/opencl/runtime/generator/filter/opencl_gl_functions.list @@ -0,0 +1,11 @@ +clCreateFromGLBuffer +clCreateFromGLRenderbuffer +clCreateFromGLTexture +clCreateFromGLTexture2D +clCreateFromGLTexture3D +clEnqueueAcquireGLObjects +clEnqueueReleaseGLObjects +clGetGLContextInfoKHR +clGetGLObjectInfo +clGetGLTextureInfo +#total 10 diff --git a/modules/core/src/opencl/runtime/generator/template/opencl_gl.hpp.in b/modules/core/src/opencl/runtime/generator/template/opencl_gl.hpp.in index 28c342f3a1..24434d2de7 100644 --- a/modules/core/src/opencl/runtime/generator/template/opencl_gl.hpp.in +++ b/modules/core/src/opencl/runtime/generator/template/opencl_gl.hpp.in @@ -5,10 +5,8 @@ @CL_REMAP_ORIGIN@ #if defined __APPLE__ -#include #include #else -#include #include #endif From 3f68787051ae4f299dac6b859212134fe42d715d Mon Sep 17 00:00:00 2001 From: Alexey Ershov Date: Mon, 1 Jun 2015 18:16:22 +0300 Subject: [PATCH 067/133] OpenGL OpenCL sample 057cd52 first versions: cv::ogl::convertFromGLTexture2D & cv::ogl::convertToGLTexture2D 5656e94 added autogenerated stuff for cl_gl.h 765f1fd resolved CL functions in opengl.cpp 9f9fee3 implemented function cv::ogl::ocl::initializeContextFromGLTexture2D() a792adb cv::ogl::ocl::initializeContextFromGLTexture2D() - added linux support (glx.h) 51c2869 added missing error message in function cv::ogl::ocl::initializeContextFromGLTexture2D() 513b887 fixed extension call in function cv::ogl::ocl::initializeContextFromGLTexture2D() 475a3e9 added CL-GL interop Windows sample (gpu/opengl_interop.cpp) 07af28f added building of CL-GL interop sample - Windows only befe3a2 fixed whitespace errors & doxygen warnings (precommit_docs) 551251a changed function name to cv::ogl::ocl::initializeContextFromGL(), removed unused argument 4d5f009 changed CL_DEVICES_FOR_GL_CONTEXT_KHR to CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR 9fc3055 changed CL_DEVICES_FOR_GL_CONTEXT_KHR to CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KH 6d31cee Revert "changed CL_DEVICES_FOR_GL_CONTEXT_KHR to CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KH" cc6a025 added texture format check in cv::ogl::convertFromGLTexture2D() 063a2c1 CL-GL sample: added Linux implementation (Xlib/GLX) c392ae9 fixed trailing whitespace 85a80d0 fixed include files ae23628 excluded samples/opengl from build case 2 9870ea5 added android EGL support 530b64c added doxygen documentation comments to CL-GL interop functions --- modules/core/include/opencv2/core/opengl.hpp | 26 +- modules/core/src/opengl.cpp | 265 ++++++++++- samples/CMakeLists.txt | 4 + samples/opengl/CMakeLists.txt | 45 ++ samples/opengl/opengl_interop.cpp | 454 +++++++++++++++++++ samples/opengl/winapp.hpp | 211 +++++++++ 6 files changed, 1003 insertions(+), 2 deletions(-) create mode 100644 samples/opengl/CMakeLists.txt create mode 100644 samples/opengl/opengl_interop.cpp create mode 100644 samples/opengl/winapp.hpp diff --git a/modules/core/include/opencv2/core/opengl.hpp b/modules/core/include/opencv2/core/opengl.hpp index 15c635c880..8c3235f5a3 100644 --- a/modules/core/include/opencv2/core/opengl.hpp +++ b/modules/core/include/opencv2/core/opengl.hpp @@ -48,6 +48,7 @@ #endif #include "opencv2/core.hpp" +#include "ocl.hpp" namespace cv { namespace ogl { @@ -511,7 +512,30 @@ CV_EXPORTS void render(const Arrays& arr, int mode = POINTS, Scalar color = Scal */ CV_EXPORTS void render(const Arrays& arr, InputArray indices, int mode = POINTS, Scalar color = Scalar::all(255)); -//! @} core_opengl +/////////////////// CL-GL Interoperability Functions /////////////////// + +namespace ocl { +using namespace cv::ocl; + +// TODO static functions in the Context class +/** @brief Creates OpenCL context from GL. +@return Returns reference to OpenCL Context + */ +CV_EXPORTS Context& initializeContextFromGL(); + +} // namespace cv::ogl::ocl + +/** @brief Converts InputArray to Texture2D object. +@param src - source InputArray. +@param texture - destination Texture2D object. + */ +CV_EXPORTS void convertToGLTexture2D(InputArray src, Texture2D& texture); + +/** @brief Converts Texture2D object to OutputArray. +@param texture - source Texture2D object. +@param dst - destination OutputArray. + */ +CV_EXPORTS void convertFromGLTexture2D(const Texture2D& texture, OutputArray dst); }} // namespace cv::ogl diff --git a/modules/core/src/opengl.cpp b/modules/core/src/opengl.cpp index 00a7f66662..542794dd7f 100644 --- a/modules/core/src/opengl.cpp +++ b/modules/core/src/opengl.cpp @@ -47,7 +47,9 @@ # ifdef HAVE_CUDA # include # endif -#endif +#else // HAVE_OPENGL +# define NO_OPENGL_SUPPORT_ERROR CV_ErrorNoReturn(cv::Error::StsBadFunc, "OpenCV was build without OpenGL support") +#endif // HAVE_OPENGL using namespace cv; using namespace cv::cuda; @@ -1572,3 +1574,264 @@ void cv::ogl::render(const ogl::Arrays& arr, InputArray indices, int mode, Scala } #endif } + +//////////////////////////////////////////////////////////////////////// +// CL-GL Interoperability + +#ifdef HAVE_OPENCL +# include "opencv2/core/opencl/runtime/opencl_gl.hpp" +#else // HAVE_OPENCL +# define NO_OPENCL_SUPPORT_ERROR CV_ErrorNoReturn(cv::Error::StsBadFunc, "OpenCV was build without OpenCL support") +#endif // HAVE_OPENCL + +#if defined(HAVE_OPENGL) +# if defined(ANDROID) +# include +# elif defined(__linux__) +# include +# endif +#endif // HAVE_OPENGL + +namespace cv { namespace ogl { + +namespace ocl { + +Context& initializeContextFromGL() +{ +#if !defined(HAVE_OPENGL) + NO_OPENGL_SUPPORT_ERROR; +#elif !defined(HAVE_OPENCL) + NO_OPENCL_SUPPORT_ERROR; +#else + cl_uint numPlatforms; + cl_int status = clGetPlatformIDs(0, NULL, &numPlatforms); + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLInitError, "OpenCL: Can't get number of platforms"); + if (numPlatforms == 0) + CV_Error(cv::Error::OpenCLInitError, "OpenCL: No available platforms"); + + std::vector platforms(numPlatforms); + status = clGetPlatformIDs(numPlatforms, &platforms[0], NULL); + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLInitError, "OpenCL: Can't get number of platforms"); + + // TODO Filter platforms by name from OPENCV_OPENCL_DEVICE + + int found = -1; + cl_device_id device = NULL; + cl_context context = NULL; + + for (int i = 0; i < (int)numPlatforms; i++) + { + // query platform extension: presence of "cl_khr_gl_sharing" extension is requred + { + AutoBuffer extensionStr; + + size_t extensionSize; + status = clGetPlatformInfo(platforms[i], CL_PLATFORM_EXTENSIONS, 0, NULL, &extensionSize); + if (status == CL_SUCCESS) + { + extensionStr.allocate(extensionSize+1); + status = clGetPlatformInfo(platforms[i], CL_PLATFORM_EXTENSIONS, extensionSize, (char*)extensionStr, NULL); + } + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLInitError, "OpenCL: Can't get platform extension string"); + + if (!strstr((const char*)extensionStr, "cl_khr_gl_sharing")) + continue; + } + + clGetGLContextInfoKHR_fn clGetGLContextInfoKHR = (clGetGLContextInfoKHR_fn) + clGetExtensionFunctionAddressForPlatform(platforms[i], "clGetGLContextInfoKHR"); + if (!clGetGLContextInfoKHR) + continue; + + cl_context_properties properties[] = + { +#if defined(WIN32) || defined(_WIN32) + CL_CONTEXT_PLATFORM, (cl_context_properties)platforms[i], + CL_GL_CONTEXT_KHR, (cl_context_properties)wglGetCurrentContext(), + CL_WGL_HDC_KHR, (cl_context_properties)wglGetCurrentDC(), +#elif defined(ANDROID) + CL_CONTEXT_PLATFORM, (cl_context_properties)platforms[i], + CL_GL_CONTEXT_KHR, (cl_context_properties)eglGetCurrentContext(), + CL_EGL_DISPLAY_KHR, (cl_context_properties)eglGetCurrentDisplay(), +#elif defined(__linux__) + CL_CONTEXT_PLATFORM, (cl_context_properties)platforms[i], + CL_GL_CONTEXT_KHR, (cl_context_properties)glXGetCurrentContext(), + CL_GLX_DISPLAY_KHR, (cl_context_properties)glXGetCurrentDisplay(), +#endif + 0 + }; + + // query device + device = NULL; + size_t numDevices = 0; + { + size_t sizeDevices = 0; + status = clGetGLContextInfoKHR(properties, CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR, 0, NULL, &sizeDevices); + if (status == CL_SUCCESS) + { + numDevices = sizeDevices / sizeof(cl_device_id); + if (numDevices > 0) + { + numDevices = 1; + sizeDevices = numDevices * sizeof(cl_device_id); + status = clGetGLContextInfoKHR(properties, CL_CURRENT_DEVICE_FOR_GL_CONTEXT_KHR, sizeDevices, (void*)&device, NULL); + } + } + if (status != CL_SUCCESS) + continue; + } + + // create context + if (numDevices > 0) + { + context = clCreateContext(properties, 1, &device, NULL, NULL, &status); + if (status != CL_SUCCESS) + { + clReleaseDevice(device); + } + else + { + found = i; + break; + } + } + } + + if (found < 0) + CV_Error(cv::Error::OpenCLInitError, "OpenCL: Can't create context for OpenGL interop"); + + Context& ctx = Context::getDefault(false); + initializeContextFromHandle(ctx, platforms[found], context, device); + return ctx; +#endif +} + +} // namespace cv::ogl::ocl + +#if defined(HAVE_OPENGL) && defined(HAVE_OPENCL) +static void __OpenCLinitializeGL() +{ +// temp stub +} +#endif // defined(HAVE_OPENGL) && defined(HAVE_OPENCL) + +void convertToGLTexture2D(InputArray src, Texture2D& texture) +{ + (void)src; (void)texture; +#if !defined(HAVE_OPENGL) + NO_OPENGL_SUPPORT_ERROR; +#elif !defined(HAVE_OPENCL) + NO_OPENCL_SUPPORT_ERROR; +#else + // ... // check "interop available" flag first (i.e. sharegroup existence) + __OpenCLinitializeGL(); + + Size srcSize = src.size(); + CV_Assert(srcSize.width == (int)texture.cols() && srcSize.height == (int)texture.rows()); + + using namespace cv::ocl; + Context& ctx = Context::getDefault(); + cl_context context = (cl_context)ctx.ptr(); + + UMat u = src.getUMat(); + + // TODO Add support for roi + CV_Assert(u.offset == 0); + CV_Assert(u.isContinuous()); + + cl_int status = 0; + cl_mem clImage = clCreateFromGLTexture(context, CL_MEM_WRITE_ONLY, gl::TEXTURE_2D, 0, texture.texId(), &status); + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clCreateFromGLTexture failed"); + + cl_mem clBuffer = (cl_mem)u.handle(ACCESS_READ); + + cl_command_queue q = (cl_command_queue)Queue::getDefault().ptr(); + status = clEnqueueAcquireGLObjects(q, 1, &clImage, 0, NULL, NULL); + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueAcquireGLObjects failed"); + size_t offset = 0; // TODO + size_t dst_origin[3] = {0, 0, 0}; + size_t region[3] = {u.cols, u.rows, 1}; + status = clEnqueueCopyBufferToImage(q, clBuffer, clImage, offset, dst_origin, region, 0, NULL, NULL); + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueCopyBufferToImage failed"); + status = clEnqueueReleaseGLObjects(q, 1, &clImage, 0, NULL, NULL); + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueReleaseGLObjects failed"); + + status = clFinish(q); // TODO Use events + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clFinish failed"); + + status = clReleaseMemObject(clImage); // TODO RAII + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clReleaseMemObject failed"); +#endif +} + +void convertFromGLTexture2D(const Texture2D& texture, OutputArray dst) +{ + (void)texture; (void)dst; +#if !defined(HAVE_OPENGL) + NO_OPENGL_SUPPORT_ERROR; +#elif !defined(HAVE_OPENCL) + NO_OPENCL_SUPPORT_ERROR; +#else + // ... // check "interop available" flag first (i.e. sharegroup existence) + __OpenCLinitializeGL(); + + // check texture format + const int dtype = CV_8UC4; + CV_Assert(texture.format() == Texture2D::RGBA); + + int textureType = dtype; + CV_Assert(textureType >= 0); + + using namespace cv::ocl; + Context& ctx = Context::getDefault(); + cl_context context = (cl_context)ctx.ptr(); + + // TODO Need to specify ACCESS_WRITE here somehow to prevent useless data copying! + dst.create(texture.size(), textureType); + UMat u = dst.getUMat(); + + // TODO Add support for roi + CV_Assert(u.offset == 0); + CV_Assert(u.isContinuous()); + + cl_int status = 0; + cl_mem clImage = clCreateFromGLTexture(context, CL_MEM_READ_ONLY, gl::TEXTURE_2D, 0, texture.texId(), &status); + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clCreateFromGLTexture failed"); + + cl_mem clBuffer = (cl_mem)u.handle(ACCESS_READ); + + cl_command_queue q = (cl_command_queue)Queue::getDefault().ptr(); + status = clEnqueueAcquireGLObjects(q, 1, &clImage, 0, NULL, NULL); + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueAcquireGLObjects failed"); + size_t offset = 0; // TODO + size_t src_origin[3] = {0, 0, 0}; + size_t region[3] = {u.cols, u.rows, 1}; + status = clEnqueueCopyImageToBuffer(q, clImage, clBuffer, src_origin, region, offset, 0, NULL, NULL); + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueCopyImageToBuffer failed"); + status = clEnqueueReleaseGLObjects(q, 1, &clImage, 0, NULL, NULL); + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueReleaseGLObjects failed"); + + status = clFinish(q); // TODO Use events + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clFinish failed"); + + status = clReleaseMemObject(clImage); // TODO RAII + if (status != CL_SUCCESS) + CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clReleaseMemObject failed"); +#endif +} + +}} // namespace cv::ogl diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index ef6cd772f4..31d7d8021a 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -18,6 +18,10 @@ if(WIN32 AND HAVE_DIRECTX) add_subdirectory(directx) endif() +if((NOT ANDROID) AND HAVE_OPENGL) + add_subdirectory(opengl) +endif() + if(ANDROID AND BUILD_ANDROID_EXAMPLES) add_subdirectory(android) endif() diff --git a/samples/opengl/CMakeLists.txt b/samples/opengl/CMakeLists.txt new file mode 100644 index 0000000000..4bf48925e3 --- /dev/null +++ b/samples/opengl/CMakeLists.txt @@ -0,0 +1,45 @@ +SET(OPENCV_OPENGL_SAMPLES_REQUIRED_DEPS opencv_core opencv_imgproc opencv_imgcodecs opencv_videoio opencv_highgui) + +ocv_check_dependencies(${OPENCV_OPENGL_SAMPLES_REQUIRED_DEPS}) + +if(BUILD_EXAMPLES AND OCV_DEPENDENCIES_FOUND) + set(project "opengl") + string(TOUPPER "${project}" project_upper) + + project("${project}_samples") + + ocv_include_modules_recurse(${OPENCV_OPENGL_SAMPLES_REQUIRED_DEPS}) + + # --------------------------------------------- + # Define executable targets + # --------------------------------------------- + MACRO(OPENCV_DEFINE_OPENGL_EXAMPLE name srcs) + set(the_target "example_${project}_${name}") + add_executable(${the_target} ${srcs}) + + ocv_target_link_libraries(${the_target} ${OPENCV_LINKER_LIBS} ${OPENCV_OPENGL_SAMPLES_REQUIRED_DEPS}) + + set_target_properties(${the_target} PROPERTIES + OUTPUT_NAME "${project}-example-${name}" + PROJECT_LABEL "(EXAMPLE_${project_upper}) ${name}") + + if(ENABLE_SOLUTION_FOLDERS) + set_target_properties(${the_target} PROPERTIES FOLDER "samples//${project}") + endif() + + if(WIN32) + if(MSVC AND NOT BUILD_SHARED_LIBS) + set_target_properties(${the_target} PROPERTIES LINK_FLAGS "/NODEFAULTLIB:atlthunk.lib /NODEFAULTLIB:atlsd.lib /DEBUG") + endif() + install(TARGETS ${the_target} RUNTIME DESTINATION "${OPENCV_SAMPLES_BIN_INSTALL_PATH}/${project}" COMPONENT samples) + endif() + ENDMACRO() + + file(GLOB all_samples RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp) + + foreach(sample_filename ${all_samples}) + get_filename_component(sample ${sample_filename} NAME_WE) + file(GLOB sample_srcs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${sample}.*) + OPENCV_DEFINE_OPENGL_EXAMPLE(${sample} ${sample_srcs}) + endforeach() +endif() diff --git a/samples/opengl/opengl_interop.cpp b/samples/opengl/opengl_interop.cpp new file mode 100644 index 0000000000..9cedb8ef4c --- /dev/null +++ b/samples/opengl/opengl_interop.cpp @@ -0,0 +1,454 @@ +#if defined(WIN32) || defined(_WIN32) +# define WIN32_LEAN_AND_MEAN +# include +#elif defined(__linux__) +# include +# include +#endif + +#include +#include +#include + +#include + +#include "opencv2/core.hpp" +#include "opencv2/core/opengl.hpp" +#include "opencv2/core/ocl.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/videoio.hpp" + +#include "winapp.hpp" + +#if defined(WIN32) || defined(_WIN32) +# pragma comment(lib, "opengl32.lib") +# pragma comment(lib, "glu32.lib") +#endif + +class GLWinApp : public WinApp +{ +public: + GLWinApp(int width, int height, std::string& window_name, cv::VideoCapture& cap) : + WinApp(width, height, window_name) + { + m_shutdown = false; + m_mode = 0; + m_modeStr[0] = cv::String("No processing"); + m_modeStr[1] = cv::String("Processing on CPU"); + m_modeStr[2] = cv::String("Processing on GPU"); + m_disableProcessing = false; + m_cap = cap; + } + + ~GLWinApp() {} + + int onClose(void) + { + m_shutdown = true; + cleanup(); +#if defined(WIN32) || defined(_WIN32) + ::DestroyWindow(m_hWnd); +#elif defined(__linux__) + glXMakeCurrent(m_display, None, NULL); + glXDestroyContext(m_display, m_glctx); + XDestroyWindow(m_display, m_window); + XCloseDisplay(m_display); +#endif + return 0; + } + +#if defined(WIN32) || defined(_WIN32) + virtual LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) + { + switch (message) + { + case WM_CHAR: + if (wParam >= '0' && wParam <= '2') + { + m_mode = (char)wParam - '0'; + return 0; + } + else if (wParam == VK_SPACE) + { + m_disableProcessing = !m_disableProcessing; + return 0; + } + else if (wParam == VK_ESCAPE) + { + return onClose(); + } + break; + + case WM_CLOSE: + return onClose(); + + case WM_DESTROY: + ::PostQuitMessage(0); + return 0; + } + + return ::DefWindowProc(hWnd, message, wParam, lParam); + } +#endif + + static float getFps() + { + static std::queue time_queue; + + int64 now = cv::getTickCount(); + int64 then = 0; + time_queue.push(now); + + if (time_queue.size() >= 2) + then = time_queue.front(); + + if (time_queue.size() >= 25) + time_queue.pop(); + + return time_queue.size() * (float)cv::getTickFrequency() / (now - then); + } + +#if defined(__linux__) + int handle_event(XEvent& e) + { + switch(e.type) + { + case ClientMessage: + if ((Atom)e.xclient.data.l[0] == m_WM_DELETE_WINDOW) + { + m_end_loop = true; + onClose(); + } + else + { + return 0; + } + break; + case Expose: + render(); + break; + case KeyPress: + switch(keycode_to_keysym(e.xkey.keycode)) + { + case XK_space: + m_disableProcessing = !m_disableProcessing; + break; + case XK_0: + m_mode = 0; + break; + case XK_1: + m_mode = 1; + break; + case XK_2: + m_mode = 2; + break; + case XK_Escape: + m_end_loop = true; + onClose(); + break; + } + break; + default: + return 0; + } + return 1; + } +#endif + + int init(void) + { +#if defined(WIN32) || defined(_WIN32) + m_hDC = GetDC(m_hWnd); + + if (setup_pixel_format() != 0) + { + std::cerr << "Can't setup pixel format" << std::endl; + return -1; + } + + m_hRC = wglCreateContext(m_hDC); + wglMakeCurrent(m_hDC, m_hRC); +#elif defined(__linux__) + m_glctx = glXCreateContext(m_display, m_visual_info, NULL, GL_TRUE); + glXMakeCurrent(m_display, m_window, m_glctx); +#endif + + glEnable(GL_TEXTURE_2D); + glEnable(GL_DEPTH_TEST); + + glViewport(0, 0, m_width, m_height); + + if (cv::ocl::haveOpenCL()) + { + m_oclCtx = cv::ogl::ocl::initializeContextFromGL(); + } + + m_oclDevName = cv::ocl::useOpenCL() ? + cv::ocl::Context::getDefault().device(0).name() : + (char*) "No OpenCL device"; + + return 0; + } // init() + + int get_texture(cv::ogl::Texture2D& texture) + { + if (!m_cap.read(m_frame_bgr)) + return -1; + + cv::cvtColor(m_frame_bgr, m_frame_rgba, CV_RGB2RGBA); + + texture.copyFrom(m_frame_rgba); + + return 0; + } + + void print_info(int mode, float fps, cv::String oclDevName) + { +#if defined(WIN32) || defined(_WIN32) + HDC hDC = m_hDC; + + HFONT hFont = (HFONT)::GetStockObject(SYSTEM_FONT); + + HFONT hOldFont = (HFONT)::SelectObject(hDC, hFont); + + if (hOldFont) + { + TEXTMETRIC tm; + ::GetTextMetrics(hDC, &tm); + + char buf[256+1]; + int y = 0; + + buf[0] = 0; + sprintf_s(buf, sizeof(buf)-1, "Mode: %s", m_modeStr[mode].c_str()); + ::TextOut(hDC, 0, y, buf, (int)strlen(buf)); + + y += tm.tmHeight; + buf[0] = 0; + sprintf_s(buf, sizeof(buf)-1, "FPS: %2.1f", fps); + ::TextOut(hDC, 0, y, buf, (int)strlen(buf)); + + y += tm.tmHeight; + buf[0] = 0; + sprintf_s(buf, sizeof(buf)-1, "OpenCL device: %s", oclDevName.c_str()); + ::TextOut(hDC, 0, y, buf, (int)strlen(buf)); + + ::SelectObject(hDC, hOldFont); + } +#elif defined(__linux__) + + char buf[256+1]; + snprintf(buf, sizeof(buf)-1, "FPS: %2.1f Mode: %s Device: %s", fps, m_modeStr[mode].c_str(), oclDevName.c_str()); + XStoreName(m_display, m_window, buf); +#endif + } + + void idle() + { + render(); + } + + int render() + { + try + { + if (m_shutdown) + return 0; + + int r; + cv::ogl::Texture2D texture; + + r = get_texture(texture); + if (r != 0) + { + return -1; + } + + switch (m_mode) + { + case 0: + // no processing + break; + + case 1: + { + // process video frame on CPU + cv::Mat m(m_height, m_width, CV_8UC4); + + texture.copyTo(m); + if (!m_disableProcessing) + { + // blur texture image with OpenCV on CPU + cv::blur(m, m, cv::Size(15, 15), cv::Point(-7, -7)); + } + texture.copyFrom(m); + + break; + } + + case 2: + { + // process video frame on GPU + cv::UMat u; + + cv::ogl::convertFromGLTexture2D(texture, u); + if (!m_disableProcessing) + { + // blur texture image with OpenCV on GPU with OpenCL + cv::blur(u, u, cv::Size(15, 15), cv::Point(-7, -7)); + } + cv::ogl::convertToGLTexture2D(u, texture); + + break; + } + + } // switch + +#if defined(__linux__) + XWindowAttributes window_attributes; + XGetWindowAttributes(m_display, m_window, &window_attributes); + glViewport(0, 0, window_attributes.width, window_attributes.height); +#endif + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + glEnable(GL_TEXTURE_2D); + + texture.bind(); + + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 0.1f); + glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 0.1f); + glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, -1.0f, 0.1f); + glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, 0.1f); + glEnd(); + +#if defined(WIN32) || defined(_WIN32) + SwapBuffers(m_hDC); +#elif defined(__linux__) + glXSwapBuffers(m_display, m_window); +#endif + + print_info(m_mode, getFps(), m_oclDevName); + } + + + catch (cv::Exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 10; + } + + return 0; + } + + int cleanup(void) + { + return 0; + } + +protected: + +#if defined(WIN32) || defined(_WIN32) + int setup_pixel_format() + { + PIXELFORMATDESCRIPTOR pfd; + + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cRedBits = 8; + pfd.cRedShift = 0; + pfd.cGreenBits = 8; + pfd.cGreenShift = 0; + pfd.cBlueBits = 8; + pfd.cBlueShift = 0; + pfd.cAlphaBits = 8; + pfd.cAlphaShift = 0; + pfd.cAccumBits = 0; + pfd.cAccumRedBits = 0; + pfd.cAccumGreenBits = 0; + pfd.cAccumBlueBits = 0; + pfd.cAccumAlphaBits = 0; + pfd.cDepthBits = 24; + pfd.cStencilBits = 8; + pfd.cAuxBuffers = 0; + pfd.iLayerType = PFD_MAIN_PLANE; + pfd.bReserved = 0; + pfd.dwLayerMask = 0; + pfd.dwVisibleMask = 0; + pfd.dwDamageMask = 0; + + int pfmt = ChoosePixelFormat(m_hDC, &pfd); + if (pfmt == 0) + return -1; + if (SetPixelFormat(m_hDC, pfmt, &pfd) == 0) + return -2; + return 0; + } +#endif + +#if defined(__linux__) + KeySym keycode_to_keysym(unsigned keycode) + { // note that XKeycodeToKeysym() is considered deprecated + int keysyms_per_keycode_return = 0; + KeySym *keysyms = XGetKeyboardMapping(m_display, keycode, 1, &keysyms_per_keycode_return); + KeySym keysym = keysyms[0]; + XFree(keysyms); + return keysym; + } +#endif + +private: + bool m_shutdown; + int m_mode; + cv::String m_modeStr[3]; + int m_disableProcessing; +#if defined(WIN32) || defined(_WIN32) + HDC m_hDC; + HGLRC m_hRC; +#elif defined(__linux__) + GLXContext m_glctx; +#endif + cv::VideoCapture m_cap; + cv::Mat m_frame_bgr; + cv::Mat m_frame_rgba; + cv::ocl::Context m_oclCtx; + cv::String m_oclDevName; +}; + +using namespace cv; + +int main(int argc, char** argv) +{ + cv::VideoCapture cap; + + if (argc > 1) + cap.open(argv[1]); + else + cap.open(0); + + int width = (int)cap.get(CAP_PROP_FRAME_WIDTH); + int height = (int)cap.get(CAP_PROP_FRAME_HEIGHT); + std::string wndname = "WGL Window"; + + GLWinApp app(width, height, wndname, cap); + + try + { + app.create(); + return app.run(); + } + catch (cv::Exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 10; + } + catch (...) + { + std::cerr << "FATAL ERROR: Unknown exception" << std::endl; + return 11; + } +} diff --git a/samples/opengl/winapp.hpp b/samples/opengl/winapp.hpp new file mode 100644 index 0000000000..233d71f5b5 --- /dev/null +++ b/samples/opengl/winapp.hpp @@ -0,0 +1,211 @@ +#if defined(WIN32) || defined(_WIN32) +# define WIN32_LEAN_AND_MEAN +# include +#elif defined(__linux__) +# include +# include +# include +#endif + +#include + +#include +#if defined(WIN32) || defined(_WIN32) +# include +#elif defined(__linux__) +# include +#endif + +#if defined(WIN32) || defined(_WIN32) +# define WINCLASS "WinAppWnd" +#endif + +#define SAFE_RELEASE(p) if (p) { p->Release(); p = NULL; } + +class WinApp +{ +public: + WinApp(int width, int height, std::string& window_name) + { + m_width = width; + m_height = height; + m_window_name = window_name; +#if defined(WIN32) || defined(_WIN32) + m_hInstance = ::GetModuleHandle(NULL); +#endif + } + + virtual ~WinApp() + { +#if defined(WIN32) || defined(_WIN32) + ::UnregisterClass(WINCLASS, m_hInstance); +#endif + } + + int create() + { +#if defined(WIN32) || defined(_WIN32) + WNDCLASSEX wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = &WinApp::StaticWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = m_hInstance; + wcex.hIcon = LoadIcon(0, IDI_APPLICATION); + wcex.hCursor = LoadCursor(0, IDC_ARROW); + wcex.hbrBackground = 0; + wcex.lpszMenuName = 0L; + wcex.lpszClassName = WINCLASS; + wcex.hIconSm = 0; + + ATOM wc = ::RegisterClassEx(&wcex); + + RECT rc = { 0, 0, m_width, m_height }; + ::AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, false); + + m_hWnd = ::CreateWindow( + (LPCTSTR)wc, m_window_name.c_str(), + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, + rc.right - rc.left, rc.bottom - rc.top, + NULL, NULL, m_hInstance, (void*)this); + + if (!m_hWnd) + return -1; + + ::ShowWindow(m_hWnd, SW_SHOW); + ::UpdateWindow(m_hWnd); + ::SetFocus(m_hWnd); +#elif defined(__linux__) + m_display = XOpenDisplay(NULL); + + if (m_display == NULL) + { + return -1; + } + + m_WM_DELETE_WINDOW = XInternAtom(m_display, "WM_DELETE_WINDOW", False); + + static GLint visual_attributes[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None }; + m_visual_info = glXChooseVisual(m_display, 0, visual_attributes); + + if (m_visual_info == NULL) + { + XCloseDisplay(m_display); + return -2; + } + + Window root = DefaultRootWindow(m_display); + + m_event_mask = ExposureMask | KeyPressMask; + + XSetWindowAttributes window_attributes; + window_attributes.colormap = XCreateColormap(m_display, root, m_visual_info->visual, AllocNone); + window_attributes.event_mask = m_event_mask; + + m_window = XCreateWindow( + m_display, root, 0, 0, m_width, m_height, 0, m_visual_info->depth, + InputOutput, m_visual_info->visual, CWColormap | CWEventMask, &window_attributes); + + XMapWindow(m_display, m_window); + XSetWMProtocols(m_display, m_window, &m_WM_DELETE_WINDOW, 1); + XStoreName(m_display, m_window, m_window_name.c_str()); +#endif + + return init(); + } + +#if defined(WIN32) || defined(_WIN32) + virtual LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) = 0; +#endif + + int run() + { +#if defined(WIN32) || defined(_WIN32) + MSG msg; + + ::ZeroMemory(&msg, sizeof(msg)); + + while (msg.message != WM_QUIT) + { + if (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) + { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + else + { + idle(); + } + } + + return static_cast(msg.wParam); +#elif defined(__linux__) + m_end_loop = false; + + do { + XEvent e; + + if (!XCheckWindowEvent(m_display, m_window, m_event_mask, &e) || !handle_event(e)) + { + idle(); + } + } while (!m_end_loop); +#endif + + return 0; + } + +protected: + +#if defined(WIN32) || defined(_WIN32) + static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) + { + WinApp* pWnd; + + if (message == WM_NCCREATE) + { + LPCREATESTRUCT pCreateStruct = ((LPCREATESTRUCT)lParam); + pWnd = (WinApp*)(pCreateStruct->lpCreateParams); + ::SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pWnd); + } + + pWnd = GetObjectFromWindow(hWnd); + + if (pWnd) + return pWnd->WndProc(hWnd, message, wParam, lParam); + else + return ::DefWindowProc(hWnd, message, wParam, lParam); + } + + inline static WinApp* GetObjectFromWindow(HWND hWnd) + { + return (WinApp*)::GetWindowLongPtr(hWnd, GWLP_USERDATA); + } +#endif + +#if defined(__linux__) + virtual int handle_event(XEvent& e) = 0; +#endif + + virtual int init() = 0; + virtual int render() = 0; + + virtual void idle() = 0; + +#if defined(WIN32) || defined(_WIN32) + HINSTANCE m_hInstance; + HWND m_hWnd; +#elif defined(__linux__) + Display* m_display; + XVisualInfo* m_visual_info; + Window m_window; + long m_event_mask; + Atom m_WM_DELETE_WINDOW; + bool m_end_loop; +#endif + int m_width; + int m_height; + std::string m_window_name; +}; From 6803d1ed2808344b810c0ca4d4cd5e79f1a9db3d Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Fri, 26 Jun 2015 14:49:31 +0200 Subject: [PATCH 068/133] Support non continuous, BORDER_REPLICATE TODO: HAL-accelerated code --- modules/imgproc/include/opencv2/imgproc.hpp | 4 +- modules/imgproc/perf/perf_spatialgradient.cpp | 12 +- modules/imgproc/src/spatialgradient.cpp | 160 +++++++----------- modules/imgproc/test/test_filter.cpp | 16 +- 4 files changed, 82 insertions(+), 110 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 947dfc3029..baa81cf0ba 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1382,12 +1382,14 @@ Sobel( src, dy, CV_16SC1, 0, 1, 3 ); @param dx output image with first-order derivative in x. @param dy output image with first-order derivative in y. @param ksize size of Sobel kernel. It must be 3. +@param borderType pixel extrapolation method, see cv::BorderTypes @sa Sobel */ CV_EXPORTS_W void spatialGradient( InputArray src, OutputArray dx, - OutputArray dy, int ksize = 3 ); + OutputArray dy, int ksize = 3, + int borderType = BORDER_DEFAULT ); /** @brief Calculates the first x- or y- image derivative using Scharr operator. diff --git a/modules/imgproc/perf/perf_spatialgradient.cpp b/modules/imgproc/perf/perf_spatialgradient.cpp index 87456146de..84d41e1dc2 100644 --- a/modules/imgproc/perf/perf_spatialgradient.cpp +++ b/modules/imgproc/perf/perf_spatialgradient.cpp @@ -7,18 +7,20 @@ using namespace testing; using std::tr1::make_tuple; using std::tr1::get; -typedef std::tr1::tuple Size_Ksize_t; -typedef perf::TestBaseWithParam Size_Ksize; +typedef std::tr1::tuple Size_Ksize_BorderType_t; +typedef perf::TestBaseWithParam Size_Ksize_BorderType; -PERF_TEST_P( Size_Ksize, spatialGradient, +PERF_TEST_P( Size_Ksize_BorderType, spatialGradient, Combine( SZ_ALL_HD, - Values( 3 ) + Values( 3 ), + Values( BORDER_DEFAULT ) ) ) { Size size = std::tr1::get<0>(GetParam()); int ksize = std::tr1::get<1>(GetParam()); + int borderType = std::tr1::get<2>(GetParam()); Mat src(size, CV_8UC1); Mat dx(size, CV_16SC1); @@ -26,7 +28,7 @@ PERF_TEST_P( Size_Ksize, spatialGradient, declare.in(src, WARMUP_RNG).out(dx, dy); - TEST_CYCLE() spatialGradient(src, dx, dy, ksize); + TEST_CYCLE() spatialGradient(src, dx, dy, ksize, borderType); SANITY_CHECK(dx); SANITY_CHECK(dy); diff --git a/modules/imgproc/src/spatialgradient.cpp b/modules/imgproc/src/spatialgradient.cpp index e87869a4ea..ac209af17c 100644 --- a/modules/imgproc/src/spatialgradient.cpp +++ b/modules/imgproc/src/spatialgradient.cpp @@ -46,128 +46,73 @@ namespace cv { -void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksize ) +void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, + int ksize, int borderType ) { // Prepare InputArray src Mat src = _src.getMat(); CV_Assert( !src.empty() ); - CV_Assert( src.isContinuous() ); CV_Assert( src.type() == CV_8UC1 ); + CV_Assert( borderType == BORDER_DEFAULT || borderType == BORDER_REPLICATE ); // Prepare OutputArrays dx, dy _dx.create( src.size(), CV_16SC1 ); _dy.create( src.size(), CV_16SC1 ); Mat dx = _dx.getMat(), dy = _dy.getMat(); - CV_Assert( dx.isContinuous() ); - CV_Assert( dy.isContinuous() ); // TODO: Allow for other kernel sizes CV_Assert(ksize == 3); - // Reference - //Sobel( src, dx, CV_16SC1, 1, 0, ksize ); - //Sobel( src, dy, CV_16SC1, 0, 1, ksize ); - // Get dimensions - int H = src.rows, - W = src.cols, - N = H * W; - - // Get raw pointers to input/output data - uchar* p_src = src.ptr(0); - short* p_dx = dx.ptr(0); - short* p_dy = dy.ptr(0); + const int H = src.rows, + W = src.cols; // Row, column indices int i, j; - /* NOTE: - * - * Sobel-x: -1 0 1 - * -2 0 2 - * -1 0 1 - * - * Sobel-y: -1 -2 -1 - * 0 0 0 - * 1 2 1 - */ + // Store pointers to rows of input/output data + // Padded by two rows for border handling + uchar* P_src[H+2]; + short* P_dx [H+2]; + short* P_dy [H+2]; - // No-SSE - int idx; + int i_top = 0, // Case for H == 1 && W == 1 && BORDER_REPLICATE + i_bottom = H - 1, + j_offl = 0, // j offset from 0th pixel to reach -1st pixel + j_offr = 0; // j offset from W-1th pixel to reach Wth pixel - p_dx[0] = 0; // Top-left corner - p_dy[0] = 0; - p_dx[W-1] = 0; // Top-right corner - p_dy[W-1] = 0; - p_dx[N-1] = 0; // Bottom-right corner - p_dy[N-1] = 0; - p_dx[N-W] = 0; // Bottom-left corner - p_dy[N-W] = 0; - - // Handle special case: column matrix - if ( W == 1 ) + if ( borderType == BORDER_DEFAULT ) // Equiv. to BORDER_REFLECT_101 { - for ( i = 1; i < H - 1; i++ ) + if ( H > 1 ) { - p_dx[i] = 0; - p_dy[i] = 4*(p_src[i + 1] - p_src[i - 1]); // Should be 2?! 4 makes tests pass + i_top = 1; + i_bottom = H - 2; } - return; - } - - // Handle special case: row matrix - if ( H == 1 ) - { - for ( j = 1; j < W - 1; j++ ) + if ( W > 1 ) { - p_dx[j] = 4*(p_src[j + 1] - p_src[j - 1]); // Should be 2?! 4 makes tests pass - p_dy[j] = 0; + j_offl = 1; + j_offr = -1; } - return; } - // Do top row - for ( j = 1; j < W - 1; j++ ) + P_src[0] = src.ptr(i_top); // Mirrored top border + P_src[H+1] = src.ptr(i_bottom); // Mirrored bottom border + + for ( i = 0; i < H; i++ ) { - idx = j; - p_dx[idx] = -(p_src[idx+W-1] + 2*p_src[idx-1] + p_src[idx+W-1]) + - (p_src[idx+W+1] + 2*p_src[idx+1] + p_src[idx+W+1]); - p_dy[idx] = 0; + P_src[i+1] = src.ptr(i); + P_dx [i] = dx.ptr(i); + P_dy [i] = dy.ptr(i); } - // Do right column - idx = 2*W - 1; - for ( i = 1; i < H - 1; i++ ) - { - p_dx[idx] = 0; - p_dy[idx] = -(p_src[idx-W-1] + 2*p_src[idx-W] + p_src[idx-W-1]) + - (p_src[idx+W-1] + 2*p_src[idx+W] + p_src[idx+W-1]); - idx += W; - } + // Pointer to row vectors + uchar *p_src, *c_src, *n_src; // previous, current, next row + short *c_dx, *c_dy; - // Do bottom row - idx = N - W + 1; - for ( j = 1; j < W - 1; j++ ) - { - p_dx[idx] = -(p_src[idx-W-1] + 2*p_src[idx-1] + p_src[idx-W-1]) + - (p_src[idx-W+1] + 2*p_src[idx+1] + p_src[idx-W+1]); - p_dy[idx] = 0; - idx++; - } - - // Do left column - idx = W; - for ( i = 1; i < H - 1; i++ ) - { - p_dx[idx] = 0; - p_dy[idx] = -(p_src[idx-W+1] + 2*p_src[idx-W] + p_src[idx-W+1]) + - (p_src[idx+W+1] + 2*p_src[idx+W] + p_src[idx+W+1]); - idx += W; - } - - // Do Inner area + int j_start = 0; +/* #if CV_SIMD128 // Characters in variable names have the following meanings: // u: unsigned char @@ -260,16 +205,39 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, int ksi } } #else - for ( i = 1; i < H - 1; i++ ) - for ( j = 1; j < W - 1; j++ ) +*/ + + /* NOTE: + * + * Sobel-x: -1 0 1 + * -2 0 2 + * -1 0 1 + * + * Sobel-y: -1 -2 -1 + * 0 0 0 + * 1 2 1 + */ + int j_p, j_n; + for ( i = 0; i < H; i++ ) { - idx = i*W + j; - p_dx[idx] = -(p_src[idx-W-1] + 2*p_src[idx-1] + p_src[idx+W-1]) + - (p_src[idx-W+1] + 2*p_src[idx+1] + p_src[idx+W+1]); - p_dy[idx] = -(p_src[idx-W-1] + 2*p_src[idx-W] + p_src[idx-W+1]) + - (p_src[idx+W-1] + 2*p_src[idx+W] + p_src[idx+W+1]); + p_src = P_src[i]; c_src = P_src[i+1]; n_src = P_src[i+2]; + c_dx = P_dx [i]; + c_dy = P_dy [i]; + + for ( j = j_start; j < W; j++ ) + { + j_p = j - 1; + j_n = j + 1; + if ( j_p < 0 ) j_p = j + j_offl; + if ( j_n >= W ) j_n = j + j_offr; + + c_dx[j] = -(p_src[j_p] + c_src[j_p] + c_src[j_p] + n_src[j_p]) + + (p_src[j_n] + c_src[j_n] + c_src[j_n] + n_src[j_n]); + c_dy[j] = -(p_src[j_p] + p_src[j] + p_src[j] + p_src[j_n]) + + (n_src[j_p] + n_src[j] + n_src[j] + n_src[j_n]); + } } -#endif +//#endif } diff --git a/modules/imgproc/test/test_filter.cpp b/modules/imgproc/test/test_filter.cpp index 0c98ed35c5..5994b1b11b 100644 --- a/modules/imgproc/test/test_filter.cpp +++ b/modules/imgproc/test/test_filter.cpp @@ -587,19 +587,17 @@ void CV_SpatialGradientTest::get_test_array_types_and_sizes( int test_case_idx, // Outputs are only CV_16SC1 for now types[OUTPUT][0] = types[OUTPUT][1] = types[REF_OUTPUT][0] - = types[REF_OUTPUT][1] = CV_16SC1; + = types[REF_OUTPUT][1] = CV_16SC1; ksize = 3; + border = BORDER_DEFAULT; // TODO: Add BORDER_REPLICATE } void CV_SpatialGradientTest::run_func() { - Mat dx, dy; - spatialGradient( test_mat[INPUT][0].clone(), dx, dy, ksize ); - - test_mat[OUTPUT][0] = dx; - test_mat[OUTPUT][1] = dy; + spatialGradient( test_mat[INPUT][0], test_mat[OUTPUT][0], + test_mat[OUTPUT][1], ksize, border ); } void CV_SpatialGradientTest::prepare_to_validation( int /*test_case_idx*/ ) @@ -607,10 +605,12 @@ void CV_SpatialGradientTest::prepare_to_validation( int /*test_case_idx*/ ) int dx, dy; dx = 1; dy = 0; - Sobel( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], CV_16SC1, dx, dy, ksize ); + Sobel( test_mat[INPUT][0], test_mat[REF_OUTPUT][0], CV_16SC1, dx, dy, ksize, + 1, 0, border ); dx = 0; dy = 1; - Sobel( test_mat[INPUT][0], test_mat[REF_OUTPUT][1], CV_16SC1, dx, dy, ksize ); + Sobel( test_mat[INPUT][0], test_mat[REF_OUTPUT][1], CV_16SC1, dx, dy, ksize, + 1, 0, border ); } From 7b01e32fe8aee1a28e249e377689cd662305614a Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Fri, 26 Jun 2015 16:41:00 +0200 Subject: [PATCH 069/133] spatialGradient: HAL-accelerated TODO: Make nosse ver faster than Sobel Make sse ver faster than Sobel for BORDER_REPLICATE --- modules/imgproc/perf/perf_spatialgradient.cpp | 2 +- modules/imgproc/src/spatialgradient.cpp | 189 ++++++++++++------ 2 files changed, 128 insertions(+), 63 deletions(-) diff --git a/modules/imgproc/perf/perf_spatialgradient.cpp b/modules/imgproc/perf/perf_spatialgradient.cpp index 84d41e1dc2..0f9479abd9 100644 --- a/modules/imgproc/perf/perf_spatialgradient.cpp +++ b/modules/imgproc/perf/perf_spatialgradient.cpp @@ -14,7 +14,7 @@ PERF_TEST_P( Size_Ksize_BorderType, spatialGradient, Combine( SZ_ALL_HD, Values( 3 ), - Values( BORDER_DEFAULT ) + Values( BORDER_DEFAULT, BORDER_REPLICATE ) ) ) { diff --git a/modules/imgproc/src/spatialgradient.cpp b/modules/imgproc/src/spatialgradient.cpp index ac209af17c..056adc5752 100644 --- a/modules/imgproc/src/spatialgradient.cpp +++ b/modules/imgproc/src/spatialgradient.cpp @@ -111,8 +111,8 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, uchar *p_src, *c_src, *n_src; // previous, current, next row short *c_dx, *c_dy; + int i_start = 0; int j_start = 0; -/* #if CV_SIMD128 // Characters in variable names have the following meanings: // u: unsigned char @@ -123,90 +123,156 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, // n: offset 0 // p: offset 1 // Example: umn is offset -1 in row and offset 0 in column + uchar tmp; v_uint8x16 v_um, v_un, v_up; v_uint16x8 v_um1, v_um2, v_un1, v_un2, v_up1, v_up2; - v_int16x8 v_smm1, v_smm2, v_smn1, v_smn2, v_smp1, v_smp2, - v_snm1, v_snm2, v_snn1, v_snn2, v_snp1, v_snp2, - v_spm1, v_spm2, v_spn1, v_spn2, v_spp1, v_spp2, - v_two = v_setall_s16(2), - v_sdx1, v_sdx2, v_sdy1, v_sdy2; + v_int16x8 v_s1m1, v_s1m2, v_s1n1, v_s1n2, v_s1p1, v_s1p2, + v_s2m1, v_s2m2, v_s2n1, v_s2n2, v_s2p1, v_s2p2, + v_s3m1, v_s3m2, v_s3n1, v_s3n2, v_s3p1, v_s3p2, + v_s4m1, v_s4m2, v_s4n1, v_s4n2, v_s4p1, v_s4p2, + v_tmp, v_sdx1, v_sdx2, v_sdy1, v_sdy2; - for ( i = 1; i < H - 1; i++ ) + uchar *m_src; + short *c_dx1, *c_dx2, *c_dy1, *c_dy2; + for ( i = 0; i < H - 2; i += 2 ) { + p_src = P_src[i]; c_src = P_src[i+1]; n_src = P_src[i+2]; m_src = P_src[i+3]; + c_dx1 = P_dx [i]; + c_dy1 = P_dy [i]; + c_dx2 = P_dx [i+1]; + c_dy2 = P_dy [i+1]; + // 16-column chunks at a time - for ( j = 1; j < W - 1 - 15; j += 16 ) + for ( j = 0; j < W - 15; j += 16 ) { + bool left = false, right = false; + if ( j == 0 ) left = true; + if ( j == W - 16 ) right = true; + // Load top row for 3x3 Sobel filter - idx = i*W + j; - v_um = v_load(&p_src[idx - W - 1]); - v_un = v_load(&p_src[idx - W]); - v_up = v_load(&p_src[idx - W + 1]); + if ( left ) { tmp = p_src[j-1]; p_src[j-1] = p_src[j+j_offl]; } + v_um = v_load(&p_src[j-1]); + if ( left ) p_src[j-1] = tmp; + + v_un = v_load(&p_src[j]); + + if ( right ) { tmp = p_src[j+16]; p_src[j+16] = p_src[j+15+j_offr]; } + v_up = v_load(&p_src[j+1]); + if ( right ) p_src[j+16] = tmp; + v_expand(v_um, v_um1, v_um2); v_expand(v_un, v_un1, v_un2); v_expand(v_up, v_up1, v_up2); - v_smm1 = v_reinterpret_as_s16(v_um1); - v_smm2 = v_reinterpret_as_s16(v_um2); - v_smn1 = v_reinterpret_as_s16(v_un1); - v_smn2 = v_reinterpret_as_s16(v_un2); - v_smp1 = v_reinterpret_as_s16(v_up1); - v_smp2 = v_reinterpret_as_s16(v_up2); + v_s1m1 = v_reinterpret_as_s16(v_um1); + v_s1m2 = v_reinterpret_as_s16(v_um2); + v_s1n1 = v_reinterpret_as_s16(v_un1); + v_s1n2 = v_reinterpret_as_s16(v_un2); + v_s1p1 = v_reinterpret_as_s16(v_up1); + v_s1p2 = v_reinterpret_as_s16(v_up2); // Load second row for 3x3 Sobel filter - v_um = v_load(&p_src[idx - 1]); - v_un = v_load(&p_src[idx]); - v_up = v_load(&p_src[idx + 1]); - v_expand(v_um, v_um1, v_um2); - v_expand(v_un, v_un1, v_un2); - v_expand(v_up, v_up1, v_up2); - v_snm1 = v_reinterpret_as_s16(v_um1); - v_snm2 = v_reinterpret_as_s16(v_um2); - v_snn1 = v_reinterpret_as_s16(v_un1); - v_snn2 = v_reinterpret_as_s16(v_un2); - v_snp1 = v_reinterpret_as_s16(v_up1); - v_snp2 = v_reinterpret_as_s16(v_up2); + if ( left ) { tmp = c_src[j-1]; c_src[j-1] = c_src[j+j_offl]; } + v_um = v_load(&c_src[j-1]); + if ( left ) c_src[j-1] = tmp; + + v_un = v_load(&c_src[j]); + + if ( right ) { tmp = c_src[j+16]; c_src[j+16] = c_src[j+15+j_offr]; } + v_up = v_load(&c_src[j+1]); + if ( right ) c_src[j+16] = tmp; - // Load last row for 3x3 Sobel filter - v_um = v_load(&p_src[idx + W - 1]); - v_un = v_load(&p_src[idx + W]); - v_up = v_load(&p_src[idx + W + 1]); v_expand(v_um, v_um1, v_um2); v_expand(v_un, v_un1, v_un2); v_expand(v_up, v_up1, v_up2); - v_spm1 = v_reinterpret_as_s16(v_um1); - v_spm2 = v_reinterpret_as_s16(v_um2); - v_spn1 = v_reinterpret_as_s16(v_un1); - v_spn2 = v_reinterpret_as_s16(v_un2); - v_spp1 = v_reinterpret_as_s16(v_up1); - v_spp2 = v_reinterpret_as_s16(v_up2); + v_s2m1 = v_reinterpret_as_s16(v_um1); + v_s2m2 = v_reinterpret_as_s16(v_um2); + v_s2n1 = v_reinterpret_as_s16(v_un1); + v_s2n2 = v_reinterpret_as_s16(v_un2); + v_s2p1 = v_reinterpret_as_s16(v_up1); + v_s2p2 = v_reinterpret_as_s16(v_up2); + + // Load third row for 3x3 Sobel filter + if ( left ) { tmp = n_src[j-1]; n_src[j-1] = n_src[j+j_offl]; } + v_um = v_load(&n_src[j-1]); + if ( left ) n_src[j-1] = tmp; + + v_un = v_load(&n_src[j]); + + if ( right ) { tmp = n_src[j+16]; n_src[j+16] = n_src[j+15+j_offr]; } + v_up = v_load(&n_src[j+1]); + if ( right ) n_src[j+16] = tmp; + + v_expand(v_um, v_um1, v_um2); + v_expand(v_un, v_un1, v_un2); + v_expand(v_up, v_up1, v_up2); + v_s3m1 = v_reinterpret_as_s16(v_um1); + v_s3m2 = v_reinterpret_as_s16(v_um2); + v_s3n1 = v_reinterpret_as_s16(v_un1); + v_s3n2 = v_reinterpret_as_s16(v_un2); + v_s3p1 = v_reinterpret_as_s16(v_up1); + v_s3p2 = v_reinterpret_as_s16(v_up2); + + // Load fourth row for 3x3 Sobel filter + if ( left ) { tmp = m_src[j-1]; m_src[j-1] = m_src[j+j_offl]; } + v_um = v_load(&m_src[j-1]); + if ( left ) m_src[j-1] = tmp; + + v_un = v_load(&m_src[j]); + + if ( right ) { tmp = m_src[j+16]; m_src[j+16] = m_src[j+15+j_offr]; } + v_up = v_load(&m_src[j+1]); + if ( right ) m_src[j+16] = tmp; + + v_expand(v_um, v_um1, v_um2); + v_expand(v_un, v_un1, v_un2); + v_expand(v_up, v_up1, v_up2); + v_s4m1 = v_reinterpret_as_s16(v_um1); + v_s4m2 = v_reinterpret_as_s16(v_um2); + v_s4n1 = v_reinterpret_as_s16(v_un1); + v_s4n2 = v_reinterpret_as_s16(v_un2); + v_s4p1 = v_reinterpret_as_s16(v_up1); + v_s4p2 = v_reinterpret_as_s16(v_up2); // dx - v_sdx1 = (v_smp1 - v_smm1) + v_two*(v_snp1 - v_snm1) + (v_spp1 - v_spm1); - v_sdx2 = (v_smp2 - v_smm2) + v_two*(v_snp2 - v_snm2) + (v_spp2 - v_spm2); + v_tmp = v_s2p1 - v_s2m1; + v_sdx1 = (v_s1p1 - v_s1m1) + (v_tmp + v_tmp) + (v_s3p1 - v_s3m1); + v_tmp = v_s2p2 - v_s2m2; + v_sdx2 = (v_s1p2 - v_s1m2) + (v_tmp + v_tmp) + (v_s3p2 - v_s3m2); // dy - v_sdy1 = (v_spm1 - v_smm1) + v_two*(v_spn1 - v_smn1) + (v_spp1 - v_smp1); - v_sdy2 = (v_spm2 - v_smm2) + v_two*(v_spn2 - v_smn2) + (v_spp2 - v_smp2); + v_tmp = v_s3n1 - v_s1n1; + v_sdy1 = (v_s3m1 - v_s1m1) + (v_tmp + v_tmp) + (v_s3p1 - v_s1p1); + v_tmp = v_s3n2 - v_s1n2; + v_sdy2 = (v_s3m2 - v_s1m2) + (v_tmp + v_tmp) + (v_s3p2 - v_s1p2); // Store - v_store(&p_dx[idx], v_sdx1); - v_store(&p_dx[idx+8], v_sdx2); - v_store(&p_dy[idx], v_sdy1); - v_store(&p_dy[idx+8], v_sdy2); - } + v_store(&c_dx1[j], v_sdx1); + v_store(&c_dx1[j+8], v_sdx2); + v_store(&c_dy1[j], v_sdy1); + v_store(&c_dy1[j+8], v_sdy2); - // Cleanup - for ( ; j < W - 1; j++ ) - { - idx = i*W + j; - p_dx[idx] = -(p_src[idx-W-1] + 2*p_src[idx-1] + p_src[idx+W-1]) + - (p_src[idx-W+1] + 2*p_src[idx+1] + p_src[idx+W+1]); - p_dy[idx] = -(p_src[idx-W-1] + 2*p_src[idx-W] + p_src[idx-W+1]) + - (p_src[idx+W-1] + 2*p_src[idx+W] + p_src[idx+W+1]); + // dx + v_tmp = v_s3p1 - v_s3m1; + v_sdx1 = (v_s2p1 - v_s2m1) + (v_tmp + v_tmp) + (v_s4p1 - v_s4m1); + v_tmp = v_s3p2 - v_s3m2; + v_sdx2 = (v_s2p2 - v_s2m2) + (v_tmp + v_tmp) + (v_s4p2 - v_s4m2); + + // dy + v_tmp = v_s4n1 - v_s2n1; + v_sdy1 = (v_s4m1 - v_s2m1) + (v_tmp + v_tmp) + (v_s4p1 - v_s2p1); + v_tmp = v_s4n2 - v_s2n2; + v_sdy2 = (v_s4m2 - v_s2m2) + (v_tmp + v_tmp) + (v_s4p2 - v_s2p2); + + // Store + v_store(&c_dx2[j], v_sdx1); + v_store(&c_dx2[j+8], v_sdx2); + v_store(&c_dy2[j], v_sdy1); + v_store(&c_dy2[j+8], v_sdy2); } } -#else -*/ - + i_start = i; + j_start = j; +#endif /* NOTE: * * Sobel-x: -1 0 1 @@ -224,7 +290,7 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, c_dx = P_dx [i]; c_dy = P_dy [i]; - for ( j = j_start; j < W; j++ ) + for ( j = i >= i_start ? 0 : j_start; j < W; j++ ) { j_p = j - 1; j_n = j + 1; @@ -237,7 +303,6 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, (n_src[j_p] + n_src[j] + n_src[j] + n_src[j_n]); } } -//#endif } From f92e2ed57a0d08d697784c7b0efb4b82f8d44a27 Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Fri, 26 Jun 2015 17:13:23 +0200 Subject: [PATCH 070/133] spatialGradient: Make nosse version faster --- modules/imgproc/src/spatialgradient.cpp | 26 ++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/modules/imgproc/src/spatialgradient.cpp b/modules/imgproc/src/spatialgradient.cpp index 056adc5752..644f1a3043 100644 --- a/modules/imgproc/src/spatialgradient.cpp +++ b/modules/imgproc/src/spatialgradient.cpp @@ -284,23 +284,39 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, * 1 2 1 */ int j_p, j_n; + uchar v00, v01, v02, v10, v11, v12, v20, v21, v22; for ( i = 0; i < H; i++ ) { p_src = P_src[i]; c_src = P_src[i+1]; n_src = P_src[i+2]; c_dx = P_dx [i]; c_dy = P_dy [i]; - for ( j = i >= i_start ? 0 : j_start; j < W; j++ ) + // Pre-load 2 columns + j = i >= i_start ? 0 : j_start; + j_p = j - 1; + if ( j_p < 0 ) j_p = j + j_offl; + v00 = p_src[j_p]; v01 = p_src[j]; + v10 = c_src[j_p]; v11 = c_src[j]; + v20 = n_src[j_p]; v21 = n_src[j]; + + for ( ; j < W; j++ ) { j_p = j - 1; j_n = j + 1; if ( j_p < 0 ) j_p = j + j_offl; if ( j_n >= W ) j_n = j + j_offr; - c_dx[j] = -(p_src[j_p] + c_src[j_p] + c_src[j_p] + n_src[j_p]) + - (p_src[j_n] + c_src[j_n] + c_src[j_n] + n_src[j_n]); - c_dy[j] = -(p_src[j_p] + p_src[j] + p_src[j] + p_src[j_n]) + - (n_src[j_p] + n_src[j] + n_src[j] + n_src[j_n]); + // Get values for next column + v02 = p_src[j_n]; + v12 = c_src[j_n]; + v22 = n_src[j_n]; + + c_dx[j] = -(v00 + v10 + v10 + v20) + (v02 + v12 + v12 + v22); + c_dy[j] = -(v00 + v01 + v01 + v02) + (v20 + v21 + v21 + v22); + + // Move values back one column for next iteration + v00 = v01; v10 = v11; v20 = v21; + v01 = v02; v11 = v12; v21 = v22; } } From febd0f14c692fc381ee60cab070d589a60b0bfdb Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Fri, 26 Jun 2015 17:15:44 +0200 Subject: [PATCH 071/133] spatialGradient: Don't dynamically alloc C array, use vector --- modules/imgproc/src/spatialgradient.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/imgproc/src/spatialgradient.cpp b/modules/imgproc/src/spatialgradient.cpp index 644f1a3043..9a33cac33d 100644 --- a/modules/imgproc/src/spatialgradient.cpp +++ b/modules/imgproc/src/spatialgradient.cpp @@ -74,9 +74,9 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, // Store pointers to rows of input/output data // Padded by two rows for border handling - uchar* P_src[H+2]; - short* P_dx [H+2]; - short* P_dy [H+2]; + std::vector P_src(H+2); + std::vector P_dx (H+2); + std::vector P_dy (H+2); int i_top = 0, // Case for H == 1 && W == 1 && BORDER_REPLICATE i_bottom = H - 1, From 8a21726ae5943421733b5b0bc6caa52f7f9bc7cc Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Fri, 26 Jun 2015 17:26:21 +0200 Subject: [PATCH 072/133] spatialGradient: Remove an unnecessary branch in nosse code --- modules/imgproc/src/spatialgradient.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/imgproc/src/spatialgradient.cpp b/modules/imgproc/src/spatialgradient.cpp index 9a33cac33d..24e8571b00 100644 --- a/modules/imgproc/src/spatialgradient.cpp +++ b/modules/imgproc/src/spatialgradient.cpp @@ -301,9 +301,7 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, for ( ; j < W; j++ ) { - j_p = j - 1; j_n = j + 1; - if ( j_p < 0 ) j_p = j + j_offl; if ( j_n >= W ) j_n = j + j_offr; // Get values for next column From 62cad09c64389581131ebe83d714b0c7be55c4ce Mon Sep 17 00:00:00 2001 From: Seon-Wook Park Date: Fri, 26 Jun 2015 17:35:17 +0200 Subject: [PATCH 073/133] spatialGradient: Process 1 row at a time in SSE --- modules/imgproc/src/spatialgradient.cpp | 68 +++++-------------------- 1 file changed, 12 insertions(+), 56 deletions(-) diff --git a/modules/imgproc/src/spatialgradient.cpp b/modules/imgproc/src/spatialgradient.cpp index 24e8571b00..f33c28d819 100644 --- a/modules/imgproc/src/spatialgradient.cpp +++ b/modules/imgproc/src/spatialgradient.cpp @@ -129,18 +129,13 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, v_int16x8 v_s1m1, v_s1m2, v_s1n1, v_s1n2, v_s1p1, v_s1p2, v_s2m1, v_s2m2, v_s2n1, v_s2n2, v_s2p1, v_s2p2, v_s3m1, v_s3m2, v_s3n1, v_s3n2, v_s3p1, v_s3p2, - v_s4m1, v_s4m2, v_s4n1, v_s4n2, v_s4p1, v_s4p2, - v_tmp, v_sdx1, v_sdx2, v_sdy1, v_sdy2; + v_tmp, v_sdx, v_sdy; - uchar *m_src; - short *c_dx1, *c_dx2, *c_dy1, *c_dy2; for ( i = 0; i < H - 2; i += 2 ) { - p_src = P_src[i]; c_src = P_src[i+1]; n_src = P_src[i+2]; m_src = P_src[i+3]; - c_dx1 = P_dx [i]; - c_dy1 = P_dy [i]; - c_dx2 = P_dx [i+1]; - c_dy2 = P_dy [i+1]; + p_src = P_src[i]; c_src = P_src[i+1]; n_src = P_src[i+2]; + c_dx = P_dx [i]; + c_dy = P_dy [i]; // 16-column chunks at a time for ( j = 0; j < W - 15; j += 16 ) @@ -212,62 +207,23 @@ void spatialGradient( InputArray _src, OutputArray _dx, OutputArray _dy, v_s3p1 = v_reinterpret_as_s16(v_up1); v_s3p2 = v_reinterpret_as_s16(v_up2); - // Load fourth row for 3x3 Sobel filter - if ( left ) { tmp = m_src[j-1]; m_src[j-1] = m_src[j+j_offl]; } - v_um = v_load(&m_src[j-1]); - if ( left ) m_src[j-1] = tmp; - - v_un = v_load(&m_src[j]); - - if ( right ) { tmp = m_src[j+16]; m_src[j+16] = m_src[j+15+j_offr]; } - v_up = v_load(&m_src[j+1]); - if ( right ) m_src[j+16] = tmp; - - v_expand(v_um, v_um1, v_um2); - v_expand(v_un, v_un1, v_un2); - v_expand(v_up, v_up1, v_up2); - v_s4m1 = v_reinterpret_as_s16(v_um1); - v_s4m2 = v_reinterpret_as_s16(v_um2); - v_s4n1 = v_reinterpret_as_s16(v_un1); - v_s4n2 = v_reinterpret_as_s16(v_un2); - v_s4p1 = v_reinterpret_as_s16(v_up1); - v_s4p2 = v_reinterpret_as_s16(v_up2); - // dx v_tmp = v_s2p1 - v_s2m1; - v_sdx1 = (v_s1p1 - v_s1m1) + (v_tmp + v_tmp) + (v_s3p1 - v_s3m1); + v_sdx = (v_s1p1 - v_s1m1) + (v_tmp + v_tmp) + (v_s3p1 - v_s3m1); v_tmp = v_s2p2 - v_s2m2; - v_sdx2 = (v_s1p2 - v_s1m2) + (v_tmp + v_tmp) + (v_s3p2 - v_s3m2); + v_sdx = (v_s1p2 - v_s1m2) + (v_tmp + v_tmp) + (v_s3p2 - v_s3m2); // dy v_tmp = v_s3n1 - v_s1n1; - v_sdy1 = (v_s3m1 - v_s1m1) + (v_tmp + v_tmp) + (v_s3p1 - v_s1p1); + v_sdy = (v_s3m1 - v_s1m1) + (v_tmp + v_tmp) + (v_s3p1 - v_s1p1); v_tmp = v_s3n2 - v_s1n2; - v_sdy2 = (v_s3m2 - v_s1m2) + (v_tmp + v_tmp) + (v_s3p2 - v_s1p2); + v_sdy = (v_s3m2 - v_s1m2) + (v_tmp + v_tmp) + (v_s3p2 - v_s1p2); // Store - v_store(&c_dx1[j], v_sdx1); - v_store(&c_dx1[j+8], v_sdx2); - v_store(&c_dy1[j], v_sdy1); - v_store(&c_dy1[j+8], v_sdy2); - - // dx - v_tmp = v_s3p1 - v_s3m1; - v_sdx1 = (v_s2p1 - v_s2m1) + (v_tmp + v_tmp) + (v_s4p1 - v_s4m1); - v_tmp = v_s3p2 - v_s3m2; - v_sdx2 = (v_s2p2 - v_s2m2) + (v_tmp + v_tmp) + (v_s4p2 - v_s4m2); - - // dy - v_tmp = v_s4n1 - v_s2n1; - v_sdy1 = (v_s4m1 - v_s2m1) + (v_tmp + v_tmp) + (v_s4p1 - v_s2p1); - v_tmp = v_s4n2 - v_s2n2; - v_sdy2 = (v_s4m2 - v_s2m2) + (v_tmp + v_tmp) + (v_s4p2 - v_s2p2); - - // Store - v_store(&c_dx2[j], v_sdx1); - v_store(&c_dx2[j+8], v_sdx2); - v_store(&c_dy2[j], v_sdy1); - v_store(&c_dy2[j+8], v_sdy2); + v_store(&c_dx[j], v_sdx); + v_store(&c_dx[j+8], v_sdx); + v_store(&c_dy[j], v_sdy); + v_store(&c_dy[j+8], v_sdy); } } i_start = i; From 898b25726178c59b8d0c294846869946467562fb Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 26 Jun 2015 18:45:58 +0300 Subject: [PATCH 074/133] replacement for #4160 --- {data => samples/data}/detect_blob.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename {data => samples/data}/detect_blob.png (100%) diff --git a/data/detect_blob.png b/samples/data/detect_blob.png similarity index 100% rename from data/detect_blob.png rename to samples/data/detect_blob.png From face069874c84906127d9caaf76be654e0dd0ed9 Mon Sep 17 00:00:00 2001 From: Maxim Kostin Date: Fri, 26 Jun 2015 23:41:25 +0300 Subject: [PATCH 075/133] Added support for highgui module on WinRT 8.1+ Signed-off-by: Maxim Kostin --- modules/highgui/CMakeLists.txt | 30 +- modules/highgui/include/opencv2/highgui.hpp | 41 +- .../include/opencv2/highgui/highgui_winrt.hpp | 48 ++ modules/highgui/src/precomp.hpp | 4 +- modules/highgui/src/window.cpp | 5 + modules/highgui/src/window_winrt.cpp | 552 ++++++------------ modules/highgui/src/window_winrt_bridge.cpp | 346 +++++++++++ modules/highgui/src/window_winrt_bridge.hpp | 233 ++++++++ 8 files changed, 890 insertions(+), 369 deletions(-) create mode 100644 modules/highgui/include/opencv2/highgui/highgui_winrt.hpp create mode 100644 modules/highgui/src/window_winrt_bridge.cpp create mode 100644 modules/highgui/src/window_winrt_bridge.hpp diff --git a/modules/highgui/CMakeLists.txt b/modules/highgui/CMakeLists.txt index dba05ec05b..03be473aee 100644 --- a/modules/highgui/CMakeLists.txt +++ b/modules/highgui/CMakeLists.txt @@ -29,6 +29,9 @@ file(GLOB highgui_ext_hdrs "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/*.hpp" "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/*.h") +# Removing WinRT API headers by default +list(REMOVE_ITEM highgui_ext_hdrs "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/highgui_winrt.hpp") + if(HAVE_QT5) set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -66,12 +69,23 @@ elseif(HAVE_QT) if(${_have_flag}) set_source_files_properties(${_RCC_OUTFILES} PROPERTIES COMPILE_FLAGS -Wno-missing-declarations) endif() -elseif(WINRT AND NOT WINRT_8_0) - # Dependencies used by the implementation referenced - # below are not available on WinRT 8.0. - # Enabling it for WiRT 8.1+ only. - status(" ${name}: WinRT detected") - list(APPEND highgui_srcs ${CMAKE_CURRENT_LIST_DIR}/src/window_winrt.cpp) +elseif(WINRT) + if(NOT WINRT_8_0) + # Dependencies used by the implementation referenced + # below are not available on WinRT 8.0. + # Enabling it for WiRT 8.1+ only. + + # WinRT 8.1+ detected. Adding WinRT API header. + status(" ${name}: WinRT detected. Adding WinRT API header") + list(APPEND highgui_ext_hdrs "${CMAKE_CURRENT_LIST_DIR}/include/opencv2/${name}/highgui_winrt.hpp") + + + list(APPEND highgui_srcs + ${CMAKE_CURRENT_LIST_DIR}/src/window_winrt.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/window_winrt_bridge.cpp) + list(APPEND highgui_hdrs + ${CMAKE_CURRENT_LIST_DIR}/src/window_winrt_bridge.hpp) + endif() # libraries below are neither available nor required # on ARM devices and/or Windows Phone @@ -81,6 +95,10 @@ elseif(WINRT AND NOT WINRT_8_0) status(" ${name}: Windows Phone detected") elseif(OpenCV_ARCH STREQUAL "ARM") status(" ${name}: ARM detected") + if(WINRT_STORE) + list(REMOVE_ITEM HIGHGUI_LIBRARIES "ws2_32") + status(" ${name}: Removing 'ws2_32.lib'") + endif() endif() status(" ${name}: Removing 'comctl32.lib, gdi32.lib, ole32.lib, setupapi.lib'") status(" ${name}: Leaving '${HIGHGUI_LIBRARIES}'") diff --git a/modules/highgui/include/opencv2/highgui.hpp b/modules/highgui/include/opencv2/highgui.hpp index 9275ae7c22..f3afceef0c 100644 --- a/modules/highgui/include/opencv2/highgui.hpp +++ b/modules/highgui/include/opencv2/highgui.hpp @@ -79,7 +79,7 @@ It provides easy interface to: attached to the control panel is a trackbar, or the control panel is empty, a new buttonbar is created. Then, a new button is attached to it. - See below the example used to generate the figure: : + See below the example used to generate the figure: @code int main(int argc, char *argv[]) int value = 50; @@ -122,6 +122,45 @@ It provides easy interface to: } @endcode + + @defgroup highgui_winrt WinRT support + + This figure explains new functionality implemented with WinRT GUI. The new GUI provides an Image control, + and a slider panel. Slider panel holds trackbars attached to it. + + Sliders are attached below the image control. Every new slider is added below the previous one. + + See below the example used to generate the figure: + @code + void sample_app::MainPage::ShowWindow() + { + static cv::String windowName("sample"); + cv::winrt_initContainer(this->cvContainer); + cv::namedWindow(windowName); // not required + + cv::Mat image = cv::imread("Assets/sample.jpg"); + cv::Mat converted = cv::Mat(image.rows, image.cols, CV_8UC4); + cvtColor(image, converted, CV_BGR2BGRA); + cv::imshow(windowName, converted); // this will create window if it hasn't been created before + + int state = 42; + cv::TrackbarCallback callback = [](int pos, void* userdata) + { + if (pos == 0) { + cv::destroyWindow(windowName); + } + }; + cv::TrackbarCallback callbackTwin = [](int pos, void* userdata) + { + if (pos >= 70) { + cv::destroyAllWindows(); + } + }; + cv::createTrackbar("Sample trackbar", windowName, &state, 100, callback); + cv::createTrackbar("Twin brother", windowName, &state, 100, callbackTwin); + } + @endcode + @defgroup highgui_c C API @} */ diff --git a/modules/highgui/include/opencv2/highgui/highgui_winrt.hpp b/modules/highgui/include/opencv2/highgui/highgui_winrt.hpp new file mode 100644 index 0000000000..f4147f3908 --- /dev/null +++ b/modules/highgui/include/opencv2/highgui/highgui_winrt.hpp @@ -0,0 +1,48 @@ +// highgui (UX) support for Windows Runtime + +// Copyright (c) Microsoft Open Technologies, Inc. +// All rights reserved. +// +// (3 - clause BSD License) +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that +// the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the +// following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or +// promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +using namespace Windows::UI::Xaml::Controls; + +namespace cv +{ + +//! @addtogroup highgui_winrt +//! @{ + +/********************************** WinRT Specific API *************************************************/ + +/** @brief Initializes container component that will be used to hold generated window content. + +@param container Container (Panel^) reference that will be used to hold generated window content: controls and image. + +@note + Must be called to assign WinRT container that will hold created windows content. +*/ + CV_EXPORTS void winrt_initContainer(::Windows::UI::Xaml::Controls::Panel^ container); + +//! @} videoio_winrt + +} // cv \ No newline at end of file diff --git a/modules/highgui/src/precomp.hpp b/modules/highgui/src/precomp.hpp index c9517783f9..796af39768 100644 --- a/modules/highgui/src/precomp.hpp +++ b/modules/highgui/src/precomp.hpp @@ -95,16 +95,18 @@ #define CV_WINDOW_MAGIC_VAL 0x00420042 #define CV_TRACKBAR_MAGIC_VAL 0x00420043 -//Yannick Verdie 2010 +//Yannick Verdie 2010, Max Kostin 2015 void cvSetModeWindow_W32(const char* name, double prop_value); void cvSetModeWindow_GTK(const char* name, double prop_value); void cvSetModeWindow_CARBON(const char* name, double prop_value); void cvSetModeWindow_COCOA(const char* name, double prop_value); +void cvSetModeWindow_WinRT(const char* name, double prop_value); double cvGetModeWindow_W32(const char* name); double cvGetModeWindow_GTK(const char* name); double cvGetModeWindow_CARBON(const char* name); double cvGetModeWindow_COCOA(const char* name); +double cvGetModeWindow_WinRT(const char* name); double cvGetPropWindowAutoSize_W32(const char* name); double cvGetPropWindowAutoSize_GTK(const char* name); diff --git a/modules/highgui/src/window.cpp b/modules/highgui/src/window.cpp index d6f35af1cb..03d446dd01 100644 --- a/modules/highgui/src/window.cpp +++ b/modules/highgui/src/window.cpp @@ -65,7 +65,10 @@ CV_IMPL void cvSetWindowProperty(const char* name, int prop_id, double prop_valu cvSetModeWindow_CARBON(name,prop_value); #elif defined (HAVE_COCOA) cvSetModeWindow_COCOA(name,prop_value); + #elif defined (WINRT) + cvSetModeWindow_WinRT(name, prop_value); #endif + break; case CV_WND_PROP_AUTOSIZE: @@ -104,6 +107,8 @@ CV_IMPL double cvGetWindowProperty(const char* name, int prop_id) return cvGetModeWindow_CARBON(name); #elif defined (HAVE_COCOA) return cvGetModeWindow_COCOA(name); + #elif defined (WINRT) + return cvGetModeWindow_WinRT(name); #else return -1; #endif diff --git a/modules/highgui/src/window_winrt.cpp b/modules/highgui/src/window_winrt.cpp index b87c9611db..ba81b51c64 100644 --- a/modules/highgui/src/window_winrt.cpp +++ b/modules/highgui/src/window_winrt.cpp @@ -1,438 +1,268 @@ +// highgui to XAML bridge for OpenCV + +// Copyright (c) Microsoft Open Technologies, Inc. +// All rights reserved. +// +// (3 - clause BSD License) +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that +// the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the +// following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or +// promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + #include "precomp.hpp" -#if defined WINRT && !defined WINRT_8_0 - +#include #include #include #include #include +#include +#include +#include -struct CvWindow; +#define CV_WINRT_NO_GUI_ERROR( funcname ) \ +{ \ + cvError( CV_StsNotImplemented, funcname, \ + "The function is not implemented. ", \ + __FILE__, __LINE__ ); \ +} -typedef struct CvTrackbar +#define CV_ERROR( Code, Msg ) \ +{ \ + cvError( (Code), cvFuncName, Msg, __FILE__, __LINE__ ); \ +}; + +/********************************** WinRT Specific API Implementation ******************************************/ + +// Initializes or overrides container contents with default XAML markup structure +void cv::winrt_initContainer(::Windows::UI::Xaml::Controls::Panel^ _container) { - int signature; - void* hwnd; // TODO: use proper handle type - char* name; - CvTrackbar* next; - CvWindow* parent; - int* data; - int pos; - int maxval; - void (*notify)(int); - void (*notify2)(int, void*); - void* userdata; - int id; + HighguiBridge::getInstance().setContainer(_container); } -CvTrackbar; +/********************************** API Implementation *********************************************************/ -typedef struct CvWindow +CV_IMPL void cvShowImage(const char* name, const CvArr* arr) { - int signature; - void* hwnd; // TODO: use proper handle type - char* name; - CvWindow* prev; - CvWindow* next; - - HGDIOBJ image; - int flags; - - CvMouseCallback on_mouse; - void* on_mouse_param; - - struct - { - void* toolbar; // TODO: use proper handle type - int pos; - int rows; - CvTrackbar* first; - } - toolbar; - - int width; - int height; -} -CvWindow; - -static CvWindow* hg_windows = 0; - -// typedef int (CV_CDECL * CvWin32WindowCallback)(HWND, UINT, WPARAM, LPARAM, int*); - -static CvWindow* icvFindWindowByName(const char* name) { - CvWindow* window = hg_windows; - - for (; window != 0 && strcmp(name, window->name) != 0; window = window->next) - ; - - return window; -} - -static CvTrackbar* -icvFindTrackbarByName(const CvWindow* window, const char* name) { - CvTrackbar* trackbar = window->toolbar.first; - - for (; trackbar != 0 && strcmp(trackbar->name, name) != 0; trackbar = trackbar->next) - ; - - return trackbar; -} - -CV_IMPL int cvInitSystem( int, char** ) -{ - static int wasInitialized = 0; - - if (!wasInitialized) - { - hg_windows = 0; - } - - return 0; -} - -CV_IMPL int cvStartWindowThread(){ - return 0; -} - -CV_IMPL int cvNamedWindow( const char* name, int flags ) -{ - int result = 0; - CV_FUNCNAME( "cvNamedWindow" ); - - __BEGIN__; - __END__; - - return result; -} - -CV_IMPL void cvDestroyWindow( const char* name ) -{ - CV_FUNCNAME( "cvDestroyWindow" ); + CV_FUNCNAME("cvShowImage"); __BEGIN__; - CvWindow* window; + CvMat stub, *image; - if(!name) - CV_ERROR( CV_StsNullPtr, "NULL name string" ); + if (!name) + CV_ERROR(CV_StsNullPtr, "NULL name"); - window = icvFindWindowByName(name); - if( !window ) - EXIT; + CvWindow* window = HighguiBridge::getInstance().namedWindow(name); - __END__; -} - -CV_IMPL void cvShowImage( const char* name, const CvArr* arr ) -{ - CV_FUNCNAME( "cvShowImage" ); - - __BEGIN__; - - CvWindow* window; - SIZE size = { 0, 0 }; - int channels = 0; - void* dst_ptr = 0; - const int channels_def = 3; - int origin = 0; - CvMat stub, dst, *image; - bool changed_size = false; - - if( !name ) - CV_ERROR( CV_StsNullPtr, "NULL name" ); - - window = icvFindWindowByName(name); - if(!window) - { - cvNamedWindow(name, CV_WINDOW_AUTOSIZE); - window = icvFindWindowByName(name); - } - - if( !window || !arr ) - EXIT; - - if( CV_IS_IMAGE_HDR( arr )) - origin = ((IplImage*)arr)->origin; - - CV_CALL( image = cvGetMat( arr, &stub )); - -#ifdef HAVE_OPENGL - if (window->useGl) - { - cv::imshow(name, cv::cvarrToMat(image)); + if (!window || !arr) return; - } -#endif - if (window->image) + CV_CALL(image = cvGetMat(arr, &stub)); + + //TODO: use approach from window_w32.cpp or cv::Mat(.., .., CV_8UC4) + // and cvtColor(.., .., CV_BGR2BGRA) to convert image here + // than beforehand. + + window->updateImage(image); + HighguiBridge::getInstance().showWindow(window); + + __END__; +} + +CV_IMPL int cvNamedWindow(const char* name, int flags) +{ + CV_FUNCNAME("cvNamedWindow"); + + if (!name) + CV_ERROR(CV_StsNullPtr, "NULL name"); + + HighguiBridge::getInstance().namedWindow(name); + + return CV_OK; +} + +CV_IMPL void cvDestroyWindow(const char* name) +{ + CV_FUNCNAME("cvDestroyWindow"); + + if (!name) + CV_ERROR(CV_StsNullPtr, "NULL name string"); + + HighguiBridge::getInstance().destroyWindow(name); +} + +CV_IMPL void cvDestroyAllWindows() +{ + HighguiBridge::getInstance().destroyAllWindows(); +} + +CV_IMPL int cvCreateTrackbar2(const char* trackbar_name, const char* window_name, + int* val, int count, CvTrackbarCallback2 on_notify, void* userdata) +{ + CV_FUNCNAME("cvCreateTrackbar2"); + + int pos = 0; + + if (!window_name || !trackbar_name) + CV_ERROR(CV_StsNullPtr, "NULL window or trackbar name"); + + if (count < 0) + CV_ERROR(CV_StsOutOfRange, "Bad trackbar max value"); + + CvWindow* window = HighguiBridge::getInstance().namedWindow(window_name); + + if (!window) { - //TODO: validate image + CV_ERROR(CV_StsNullPtr, "NULL window"); } - if (size.cx != image->width || size.cy != image->height || channels != channels_def) + window->createSlider(trackbar_name, val, count, on_notify, userdata); + + return CV_OK; +} + +CV_IMPL void cvSetTrackbarPos(const char* trackbar_name, const char* window_name, int pos) +{ + CV_FUNCNAME("cvSetTrackbarPos"); + + CvTrackbar* trackbar = 0; + + if (trackbar_name == 0 || window_name == 0) + CV_ERROR(CV_StsNullPtr, "NULL trackbar or window name"); + + CvWindow* window = HighguiBridge::getInstance().findWindowByName(window_name); + if (window) + trackbar = window->findTrackbarByName(trackbar_name); + + if (trackbar) + trackbar->setPosition(pos); +} + +CV_IMPL void cvSetTrackbarMax(const char* trackbar_name, const char* window_name, int maxval) +{ + CV_FUNCNAME("cvSetTrackbarMax"); + + if (maxval >= 0) { - changed_size = true; + if (trackbar_name == 0 || window_name == 0) + CV_ERROR(CV_StsNullPtr, "NULL trackbar or window name"); - //TODO: handle image resize + CvTrackbar* trackbar = HighguiBridge::getInstance().findTrackbarByName(trackbar_name, window_name); + + if (trackbar) + trackbar->setMaxPosition(maxval); } - - cvInitMatHeader( &dst, size.cy, size.cx, CV_8UC3, - dst_ptr, (size.cx * channels + 3) & -4 ); - cvConvertImage( image, &dst, origin == 0 ? CV_CVTIMG_FLIP : 0 ); - - if (changed_size) - //TODO: handle consequent image resize - - __END__; } -CV_IMPL void cvResizeWindow(const char* name, int width, int height ) +CV_IMPL int cvGetTrackbarPos(const char* trackbar_name, const char* window_name) { - CV_FUNCNAME( "cvResizeWindow" ); + int pos = -1; - __BEGIN__; + CV_FUNCNAME("cvGetTrackbarPos"); - CvWindow* window; + if (trackbar_name == 0 || window_name == 0) + CV_ERROR(CV_StsNullPtr, "NULL trackbar or window name"); - if( !name ) - CV_ERROR( CV_StsNullPtr, "NULL name" ); + CvTrackbar* trackbar = HighguiBridge::getInstance().findTrackbarByName(trackbar_name, window_name); - window = icvFindWindowByName(name); - if(!window) - EXIT; + if (trackbar) + pos = trackbar->getPosition(); - // TODO: implement appropriate logic here - - __END__; + return pos; } +/********************************** Not YET implemented API ****************************************************/ -CV_IMPL void cvMoveWindow( const char* name, int x, int y ) +CV_IMPL int cvWaitKey(int delay) { - CV_FUNCNAME( "cvMoveWindow" ); + CV_WINRT_NO_GUI_ERROR("cvWaitKey"); - __BEGIN__; - - CvWindow* window; - RECT rect; - - if( !name ) - CV_ERROR( CV_StsNullPtr, "NULL name" ); - - window = icvFindWindowByName(name); - if(!window) - EXIT; - - // TODO: implement appropriate logic here - - __END__; -} - - - -CV_IMPL void cvDestroyAllWindows(void) -{ - // TODO: implement appropriate logic here -} - -CV_IMPL int cvWaitKey( int delay ) -{ // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms724411(v=vs.85).aspx int time0 = GetTickCount64(); - for(;;) + for (;;) { CvWindow* window; - if ((delay > 0 && abs((int)(GetTickCount64() - time0)) >= delay) || hg_windows == 0) - return -1; - if (delay <= 0) { // TODO: implement appropriate logic here } - - for( window = hg_windows; window != 0; window = window->next ) - { - } } } - - -CV_IMPL int -cvCreateTrackbar( const char* trackbar_name, const char* window_name, - int* val, int count, CvTrackbarCallback on_notify ) +CV_IMPL void cvSetMouseCallback(const char* window_name, CvMouseCallback on_mouse, void* param) { - // TODO: implement appropriate logic here - return 0; -} + CV_WINRT_NO_GUI_ERROR("cvSetMouseCallback"); -CV_IMPL int -cvCreateTrackbar2( const char* trackbar_name, const char* window_name, - int* val, int count, CvTrackbarCallback2 on_notify2, - void* userdata ) -{ - // TODO: implement appropriate logic here - return 0; -} + CV_FUNCNAME("cvSetMouseCallback"); -CV_IMPL void -cvSetMouseCallback( const char* window_name, CvMouseCallback on_mouse, void* param ) -{ - CV_FUNCNAME( "cvSetMouseCallback" ); + if (!window_name) + CV_ERROR(CV_StsNullPtr, "NULL window name"); - __BEGIN__; - - CvWindow* window = 0; - - if( !window_name ) - CV_ERROR( CV_StsNullPtr, "NULL window name" ); - - window = icvFindWindowByName(window_name); - if( !window ) - EXIT; + CvWindow* window = HighguiBridge::getInstance().findWindowByName(window_name); + if (!window) + return; // TODO: implement appropriate logic here - - __END__; } +/********************************** Disabled or not supported API **********************************************/ -CV_IMPL int cvGetTrackbarPos( const char* trackbar_name, const char* window_name ) +CV_IMPL void cvMoveWindow(const char* name, int x, int y) { - int pos = -1; - - CV_FUNCNAME( "cvGetTrackbarPos" ); - - __BEGIN__; - - CvWindow* window; - CvTrackbar* trackbar = 0; - - if( trackbar_name == 0 || window_name == 0 ) - CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" ); - - window = icvFindWindowByName( window_name ); - if( window ) - trackbar = icvFindTrackbarByName( window, trackbar_name ); - - if( trackbar ) - pos = trackbar->pos; - - __END__; - - return pos; + CV_WINRT_NO_GUI_ERROR("cvMoveWindow"); } - -CV_IMPL void cvSetTrackbarPos( const char* trackbar_name, const char* window_name, int pos ) +CV_IMPL void cvResizeWindow(const char* name, int width, int height) { - CV_FUNCNAME( "cvSetTrackbarPos" ); - - __BEGIN__; - - CvWindow* window; - CvTrackbar* trackbar = 0; - - if( trackbar_name == 0 || window_name == 0 ) - CV_ERROR( CV_StsNullPtr, "NULL trackbar or window name" ); - - window = icvFindWindowByName( window_name ); - if( window ) - trackbar = icvFindTrackbarByName( window, trackbar_name ); - - if( trackbar ) - { - if( pos < 0 ) - pos = 0; - - if( pos > trackbar->maxval ) - pos = trackbar->maxval; - - //TODO: update trackbar - } - - __END__; + CV_WINRT_NO_GUI_ERROR("cvResizeWindow"); } - -CV_IMPL void cvSetTrackbarMax(const char* trackbar_name, const char* window_name, int maxval) +CV_IMPL int cvInitSystem(int, char**) { - CV_FUNCNAME( "cvSetTrackbarMax" ); - - __BEGIN__; - - if (maxval >= 0) - { - CvWindow* window = 0; - CvTrackbar* trackbar = 0; - if (trackbar_name == 0 || window_name == 0) - { - CV_ERROR(CV_StsNullPtr, "NULL trackbar or window name"); - } - - window = icvFindWindowByName(window_name); - if (window) - { - trackbar = icvFindTrackbarByName(window, trackbar_name); - if (trackbar) - { - // The position will be min(pos, maxval). - trackbar->maxval = maxval; - - //TODO: update trackbar - } - } - } - - __END__; + CV_WINRT_NO_GUI_ERROR("cvInitSystem"); + return CV_StsNotImplemented; } - -CV_IMPL void* cvGetWindowHandle( const char* window_name ) +CV_IMPL void* cvGetWindowHandle(const char*) { - void* hwnd = 0; - - CV_FUNCNAME( "cvGetWindowHandle" ); - - __BEGIN__; - - CvWindow* window; - - if( window_name == 0 ) - CV_ERROR( CV_StsNullPtr, "NULL window name" ); - - window = icvFindWindowByName( window_name ); - if( window ) - hwnd = (void*)window->hwnd; - - __END__; - - return hwnd; + CV_WINRT_NO_GUI_ERROR("cvGetWindowHandle"); + return (void*) CV_StsNotImplemented; } - -CV_IMPL const char* cvGetWindowName( void* window_handle ) +CV_IMPL const char* cvGetWindowName(void*) { - const char* window_name = ""; - - CV_FUNCNAME( "cvGetWindowName" ); - - __BEGIN__; - - CvWindow* window = 0; - - if( window_handle == 0 ) - CV_ERROR( CV_StsNullPtr, "NULL window" ); - - // window = TODO: find window by handle - if( window ) - window_name = window->name; - - __END__; - - return 0; + CV_WINRT_NO_GUI_ERROR("cvGetWindowName"); + return (const char*) CV_StsNotImplemented; } -#endif //defined WINRT && !defined WINRT_8_0 \ No newline at end of file +void cvSetModeWindow_WinRT(const char* name, double prop_value) { + CV_WINRT_NO_GUI_ERROR("cvSetModeWindow"); +} + +double cvGetModeWindow_WinRT(const char* name) { + CV_WINRT_NO_GUI_ERROR("cvGetModeWindow"); + return CV_StsNotImplemented; +} + +CV_IMPL int cvStartWindowThread() { + CV_WINRT_NO_GUI_ERROR("cvStartWindowThread"); + return CV_StsNotImplemented; +} \ No newline at end of file diff --git a/modules/highgui/src/window_winrt_bridge.cpp b/modules/highgui/src/window_winrt_bridge.cpp new file mode 100644 index 0000000000..60421d681f --- /dev/null +++ b/modules/highgui/src/window_winrt_bridge.cpp @@ -0,0 +1,346 @@ +// highgui to XAML bridge for OpenCV + +// Copyright (c) Microsoft Open Technologies, Inc. +// All rights reserved. +// +// (3 - clause BSD License) +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that +// the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the +// following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or +// promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include "opencv2\highgui\highgui_winrt.hpp" +#include "window_winrt_bridge.hpp" + +#include +#include // Windows::Storage::Streams::IBufferByteAccess + +using namespace Microsoft::WRL; // ComPtr +using namespace Windows::Storage::Streams; // IBuffer +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Media::Imaging; + +using namespace ::std; + +/***************************** Constants ****************************************/ + +// Default markup for the container content allowing for proper components placement +const Platform::String^ CvWindow::markupContent = +"\n" \ +" \n" \ +" \n" \ +" \n" \ +" \n" \ +" \n" \ +""; + +const double CvWindow::sliderDefaultWidth = 100; + +/***************************** HighguiBridge class ******************************/ + +HighguiBridge& HighguiBridge::getInstance() +{ + static HighguiBridge instance; + return instance; +} + +void HighguiBridge::setContainer(Windows::UI::Xaml::Controls::Panel^ container) +{ + this->container = container; +} + +CvWindow* HighguiBridge::findWindowByName(cv::String name) +{ + auto search = windowsMap->find(name); + if (search != windowsMap->end()) { + return search->second; + } + + return nullptr; +} + +CvTrackbar* HighguiBridge::findTrackbarByName(cv::String trackbar_name, cv::String window_name) +{ + CvWindow* window = findWindowByName(window_name); + + if (window) + return window->findTrackbarByName(trackbar_name); + + return nullptr; +} + +Platform::String^ HighguiBridge::convertString(cv::String name) +{ + auto data = name.c_str(); + int bufferSize = MultiByteToWideChar(CP_UTF8, 0, data, -1, nullptr, 0); + auto wide = std::make_unique(bufferSize); + if (0 == MultiByteToWideChar(CP_UTF8, 0, data, -1, wide.get(), bufferSize)) + return nullptr; + + std::wstring* stdStr = new std::wstring(wide.get()); + return ref new Platform::String(stdStr->c_str()); +} + +void HighguiBridge::cleanContainer() +{ + container->Children->Clear(); +} + +void HighguiBridge::showWindow(CvWindow* window) +{ + currentWindow = window; + cleanContainer(); + HighguiBridge::getInstance().container->Children->Append(window->getPage()); +} + +CvWindow* HighguiBridge::namedWindow(cv::String name) { + + CvWindow* window = HighguiBridge::getInstance().findWindowByName(name.c_str()); + if (!window) + { + window = createWindow(name); + } + + return window; +} + +void HighguiBridge::destroyWindow(cv::String name) +{ + auto window = windowsMap->find(name); + if (window != windowsMap->end()) + { + // Check if deleted window is the one currently displayed + // and clear container if this is the case + if (window->second == currentWindow) + { + cleanContainer(); + } + + windowsMap->erase(window); + } +} + +void HighguiBridge::destroyAllWindows() +{ + cleanContainer(); + windowsMap->clear(); +} + +CvWindow* HighguiBridge::createWindow(cv::String name) +{ + CvWindow* window = new CvWindow(name); + windowsMap->insert(std::pair(name, window)); + + return window; +} + +/***************************** CvTrackbar class *********************************/ + +CvTrackbar::CvTrackbar(cv::String name, Slider^ slider, CvWindow* parent) : name(name), slider(slider), parent(parent) {} + +CvTrackbar::~CvTrackbar() {} + +void CvTrackbar::setPosition(double pos) +{ + if (pos < 0) + pos = 0; + + if (pos > slider->Maximum) + pos = slider->Maximum; + + slider->Value = pos; +} + +void CvTrackbar::setMaxPosition(double pos) +{ + if (pos < 0) + pos = 0; + + slider->Maximum = pos; +} + +void CvTrackbar::setSlider(Slider^ slider) { + if (slider) + this->slider = slider; +} + +double CvTrackbar::getPosition() +{ + return slider->Value; +} + +double CvTrackbar::getMaxPosition() +{ + return slider->Maximum; +} + +Slider^ CvTrackbar::getSlider() +{ + return slider; +} + +/***************************** CvWindow class ***********************************/ + +CvWindow::CvWindow(cv::String name, int flags) : name(name) +{ + this->page = (Page^)Windows::UI::Xaml::Markup::XamlReader::Load(const_cast(markupContent)); + this->sliderMap = new std::map(); + + sliderPanel = (Panel^)page->FindName("cvTrackbar"); + imageControl = (Image^)page->FindName("cvImage"); + buttonPanel = (Panel^)page->FindName("cvButton"); + + // Required to adapt controls to the size of the image. + // System calculates image control width first, after that we can + // update other controls + imageControl->Loaded += ref new Windows::UI::Xaml::RoutedEventHandler( + [=](Platform::Object^ sender, + Windows::UI::Xaml::RoutedEventArgs^ e) + { + // Need to update sliders with appropriate width + for (auto iter = sliderMap->begin(); iter != sliderMap->end(); ++iter) { + iter->second->getSlider()->Width = imageControl->ActualWidth; + } + + // Need to update buttons with appropriate width + // TODO: implement when adding buttons + }); + +} + +CvWindow::~CvWindow() {} + +void CvWindow::createSlider(cv::String name, int* val, int count, CvTrackbarCallback2 on_notify, void* userdata) +{ + CvTrackbar* trackbar = findTrackbarByName(name); + + // Creating slider if name is new or reusing the existing one + Slider^ slider = !trackbar ? ref new Slider() : trackbar->getSlider(); + + slider->Header = HighguiBridge::getInstance().convertString(name); + + // Making slider the same size as the image control or setting minimal size. + // This is added to cover potential edge cases because: + // 1. Fist clause will not be true until the second call to any container-updating API + // e.g. cv::createTrackbar, cv:imshow or cv::namedWindow + // 2. Second clause will work but should be immediately overridden by Image->Loaded callback, + // see CvWindow ctor. + if (this->imageControl->ActualWidth > 0) { + // One would use double.NaN for auto-stretching but there is no such constant in C++/CX + // see https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.frameworkelement.width + slider->Width = this->imageControl->ActualWidth; + } else { + // This value would never be used/seen on the screen unless there is something wrong with the image. + // Although this code actually gets called, slider width will be overridden in the callback after + // Image control is loaded. See callback implementation in CvWindow ctor. + slider->Width = sliderDefaultWidth; + } + slider->Value = *val; + slider->Maximum = count; + slider->Visibility = Windows::UI::Xaml::Visibility::Visible; + slider->Margin = Windows::UI::Xaml::ThicknessHelper::FromLengths(10, 10, 10, 0); + slider->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Left; + + if (!trackbar) + { + if (!sliderPanel) return; + + // Adding slider to the list for current window + CvTrackbar* trackbar = new CvTrackbar(name, slider, this); + trackbar->callback = on_notify; + slider->ValueChanged += + ref new Controls::Primitives::RangeBaseValueChangedEventHandler( + [=](Platform::Object^ sender, + Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs^ e) + { + Slider^ slider = (Slider^)sender; + trackbar->callback(slider->Value, nullptr); + }); + this->sliderMap->insert(std::pair(name, trackbar)); + + // Adding slider to the window + sliderPanel->Children->Append(slider); + } +} + +CvTrackbar* CvWindow::findTrackbarByName(cv::String name) +{ + auto search = sliderMap->find(name); + if (search != sliderMap->end()) { + return search->second; + } + + return nullptr; +} + +void CvWindow::updateImage(CvMat* src) +{ + if (!imageControl) return; + + this->imageData = src; + this->imageWidth = src->width; + + // Create the WriteableBitmap + WriteableBitmap^ bitmap = ref new WriteableBitmap(src->cols, src->rows); + + // Get access to the pixels + IBuffer^ buffer = bitmap->PixelBuffer; + unsigned char* dstPixels; + + // Obtain IBufferByteAccess + ComPtr pBufferByteAccess; + ComPtr pBuffer((IInspectable*)buffer); + pBuffer.As(&pBufferByteAccess); + + // Get pointer to pixel bytes + pBufferByteAccess->Buffer(&dstPixels); + memcpy(dstPixels, src->data.ptr, CV_ELEM_SIZE(src->type) * src->cols*src->rows); + + // Set the bitmap to the Image element + imageControl->Source = bitmap; +} + +Page^ CvWindow::getPage() +{ + return page; +} + +//TODO: prototype, not in use yet +void CvWindow::createButton(cv::String name) +{ + if (!buttonPanel) return; + + Button^ b = ref new Button(); + b->Content = HighguiBridge::getInstance().convertString(name); + b->Width = 260; + b->Height = 80; + b->Click += ref new Windows::UI::Xaml::RoutedEventHandler( + [=](Platform::Object^ sender, + Windows::UI::Xaml::RoutedEventArgs^ e) + { + Button^ button = (Button^)sender; + // TODO: more logic here... + }); + + buttonPanel->Children->Append(b); +} + +// end \ No newline at end of file diff --git a/modules/highgui/src/window_winrt_bridge.hpp b/modules/highgui/src/window_winrt_bridge.hpp new file mode 100644 index 0000000000..d19dd29c82 --- /dev/null +++ b/modules/highgui/src/window_winrt_bridge.hpp @@ -0,0 +1,233 @@ +// highgui to XAML bridge for OpenCV + +// Copyright (c) Microsoft Open Technologies, Inc. +// All rights reserved. +// +// (3 - clause BSD License) +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that +// the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the +// following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or +// promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +#include + +using namespace Windows::UI::Xaml::Controls; + +class CvWindow; +class CvTrackbar; + +class HighguiBridge +{ +public: + + /** @brief Instantiates a Highgui singleton (Meyers type). + + The function Instantiates a Highgui singleton (Meyers type) and returns reference to that instance. + */ + static HighguiBridge& getInstance(); + + /** @brief Finds window by name and returns the reference to it. + + @param name Name of the window. + + The function finds window by name and returns the reference to it. Returns nullptr + if window with specified name is not found or name argument is null. + */ + CvWindow* findWindowByName(cv::String name); + + /** @brief Returns reference to the trackbar(slider) registered within window with a provided name. + + @param name Name of the window. + + The function returns reference to the trackbar(slider) registered within window with a provided name. + Returns nullptr if trackbar with specified name is not found or window reference is nullptr. + */ + CvTrackbar* findTrackbarByName(cv::String trackbarName, cv::String windowName); + + /** @brief Converts cv::String to Platform::String. + + @param name String to convert. + + The function converts cv::String to Platform::String. + Returns nullptr if conversion fails. + */ + Platform::String^ convertString(cv::String name); + + /** @brief Creates window if there is no window with this name, otherwise returns existing window. + + @param name Window name. + + The function creates window if there is no window with this name, otherwise returns existing window. + */ + CvWindow* namedWindow(cv::String name); + + /** @brief Shows provided window. + + The function shows provided window: makes provided window current, removes current container + contents and shows current window by putting it as a container content. + */ + void showWindow(CvWindow* window); + + /** @brief Destroys window if there exists window with this name, otherwise does nothing. + + @param name Window name. + + The function destroys window if there exists window with this name, otherwise does nothing. + If window being destroyed is the current one, it will be hidden by clearing the window container. + */ + void destroyWindow(cv::String name); + + /** @brief Destroys all windows. + + The function destroys all windows. + */ + void destroyAllWindows(); + + /** @brief Assigns container used to display windows. + + @param _container Container reference. + + The function assigns container used to display windows. + */ + void setContainer(Windows::UI::Xaml::Controls::Panel^ _container); + +private: + + // Meyers singleton + HighguiBridge(const HighguiBridge &); + void operator=(HighguiBridge &); + HighguiBridge() { + windowsMap = new std::map(); + }; + + /** @brief Creates window if there is no window with this name. + + @param name Window name. + + The function creates window if there is no window with this name. + */ + CvWindow* createWindow(cv::String name); + + /** @brief Cleans current container contents. + + The function cleans current container contents. + */ + void cleanContainer(); + + // see https://msdn.microsoft.com/en-US/library/windows/apps/xaml/hh700103.aspx + // see https://msdn.microsoft.com/ru-ru/library/windows.foundation.collections.aspx + std::map* windowsMap; + CvWindow* currentWindow; + + // Holds current container/content to manipulate with + Windows::UI::Xaml::Controls::Panel^ container; +}; + +class CvTrackbar +{ +public: + CvTrackbar(cv::String name, Slider^ slider, CvWindow* parent); + ~CvTrackbar(); + + double getPosition(); + void setPosition(double pos); + double getMaxPosition(); + void setMaxPosition(double pos); + Slider^ getSlider(); + void setSlider(Slider^ pos); + + CvTrackbarCallback2 callback; + +private: + cv::String name; + Slider^ slider; + CvWindow* parent; +}; + +class CvWindow +{ +public: + CvWindow(cv::String name, int flag = CV_WINDOW_NORMAL); + ~CvWindow(); + + /** @brief NOTE: prototype. + + Should create button if there is no button with this name already. + */ + void createButton(cv::String name); + + /** @brief Creates slider if there is no slider with this name already. + + The function creates slider if there is no slider with this name already OR resets + provided values for the existing one. + */ + void createSlider(cv::String name, int* val, int count, CvTrackbarCallback2 on_notify, void* userdata); + + /** @brief Updates window image. + + @param src Image data object reference. + + The function updates window image. If argument is null or image control is not found - does nothing. + */ + void updateImage(CvMat* arr); + + /** @brief Returns reference to the trackbar(slider) registered within provided window. + + @param name Name of the window. + + The function returns reference to the trackbar(slider) registered within provided window. + Returns nullptr if trackbar with specified name is not found or window reference is nullptr. + */ + CvTrackbar* findTrackbarByName(cv::String name); + Page^ getPage(); + +private: + cv::String name; + + // Holds image data in CV format + CvMat* imageData; + + // Map of all sliders assigned to this window + std::map* sliderMap; + + // Window contents holder + Page^ page; + + // Image control displayed by this window + Image^ imageControl; + + // Container for sliders + Panel^ sliderPanel; + + // Container for buttons + // TODO: prototype, not available via API + Panel^ buttonPanel; + + // Holds image width to arrange other UI elements. + // Required since imageData->width value gets recalculated when processing + int imageWidth; + + // Default markup for the container content allowing for proper components placement + static const Platform::String^ markupContent; + + // Default Slider size, fallback solution for unexpected edge cases + static const double sliderDefaultWidth; +}; \ No newline at end of file From ccdd1126f65a2b5abdc1203203f0c1f1ea91e4bc Mon Sep 17 00:00:00 2001 From: Maxim Kostin Date: Thu, 25 Jun 2015 18:22:46 +0300 Subject: [PATCH 076/133] Updated sample to showcase highgui WinRT usage Signed-off-by: Maxim Kostin --- .../FaceDetection/FaceDetection/MainPage.xaml | 40 ++++++++++++++----- .../FaceDetection/MainPage.xaml.cpp | 29 +++----------- .../FaceDetection/FaceDetection/opencv.props | 21 ++++++---- 3 files changed, 49 insertions(+), 41 deletions(-) diff --git a/samples/winrt/FaceDetection/FaceDetection/MainPage.xaml b/samples/winrt/FaceDetection/FaceDetection/MainPage.xaml index c9ebdd2017..31c6ba8ac6 100644 --- a/samples/winrt/FaceDetection/FaceDetection/MainPage.xaml +++ b/samples/winrt/FaceDetection/FaceDetection/MainPage.xaml @@ -1,16 +1,34 @@ - + -