gen_java.py 33.3 KB
Newer Older
A
Andrey Kamaev 已提交
1 2 3 4 5 6 7 8
import sys, re, os.path
from string import Template

try:
    from cStringIO import StringIO
except:
    from StringIO import StringIO

9 10 11 12 13 14 15 16 17 18 19 20 21
func_ignore_list = (
    "namedWindow",
    "destroyWindow",
    "destroyAllWindows",
    "startWindowThread",
    "setWindowProperty",
    "getWindowProperty",
    "getTrackbarPos",
    "setTrackbarPos",
    "imshow",
    "waitKey",
)

22 23 24
class_ignore_list = (
    "VideoWriter",
)
25

26 27 28 29 30 31 32 33
# c_type    : { java/jni correspondence }
type_dict = {
# "simple"  : { j_type : "?", jn_type : "?", jni_type : "?", suffix : "?" },
    ""        : { "j_type" : "", "jn_type" : "long", "jni_type" : "jlong" }, # c-tor ret_type
    "void"    : { "j_type" : "void", "jn_type" : "void", "jni_type" : "void" },
    "env"     : { "j_type" : "", "jn_type" : "", "jni_type" : "JNIEnv*"},
    "cls"     : { "j_type" : "", "jn_type" : "", "jni_type" : "jclass"},
    "bool"    : { "j_type" : "boolean", "jn_type" : "boolean", "jni_type" : "jboolean", "suffix" : "Z" },
34 35
    "int"     : { "j_type" : "int", "jn_type" : "int", "jni_type" : "jint", "suffix" : "I" },
    "long"    : { "j_type" : "int", "jn_type" : "int", "jni_type" : "jint", "suffix" : "I" },
36 37 38 39 40 41
    "float"   : { "j_type" : "float", "jn_type" : "float", "jni_type" : "jfloat", "suffix" : "F" },
    "double"  : { "j_type" : "double", "jn_type" : "double", "jni_type" : "jdouble", "suffix" : "D" },
    "size_t"  : { "j_type" : "long", "jn_type" : "long", "jni_type" : "jlong", "suffix" : "J" },
    "__int64" : { "j_type" : "long", "jn_type" : "long", "jni_type" : "jlong", "suffix" : "J" },
# "complex" : { j_type : "?", jn_args : (("", ""),), jn_name : "", jni_var : "", jni_name : "", "suffix" : "?" },
    "Mat"     : { "j_type" : "Mat", "jn_type" : "long", "jn_args" : (("__int64", ".nativeObj"),),
42
                  "jni_var" : "Mat& %(n)s = *((Mat*)%(n)s_nativeObj)",
43
                  "jni_type" : "jlong", #"jni_name" : "*%(n)s",
44 45
                  "suffix" : "J" },
    "Point"   : { "j_type" : "Point", "jn_args" : (("double", ".x"), ("double", ".y")),
46
                  "jni_var" : "Point %(n)s((int)%(n)s_x, (int)%(n)s_y)",
47 48
                  "suffix" : "DD"},
    "Point2f" : { "j_type" : "Point", "jn_args" : (("double", ".x"), ("double", ".y")),
49
                  "jni_var" : "Point2f %(n)s((float)%(n)s_x, (float)%(n)s_y)",
50 51
                  "suffix" : "DD"},
    "Point2d" : { "j_type" : "Point", "jn_args" : (("double", ".x"), ("double", ".y")),
52
                  "jni_var" : "Point2d %(n)s(%(n)s_x, %(n)s_y)",
53 54
                  "suffix" : "DD"},
    "Point3i" : { "j_type" : "Point", "jn_args" : (("double", ".x"), ("double", ".y"), ("double", ".z")),
55
                  "jni_var" : "Point3i %(n)s((int)%(n)s_x, (int)%(n)s_y, (int)%(n)s_z)",
56 57
                  "suffix" : "DDD"},
    "Point3f" : { "j_type" : "Point", "jn_args" : (("double", ".x"), ("double", ".y"), ("double", ".z")),
58
                  "jni_var" : "Point3f %(n)s((float)%(n)s_x, (float)%(n)s_y, (float)%(n)s_z)",
59 60
                  "suffix" : "DDD"},
    "Point3d" : { "j_type" : "Point", "jn_args" : (("double", ".x"), ("double", ".y"), ("double", ".z")),
61
                  "jni_var" : "Point3d %(n)s(%(n)s_x, %(n)s_y, %(n)s_z)",
62 63
                  "suffix" : "DDD"},
    "Rect"    : { "j_type" : "Rect",  "jn_args" : (("int", ".x"), ("int", ".y"), ("int", ".width"), ("int", ".height")),
64
                  "jni_var" : "Rect %(n)s(%(n)s_x, %(n)s_y, %(n)s_width, %(n)s_height)",
65
                  "suffix" : "IIII"},
66
    "Size"    : { "j_type" : "Size",  "jn_args" : (("double", ".width"), ("double", ".height")),
67
                  "jni_var" : "Size %(n)s((int)%(n)s_width, (int)%(n)s_height)",
68 69
                  "suffix" : "DD"},
    "Size2f"  : { "j_type" : "Size",  "jn_args" : (("double", ".width"), ("double", ".height")),
70
                  "jni_var" : "Size2f %(n)s((float)%(n)s_width, (float)%(n)s_height)",
71 72
                  "suffix" : "DD"},
 "RotatedRect": { "j_type" : "RotatedRect",  "jn_args" : (("double", ".center.x"), ("double", ".center.y"), ("double", ".size.width"), ("double", ".size.height"), ("double", ".angle")),
73
                  "jni_var" : "RotatedRect %(n)s(cv::Point2f(%(n)s_center_x, %(n)s_center_y), cv::Size2f(%(n)s_size_width, %(n)s_size_height), %(n)s_angle)",
74
                  "suffix" : "DDDDD"},
75
    "Scalar"  : { "j_type" : "Scalar",  "jn_args" : (("double", ".v0"), ("double", ".v1"), ("double", ".v2"), ("double", ".v3")),
76
                  "jni_var" : "Scalar %(n)s(%(n)s_v0, %(n)s_v1, %(n)s_v2, %(n)s_v3)",
77
                  "suffix" : "DDDD"},
78 79 80 81 82 83
    "Range"   : { "j_type" : "Range",  "jn_args" : (("int", ".start"), ("int", ".end")),
                  "jni_var" : "cv::Range %(n)s(%(n)s_start, %(n)s_end)",
                  "suffix" : "II"},
    "CvSlice"   : { "j_type" : "Range",  "jn_args" : (("int", ".start"), ("int", ".end")),
                  "jni_var" : "cv::Range %(n)s(%(n)s_start, %(n)s_end)",
                  "suffix" : "II"},
84 85
    "string"  : { "j_type" : "java.lang.String",  "jn_type" : "java.lang.String",
                  "jni_type" : "jstring", "jni_name" : "n_%(n)s",
86
                  "jni_var" : 'const char* utf_%(n)s = env->GetStringUTFChars(%(n)s, 0); std::string n_%(n)s( utf_%(n)s ? utf_%(n)s : "" ); env->ReleaseStringUTFChars(%(n)s, utf_%(n)s)',
87
                  "suffix" : "Ljava_lang_String_2"},
88 89 90 91
    "String"  : { "j_type" : "java.lang.String",  "jn_type" : "java.lang.String",
                  "jni_type" : "jstring", "jni_name" : "n_%(n)s",
                  "jni_var" : 'const char* utf_%(n)s = env->GetStringUTFChars(%(n)s, 0); String n_%(n)s( utf_%(n)s ? utf_%(n)s : "" ); env->ReleaseStringUTFChars(%(n)s, utf_%(n)s)',
                  "suffix" : "Ljava_lang_String_2"},
92 93 94 95
    "c_string": { "j_type" : "java.lang.String",  "jn_type" : "java.lang.String",
                  "jni_type" : "jstring", "jni_name" : "n_%(n)s.c_str()",
                  "jni_var" : 'const char* utf_%(n)s = env->GetStringUTFChars(%(n)s, 0); std::string n_%(n)s( utf_%(n)s ? utf_%(n)s : "" ); env->ReleaseStringUTFChars(%(n)s, utf_%(n)s)',
                  "suffix" : "Ljava_lang_String_2"},
A
Andrey Kamaev 已提交
96 97 98

}

99 100
setManualFunctions=set(['minMaxLoc'])

A
Andrey Kamaev 已提交
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
class ConstInfo(object):
    def __init__(self, cname, name, val):
##        self.name = re.sub(r"^cv\.", "", name).replace(".", "_")
        self.cname = cname
        self.name =  re.sub(r"^Cv", "", name)
        #self.name = re.sub(r"([a-z])([A-Z])", r"\1_\2", name)
        #self.name = self.name.upper()
        self.value = val


class ClassInfo(object):
    def __init__(self, decl): # [ 'class/struct cname', [bases], [modlist] ]
        name = decl[0]
        name = name[name.find(" ")+1:].strip()
        self.cname = self.name = self.jname = re.sub(r"^cv\.", "", name)
        self.cname =self.cname.replace(".", "::")
117
        #self.jname =  re.sub(r"^Cv", "", self.jname)
A
Andrey Kamaev 已提交
118 119 120 121 122 123 124 125 126 127 128 129
        self.methods = {}
        self.consts = [] # using a list to save the occurence order
        for m in decl[2]:
            if m.startswith("="):
                self.jname = m[1:]


class ArgInfo(object):
    def __init__(self, arg_tuple): # [ ctype, name, def val, [mod], argno ]
        self.ctype = arg_tuple[0]
        self.name = arg_tuple[1]
        self.defval = arg_tuple[2]
130 131 132 133 134
        self.out = ""
        if "/O" in arg_tuple[3]:
            self.out = "O"
        if "/IO" in arg_tuple[3]:
            self.out = "IO"
A
Andrey Kamaev 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159


class FuncInfo(object):
    def __init__(self, decl): # [ funcname, return_ctype, [modifiers], [args] ]
        name = re.sub(r"^cv\.", "", decl[0])
        self.cname = name.replace(".", "::")
        classname = ""
        dpos = name.rfind(".")
        if dpos >= 0:
            classname = name[:dpos]
            name = name[dpos+1:]
        self.classname = classname
        self.jname = self.name = name
        if "[" in name:
            self.jname = "getelem"
        for m in decl[2]:
            if m.startswith("="):
                self.jname = m[1:]
        self.jn_name = "n_" + self.jname
        self.jni_name= re.sub(r"_", "_1", self.jn_name)
        if self.classname:
            self.jni_name = "00024" + self.classname + "_" + self.jni_name
        self.static = ["","static"][ "/S" in decl[2] ]
        self.ctype = decl[1] or ""
        self.args = []
160 161 162
        #self.jni_suffix = "__"
        #if self.classname and self.ctype and not self.static: # non-static class methods except c-tors
        #    self.jni_suffix += "J" # artifical 'self'
A
Andrey Kamaev 已提交
163 164 165
        for a in decl[3]:
            ai = ArgInfo(a)
            self.args.append(ai)
166
        #    self.jni_suffix += ctype2j.get(ai.ctype, ["","","",""])[3]
A
Andrey Kamaev 已提交
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192



class FuncFamilyInfo(object):
    def __init__(self, decl): # [ funcname, return_ctype, [modifiers], [args] ]
        self.funcs = []
        self.funcs.append( FuncInfo(decl) )
        self.jname = self.funcs[0].jname
        self.isconstructor = self.funcs[0].name == self.funcs[0].classname



    def add_func(self, fi):
        self.funcs.append( fi )


class JavaWrapperGenerator(object):
    def __init__(self):
        self.clear()

    def clear(self):
        self.classes = { "Mat" : ClassInfo([ 'class Mat', [], [] ]) }
        self.funcs = {}
        self.consts = [] # using a list to save the occurence order
        self.module = ""
        self.java_code = StringIO()
193
        self.jn_code = StringIO()
A
Andrey Kamaev 已提交
194 195
        self.cpp_code = StringIO()
        self.ported_func_counter = 0
196 197 198
        self.ported_func_list = []
        self.skipped_func_list = []
        self.total_func_counter = 0
A
Andrey Kamaev 已提交
199 200 201 202 203 204 205 206

    def add_class(self, decl):
        classinfo = ClassInfo(decl)
        if classinfo.name in self.classes:
            print "Generator error: class %s (%s) is duplicated" % \
                    (classinfo.name, classinfo.cname)
            sys.exit(-1)
        self.classes[classinfo.name] = classinfo
207
        if classinfo.name in type_dict:
A
Andrey Kamaev 已提交
208 209
            print "Duplicated class: " + classinfo.name
            sys.exit(-1)
210
        type_dict[classinfo.name] = \
211 212 213
            { "j_type" : classinfo.name,
              "jn_type" : "long", "jn_args" : (("__int64", ".nativeObj"),),
              "jni_name" : "(*("+classinfo.name+"*)%(n)s_nativeObj)", "jni_type" : "jlong",
214
              "suffix" : "J" }
A
Andrey Kamaev 已提交
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243


    def add_const(self, decl): # [ "const cname", val, [], [] ]
        consts = self.consts
        name = decl[0].replace("const ", "").strip()
        name = re.sub(r"^cv\.", "", name)
        cname = name.replace(".", "::")
        # check if it's a class member
        dpos = name.rfind(".")
        if dpos >= 0:
            classname = name[:dpos]
            name = name[dpos+1:]
            if classname in self.classes:
                consts = self.classes[classname].consts
            else:
                # this class isn't wrapped
                # skipping this const
                return
        constinfo = ConstInfo(cname, name, decl[1])
        # checking duplication
        for c in consts:
            if c.name == constinfo.name:
                print "Generator error: constant %s (%s) is duplicated" \
                        % (constinfo.name, constinfo.cname)
                sys.exit(-1)
        consts.append(constinfo)

    def add_func(self, decl):
        ffi = FuncFamilyInfo(decl)
244 245 246
	if ffi.jname in setManualFunctions :
		print "Found function, which is ported manually: " + ffi.jname 
		return None
A
Andrey Kamaev 已提交
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
        func_map = self.funcs
        classname = ffi.funcs[0].classname
        if classname:
            if classname in self.classes:
                func_map = self.classes[classname].methods
            else:
                print "Generator error: the class %s for method %s is missing" % \
                        (classname, ffi.jname)
                sys.exit(-1)
        if ffi.jname in func_map:
            func_map[ffi.jname].add_func(ffi.funcs[0])
        else:
            func_map[ffi.jname] = ffi

    def save(self, path, name, buf):
        f = open(path + "/" + name, "wt")
        f.write(buf.getvalue())
        f.close()

    def gen(self, srcfiles, module, output_path):
        self.clear()
        self.module = module
        parser = hdr_parser.CppHeaderParser()

        # step 1: scan the headers and build more descriptive maps of classes, consts, functions
        for hdr in srcfiles:
            decls = parser.parse(hdr)
            for decl in decls:
                name = decl[0]
                if name.startswith("struct") or name.startswith("class"):
                    self.add_class(decl)
                    pass
                elif name.startswith("const"):
                    self.add_const(decl)
                else: # function
                    self.add_func(decl)
                    pass

        # java module header
286
        self.java_code.write("package org.opencv;\n\npublic class %s {\n" % module)
A
Andrey Kamaev 已提交
287

288 289 290 291
        if module == "core":
            self.java_code.write(\
"""
    private static final int
A
Andrey Kamaev 已提交
292 293 294 295 296 297 298
            CV_8U  = 0,
            CV_8S  = 1,
            CV_16U = 2,
            CV_16S = 3,
            CV_32S = 4,
            CV_32F = 5,
            CV_64F = 6,
299 300
            CV_USRTYPE1 = 7;

301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
    //Manual ported functions

    // C++: minMaxLoc(Mat src, double* minVal, double* maxVal=0, Point* minLoc=0, Point* maxLoc=0, InputArray mask=noArray()) 
    //javadoc: minMaxLoc
    public static class MinMaxLocResult {
        public double minVal;
        public double maxVal;
        public Point minLoc;
        public Point maxLoc;

	public MinMaxLocResult() {
	    minVal=0; maxVal=0;
	    minLoc=new Point();
	    maxLoc=new Point();
	}
    }
    public static MinMaxLocResult minMaxLoc(Mat src, Mat mask) {
        MinMaxLocResult res = new MinMaxLocResult();
        long maskNativeObj=0;
        if (mask != null) {
                maskNativeObj=mask.nativeObj;
        }
        double resarr[] = n_minMaxLoc(src.nativeObj, maskNativeObj);
        res.minVal=resarr[0];
        res.maxVal=resarr[1];
        res.minLoc.x=resarr[2];
        res.minLoc.y=resarr[3];
        res.maxLoc.x=resarr[4];
        res.maxLoc.y=resarr[5];
        return res;
    }
    public static MinMaxLocResult minMaxLoc(Mat src) {
        return minMaxLoc(src, null);
    }
    private static native double[] n_minMaxLoc(long src_nativeObj, long mask_nativeObj);


338 339 340 341 342 343
""" )

        if module == "imgproc":
            self.java_code.write(\
"""
    public static final int
A
Andrey Kamaev 已提交
344 345 346 347 348 349
            IPL_BORDER_CONSTANT = 0,
            IPL_BORDER_REPLICATE = 1,
            IPL_BORDER_REFLECT = 2,
            IPL_BORDER_WRAP = 3,
            IPL_BORDER_REFLECT_101 = 4,
            IPL_BORDER_TRANSPARENT = 5;
350
""" )
351

A
Andrey Pavlenko 已提交
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
        if module == "calib3d":
            self.java_code.write(\
"""
    public static final int
            CV_LMEDS = 4,
            CV_RANSAC = 8,
            CV_FM_LMEDS = CV_LMEDS,
            CV_FM_RANSAC = CV_RANSAC;

    public static final int
            CV_FM_7POINT = 1,
            CV_FM_8POINT = 2;

    public static final int
            CV_CALIB_USE_INTRINSIC_GUESS = 1,
            CV_CALIB_FIX_ASPECT_RATIO = 2,
            CV_CALIB_FIX_PRINCIPAL_POINT = 4,
            CV_CALIB_ZERO_TANGENT_DIST = 8,
            CV_CALIB_FIX_FOCAL_LENGTH = 16,
            CV_CALIB_FIX_K1 = 32,
            CV_CALIB_FIX_K2 = 64,
            CV_CALIB_FIX_K3 = 128,
            CV_CALIB_FIX_K4 = 2048,
            CV_CALIB_FIX_K5 = 4096,
            CV_CALIB_FIX_K6 = 8192,
            CV_CALIB_RATIONAL_MODEL = 16384,
            CV_CALIB_FIX_INTRINSIC = 256,
            CV_CALIB_SAME_FOCAL_LENGTH = 512,
            CV_CALIB_ZERO_DISPARITY = 1024;
""" )

383 384 385 386 387 388 389 390
        # java native stuff
        self.jn_code.write("""
    //
    // native stuff
    //
    static { System.loadLibrary("opencv_java");	}
""")

A
Andrey Kamaev 已提交
391 392
        # cpp module header
        self.cpp_code.write(\
393 394 395
"""//
// This file is auto-generated, please don't edit!
//
A
Andrey Kamaev 已提交
396 397

#include <jni.h>
398 399

#ifdef DEBUG
400
#include <android/log.h>
401 402
#define MODULE_LOG_TAG "OpenCV.%s"
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, MODULE_LOG_TAG, __VA_ARGS__))
403
#endif // DEBUG
A
Andrey Kamaev 已提交
404

405
""" % module)
A
Andrey Kamaev 已提交
406 407
        self.cpp_code.write( "\n".join(['#include "opencv2/%s/%s"' % (module, os.path.basename(f)) \
                            for f in srcfiles]) )
408
        self.cpp_code.write('\nusing namespace cv;\n')
409
        self.cpp_code.write('\n\nextern "C" {\n\n')
A
Andrey Kamaev 已提交
410 411 412 413 414 415 416 417

        # step 2: generate the code for global constants
        self.gen_consts()

        # step 3: generate the code for all the global functions
        self.gen_funcs()

        # step 4: generate code for the classes
418
        self.gen_classes()
A
Andrey Kamaev 已提交
419

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
        if module == "core":
            self.cpp_code.write(\
"""
JNIEXPORT jdoubleArray JNICALL Java_org_opencv_core_n_1minMaxLoc
  (JNIEnv* env, jclass cls, jlong src_nativeObj, jlong mask_nativeObj)
{
    try {
#ifdef DEBUG
        LOGD("core::n_1minMaxLoc()");
#endif // DEBUG

        jdoubleArray result;
        result = env->NewDoubleArray(6);
        if (result == NULL) {
            return NULL; /* out of memory error thrown */
        }
        
        Mat& src = *((Mat*)src_nativeObj);
    
        double minVal, maxVal;
        Point minLoc, maxLoc;
        if (mask_nativeObj != 0) {
            Mat& mask = *((Mat*)mask_nativeObj);
            minMaxLoc(src, &minVal, &maxVal, &minLoc, &maxLoc, mask);
        } else {
            minMaxLoc(src, &minVal, &maxVal, &minLoc, &maxLoc);
        }
            
        jdouble fill[6];
        fill[0]=minVal;
        fill[1]=maxVal;
        fill[2]=minLoc.x;
        fill[3]=minLoc.y;
        fill[4]=maxLoc.x;
        fill[5]=maxLoc.y;
    
        env->SetDoubleArrayRegion(result, 0, 6, fill);

	return result;

    } catch(cv::Exception e) {
#ifdef DEBUG
        LOGD("core::n_1minMaxLoc() catched cv::Exception: %s", e.what());
#endif // DEBUG
        jclass je = env->FindClass("org/opencv/CvException");
        if(!je) je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, e.what());
        return NULL;
    } catch (...) {
#ifdef DEBUG
        LOGD("core::n_1minMaxLoc() catched unknown exception (...)");
#endif // DEBUG
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {$module::$fname()}");
        return NULL;
    }
}
""")

479
        # module tail
480
        self.java_code.write("\n\n" + self.jn_code.getvalue() + "\n")
A
Andrey Kamaev 已提交
481
        self.java_code.write("}\n")
482
        self.cpp_code.write('} // extern "C"\n')
A
Andrey Kamaev 已提交
483 484 485

        self.save(output_path, module+".java", self.java_code)
        self.save(output_path, module+".cpp",  self.cpp_code)
486 487 488 489 490 491 492 493 494 495 496
        # report
        report = StringIO()
        report.write("PORTED FUNCs LIST (%i of %i):\n\n" % \
            (self.ported_func_counter, self.total_func_counter) \
        )
        report.write("\n".join(self.ported_func_list))
        report.write("\n\nSKIPPED FUNCs LIST (%i of %i):\n\n" % \
            (self.total_func_counter - self.ported_func_counter, self.total_func_counter) \
        )
        report.write("".join(self.skipped_func_list))
        self.save(output_path, module+".txt", report)
A
Andrey Kamaev 已提交
497

498
        print "Done %i of %i funcs." % (self.ported_func_counter, self.total_func_counter)
A
Andrey Kamaev 已提交
499 500 501 502 503 504 505 506 507 508 509 510


    def gen_consts(self):
        # generate the code for global constants
        if self.consts:
            self.java_code.write("""
    public static final int
            """ + """,
            """.join(["%s = %s" % (c.name, c.value) for c in self.consts]) + \
            ";\n\n")


511
    def gen_func(self, fi, isoverload, jn_code):
512 513 514 515

        if fi.name in func_ignore_list: # skip irrelevant funcs
            return

516
        self.total_func_counter += 1
A
Andrey Kamaev 已提交
517

518
        # // C++: c_decl
A
Andrey Kamaev 已提交
519
        # e.g:
520
        # //  C++: void add(Mat src1, Mat src2, Mat dst, Mat mask = Mat(), int dtype = -1)
A
Andrey Kamaev 已提交
521 522 523
        c_decl = "%s %s %s(%s)" % \
            ( fi.static, fi.ctype, fi.cname, \
              ", ".join(a.ctype + " " + a.name + [""," = "+a.defval][bool(a.defval)] for a in fi.args) )
524
        indent = " " * 4
A
Andrey Kamaev 已提交
525
        if fi.classname:
526
            indent += " " * 4
527 528
        # java comment
        self.java_code.write( "\n%s// C++: %s\n" % (indent, c_decl) )
A
Andrey Kamaev 已提交
529
        # check if we 'know' all the types
530
        if fi.ctype not in type_dict: # unsupported ret type
531
            msg = "// Return type '%s' is not supported, skipping the function\n\n" % fi.ctype
532
            self.skipped_func_list.append(c_decl + "\n" + msg)
533
            self.java_code.write( indent + msg )
534
            #self.cpp_code.write( msg )
A
Andrey Kamaev 已提交
535 536 537
            print "SKIP:", c_decl, "\n\tdue to RET type", fi.ctype
            return
        for a in fi.args:
538
            if a.ctype not in type_dict:
539
                msg = "// Unknown type '%s' (%s), skipping the function\n\n" % (a.ctype, a.out or "I")
540
                self.skipped_func_list.append(c_decl + "\n" + msg)
541
                self.java_code.write( indent + msg )
542
                #self.cpp_code.write( msg )
543
                print "SKIP:", c_decl, "\n\tdue to ARG type", a.ctype, "/" + (a.out or "I")
A
Andrey Kamaev 已提交
544
                return
545
            if a.ctype != "Mat" and "jn_args" in type_dict[a.ctype] and a.out: # complex out args not yet supported
546
                msg = "// Unsupported OUT type '%s' (%s), skipping the function\n\n" % (a.ctype, a.out or "I")
547
                self.skipped_func_list.append(c_decl + "\n" + msg)
548
                self.java_code.write( indent + msg )
549
                #self.cpp_code.write( msg )
550
                print "SKIP:", c_decl, "\n\tdue to OUT ARG of type", a.ctype, "/" + (a.out or "I")
A
Andrey Kamaev 已提交
551 552 553
                return

        self.ported_func_counter += 1
554
        self.ported_func_list.append(c_decl)
555

556 557 558 559
        # jn & cpp comment
        jn_code.write( "\n%s// C++: %s\n" % (indent, c_decl) )
        self.cpp_code.write( "\n//\n// %s\n//\n" % c_decl )

A
Andrey Kamaev 已提交
560
        # java args
561
        args = fi.args[:] # copy
A
Andrey Kamaev 已提交
562 563 564 565 566
        if args and args[-1].defval:
            isoverload = True

        while True:

567
             # java native method args
A
Andrey Kamaev 已提交
568 569 570
            jn_args = []
            # jni (cpp) function args
            jni_args = [ArgInfo([ "env", "env", "", [], "" ]), ArgInfo([ "cls", "cls", "", [], "" ])]
571 572 573 574 575 576
            suffix = "__"
            if fi.classname and fi.ctype and not fi.static: # non-static class method except c-tor
                # adding 'self'
                jn_args.append ( ArgInfo([ "__int64", "nativeObj", "", [], "" ]) )
                jni_args.append( ArgInfo([ "__int64", "self", "", [], "" ]) )
                suffix += "J"
A
Andrey Kamaev 已提交
577
            for a in args:
578 579 580
                suffix += type_dict[a.ctype].get("suffix") or ""
                fields = type_dict[a.ctype].get("jn_args") or []
                if fields: # complex type
A
Andrey Kamaev 已提交
581
                    for f in fields:
582
                        jn_args.append ( ArgInfo([ f[0], a.name + f[1], "", [], "" ]) )
A
Andrey Kamaev 已提交
583 584
                        jni_args.append( ArgInfo([ f[0], a.name + f[1].replace(".","_"), "", [], "" ]) )
                else:
585
                    jn_args.append(a)
A
Andrey Kamaev 已提交
586
                    jni_args.append(a)
587 588 589 590 591
                if a.out:
                    if "vector" in a.ctype: # -> Mat
                        pass
                    else: # -> double[6]
                        pass
A
Andrey Kamaev 已提交
592 593 594 595 596

            # java part:
            # private java NATIVE method decl
            # e.g.
            # private static native void n_add(long src1, long src2, long dst, long mask, int dtype);
597
            jn_code.write( Template(\
598
                "${indent}private static native $jn_type $jn_name($jn_args);\n").substitute(\
A
Andrey Kamaev 已提交
599
                indent = indent, \
600
                jn_type = type_dict[fi.ctype].get("jn_type", "double[]"), \
601 602
                jn_name = fi.jn_name, \
                jn_args = ", ".join(["%s %s" % (type_dict[a.ctype]["jn_type"], a.name.replace(".","_")) for a in jn_args])
A
Andrey Kamaev 已提交
603 604 605
            ) );

            # java part:
606 607 608 609 610 611 612 613 614

            #java doc comment
            f_name = fi.name
            if fi.classname:
                f_name = fi.classname + "::" + fi.name
            self.java_code.write(indent + "//javadoc: " + f_name + "(%s)\n" % \
                ", ".join([a.name for a in args])
            )

A
Andrey Kamaev 已提交
615 616 617 618
            # public java wrapper method impl (calling native one above)
            # e.g.
            # public static void add( Mat src1, Mat src2, Mat dst, Mat mask, int dtype )
            # { n_add( src1.nativeObj, src2.nativeObj, dst.nativeObj, mask.nativeObj, dtype );  }
619
            impl_code = "return $jn_name($jn_args_call);"
A
Andrey Kamaev 已提交
620
            if fi.ctype == "void":
621
                impl_code = "$jn_name($jn_args_call);"
A
Andrey Kamaev 已提交
622
            elif fi.ctype == "": # c-tor
623
                impl_code = "nativeObj = $jn_name($jn_args_call);"
A
Andrey Kamaev 已提交
624
            elif fi.ctype in self.classes: # wrapped class
625
                impl_code = "return new %s( $jn_name($jn_args_call) );" % \
A
Andrey Kamaev 已提交
626
                    self.classes[fi.ctype].jname
627 628
            elif "jn_type" not in type_dict[fi.ctype]:
                impl_code = "return new "+type_dict[fi.ctype]["j_type"]+"( $jn_name($jn_args_call) );"
A
Andrey Kamaev 已提交
629 630 631 632 633 634

            static = "static"
            if fi.classname:
                static = fi.static

            self.java_code.write( Template(\
635
                "${indent}public $static $j_type $j_name($j_args)").substitute(\
A
Andrey Kamaev 已提交
636 637
                indent = indent, \
                static=static, \
638
                j_type=type_dict[fi.ctype]["j_type"], \
A
Andrey Kamaev 已提交
639
                j_name=fi.jname, \
640
                j_args=", ".join(["%s %s" % (type_dict[a.ctype]["j_type"], a.name) for a in args]) \
A
Andrey Kamaev 已提交
641 642
            ) )

643
            self.java_code.write( Template("\n$indent{ " + impl_code + " }\n").substitute(\
A
Andrey Kamaev 已提交
644 645
                indent = indent, \
                jn_name=fi.jn_name, \
646
                jn_args_call=", ".join( [a.name for a in jn_args] )\
A
Andrey Kamaev 已提交
647 648 649
            ) )

            # cpp part:
650 651
            # jni_func(..) { _retval_ = cv_func(..); return _retval_; }
            ret = "return _retval_;"
652
            default = "return 0;"
A
Andrey Kamaev 已提交
653
            if fi.ctype == "void":
654 655 656 657
                ret = "return;"
                default = "return;"
            elif not fi.ctype: # c-tor
                ret = "return (jlong) _retval_;"
658
            elif fi.ctype == "string":
659
                ret = "return env->NewStringUTF(_retval_.c_str());"
660
                default = 'return env->NewStringUTF("");'
661
            elif fi.ctype in self.classes: # wrapped class:
662 663 664 665 666 667 668
                ret = "return (jlong) new %s(_retval_);" % fi.ctype
            elif "jni_type" not in type_dict[fi.ctype]: # jdoubleArray
                ret = "double _tmp_[6]; " + \
                      "/* "+fi.ctype+"_to_double6(_retval_, _tmp_); */" + \
                      "jdoubleArray _da_ = env->NewDoubleArray(6); " + \
                      "env->SetDoubleArrayRegion(_da_, 0, 6, _tmp_); " + \
                      "return _da_;"
A
Andrey Kamaev 已提交
669 670 671

            cvname = "cv::" + fi.name
            j2cvargs = []
672 673 674
            retval = fi.ctype + " _retval_ = "
            if fi.ctype == "void":
                retval = ""
A
Andrey Kamaev 已提交
675 676
            if fi.classname:
                if not fi.ctype: # c-tor
677 678
                    retval = fi.classname + "* _retval_ = "
                    cvname = "new " + fi.classname
A
Andrey Kamaev 已提交
679
                elif fi.static:
680
                    cvname = "%s::%s" % (fi.classname, fi.name)
A
Andrey Kamaev 已提交
681
                else:
682
                    cvname = "me->" + fi.name
A
Andrey Kamaev 已提交
683
                    j2cvargs.append(\
684
                        "%(cls)s* me = (%(cls)s*) self; //TODO: check for NULL" \
685
                            % { "cls" : fi.classname} \
A
Andrey Kamaev 已提交
686 687 688
                    )
            cvargs = []
            for a in args:
689 690 691
                cvargs.append( type_dict[a.ctype].get("jni_name", "%(n)s") % {"n" : a.name})
                if "jni_var" in type_dict[a.ctype]: # complex type
                    j2cvargs.append(type_dict[a.ctype]["jni_var"] % {"n" : a.name} + ";")
A
Andrey Kamaev 已提交
692

693
            rtype = type_dict[fi.ctype].get("jni_type", "jdoubleArray")
A
Andrey Kamaev 已提交
694 695 696 697 698 699
            self.cpp_code.write ( Template( \
"""

JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_$fname
  ($args)
{
700
    try {
701 702 703
#ifdef DEBUG
        LOGD("$module::$fname()");
#endif // DEBUG
704
        $j2cv
705 706
        $retval$cvname( $cvargs );
        $ret
707
    } catch(cv::Exception e) {
708 709 710
#ifdef DEBUG
        LOGD("$module::$fname() catched cv::Exception: %s", e.what());
#endif // DEBUG
711 712 713
        jclass je = env->FindClass("org/opencv/CvException");
        if(!je) je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, e.what());
714
        $default
715
    } catch (...) {
716 717 718
#ifdef DEBUG
        LOGD("$module::$fname() catched unknown exception (...)");
#endif // DEBUG
719 720
        jclass je = env->FindClass("java/lang/Exception");
        env->ThrowNew(je, "Unknown exception in JNI code {$module::$fname()}");
721
        $default
722
    }
A
Andrey Kamaev 已提交
723 724
}

725

A
Andrey Kamaev 已提交
726 727 728 729
""" ).substitute( \
        rtype = rtype, \
        module = self.module, \
        fname = fi.jni_name + ["",suffix][isoverload], \
730
        args = ", ".join(["%s %s" % (type_dict[a.ctype].get("jni_type"), a.name) for a in jni_args]), \
731
        j2cv = "\n        ".join([a for a in j2cvargs]), \
A
Andrey Kamaev 已提交
732 733 734
        ret = ret, \
        cvname = cvname, \
        cvargs = ", ".join([a for a in cvargs]), \
735 736
        default = default, \
        retval = retval, \
A
Andrey Kamaev 已提交
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754
    ) )

            # processing args with default values
            if args and args[-1].defval:
                a = args.pop()
            else:
                break



    def gen_funcs(self):
        # generate the code for all the global functions
        indent = "\t"
        fflist = self.funcs.items()
        fflist.sort()
        for name, ffi in fflist:
            assert not ffi.funcs[0].classname, "Error: global func is a class member - "+name
            for fi in ffi.funcs:
755
                self.gen_func(fi, len(ffi.funcs)>1, self.jn_code)
A
Andrey Kamaev 已提交
756 757 758 759


    def gen_classes(self):
        # generate code for the classes (their methods and consts)
760 761
        indent = " " * 4
        indent_m = indent + " " * 4
A
Andrey Kamaev 已提交
762 763 764
        classlist = self.classes.items()
        classlist.sort()
        for name, ci in classlist:
765
            if name == "Mat" or name in class_ignore_list:
766 767
                continue
            self.java_code.write( "\n\n" + indent + "// C++: class %s" % (ci.cname) + "\n" )
768
            self.java_code.write( indent + "//javadoc: " + name + "\n" ) #java doc comment
A
Andrey Kamaev 已提交
769 770 771 772 773 774 775
            self.java_code.write( indent + "public static class %s {\n\n" % (ci.jname) )
            # self
            self.java_code.write( indent_m + "protected final long nativeObj;\n" )
            self.java_code.write( indent_m + "protected %s(long addr) { nativeObj = addr; }\n\n" \
                % name );
            # constants
            if ci.consts:
776
                prefix = "\n" + indent_m + "\t"
A
Andrey Kamaev 已提交
777 778 779
                s = indent_m + "public static final int" + prefix +\
                    ("," + prefix).join(["%s = %s" % (c.name, c.value) for c in ci.consts]) + ";\n\n"
                self.java_code.write( s )
780 781
            # methods
            jn_code = StringIO()
A
Andrey Kamaev 已提交
782 783 784 785 786 787
            # c-tors
            fflist = ci.methods.items()
            fflist.sort()
            for n, ffi in fflist:
                if ffi.isconstructor:
                    for fi in ffi.funcs:
788
                        self.gen_func(fi, len(ffi.funcs)>1, jn_code)
A
Andrey Kamaev 已提交
789 790 791 792
            self.java_code.write( "\n" )
            for n, ffi in fflist:
                if not ffi.isconstructor:
                    for fi in ffi.funcs:
793
                        self.gen_func(fi, len(ffi.funcs)>1, jn_code)
A
Andrey Kamaev 已提交
794

795 796 797 798 799 800 801 802 803 804 805 806 807
            # finalize()
            self.java_code.write(
"""
        @Override
        protected void finalize() throws Throwable {
            n_delete(nativeObj);
            super.finalize();
        }

"""
            )

            self.java_code.write(indent_m + "// native stuff\n\n")
808
            self.java_code.write(indent_m + 'static { System.loadLibrary("opencv_java"); }\n')
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
            self.java_code.write( jn_code.getvalue() )
            self.java_code.write(
"""
        // native support for java finalize()
        private static native void n_delete(long nativeObj);
"""
            )
            self.java_code.write("\n" + indent + "}\n\n")

            # native support for java finalize()
            self.cpp_code.write( \
"""
//
//  native support for java finalize()
//  static void %(cls)s::n_delete( __int64 self )
//

JNIEXPORT void JNICALL Java_org_opencv_%(module)s_00024%(cls)s_n_1delete
  (JNIEnv* env, jclass cls, jlong self)
{
    delete (%(cls)s*) self;
}

""" % {"module" : module, "cls" : name}
            )
A
Andrey Kamaev 已提交
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855


if __name__ == "__main__":
    if len(sys.argv) < 4:
        print "Usage:\n", \
            os.path.basename(sys.argv[0]), \
            "<full path to hdr_parser.py> <module name> <C++ header> [<C++ header>...]"
        print "Current args are: ", ", ".join(["'"+a+"'" for a in sys.argv])
        exit(0)

    dstdir = "."
    hdr_parser_path = os.path.abspath(sys.argv[1])
    if hdr_parser_path.endswith(".py"):
        hdr_parser_path = os.path.dirname(hdr_parser_path)
    sys.path.append(hdr_parser_path)
    import hdr_parser
    module = sys.argv[2]
    srcfiles = sys.argv[3:]
    print "Generating module '" + module + "' from headers:\n\t" + "\n\t".join(srcfiles)
    generator = JavaWrapperGenerator()
    generator.gen(srcfiles, module, dstdir)