[
  {
    "path": "README.md",
    "content": "# ncnn_paddleocr\nThis is a sample paddleocr ncnn android project, it depends on ncnn library and opencv  \nhttps://github.com/Tencent/ncnn  \nhttps://github.com/nihui/opencv-mobile\n\nconvert paddleocr light model to ncnn,you can use it by ncnn.  \nthe infer code you can use chineseocr_lite project.  \nPS：if you use angle model plz change the input shape dstHeight from 32 to 48  \n# model support  \n## text detection  \n1.mv3dbnet-sim-op(paddleocr_mobile)  \n2.pdocrv2.0_det-op(PP-OCRv2)  \n3.ch_PP-OCRv3_det(PP-OCRv3)  \n4.ch_PP-OCRv4_det(PP-OCRv4)  [model](https://github.com/FeiGeChuanShu/ncnn_ppstructure)  \n## text angle cls  \n1.angle-sim-op  \n## text recognition  \n1.mv3rec-sim-op(paddleocr_mobile)  \n2.pdocrv2.0_rec-op(PP-OCRv2)  \n3.ch_PP-OCRv3_rec(PP-OCRv3)  \n4.ch_PP-OCRv4_rec(PP-OCRv4)  [model](https://github.com/FeiGeChuanShu/ncnn_ppstructure)  \n## how to build and run\n### step1\nhttps://github.com/Tencent/ncnn/releases\n\n* Download ncnn-YYYYMMDD-android-vulkan.zip or build ncnn for android yourself\n* Extract ncnn-YYYYMMDD-android-vulkan.zip into **app/src/main/jni** and change the **ncnn_DIR** path to yours in **app/src/main/jni/CMakeLists.txt**\n\n### step2\nhttps://github.com/nihui/opencv-mobile\n\n* Download opencv-mobile-XYZ-android.zip\n* Extract opencv-mobile-XYZ-android.zip into **app/src/main/jni** and change the **OpenCV_DIR** path to yours in **app/src/main/jni/CMakeLists.txt**\n\n### step3\n* Open this project with Android Studio, build it and enjoy!  \n\n## screenshot  \n![](screenshot.png)  \n1.https://github.com/DayBreak-u/chineseocr_lite/tree/onnx/cpp_projects/OcrLiteNcnn  \n2.https://github.com/frotms/PaddleOCR2Pytorch  \n3.https://github.com/PaddlePaddle/PaddleOCR#PP-OCRv2  \n4.https://github.com/nihui/ncnn-android-yolov5  \n5.https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.5  \n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 24\n    buildToolsVersion \"29.0.2\"\n\n    defaultConfig {\n        applicationId \"com.tencent.paddleocrncnn\"\n        archivesBaseName = \"$applicationId\"\n\n        ndk {\n            moduleName \"ncnn\"\n            abiFilters \"armeabi-v7a\", \"arm64-v8a\"\n        }\n        minSdkVersion 24\n    }\n\n    externalNativeBuild {\n        cmake {\n            version \"3.10.2\"\n            path file('src/main/jni/CMakeLists.txt')\n        }\n    }\n    dependencies {\n        implementation 'com.android.support:support-v4:24.0.0'\n    }\n}\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n      package=\"com.tencent.paddleocrncnn\"\n      android:versionCode=\"1\"\n      android:versionName=\"1.1\">\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>\n    <application android:label=\"@string/app_name\" >\n        <activity android:name=\"MainActivity\"\n                  android:label=\"@string/app_name\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <provider\n            android:name=\"android.support.v4.content.FileProvider\"\n            android:authorities=\"com.tencent.paddleocrncnn.fileprovider\"\n            android:exported=\"false\"\n            android:grantUriPermissions=\"true\">\n            <meta-data\n                android:name=\"android.support.FILE_PROVIDER_PATHS\"\n                android:resource=\"@xml/file_paths\" />\n        </provider>\n    </application>\n</manifest> \n"
  },
  {
    "path": "app/src/main/assets/ch_PP-OCRv3_det.param",
    "content": "7767517\n153 185\nInput                    x                        0 1 input\nConvolution              Conv_0                   1 1 input hardswish_0.tmp_0 0=8 1=3 3=2 4=1 5=1 6=216 9=6 -23310=2,1.666667e-01,5.000000e-01\nSplit                    splitncnn_0              1 2 hardswish_0.tmp_0 hardswish_0.tmp_0_splitncnn_0 hardswish_0.tmp_0_splitncnn_1\nConvolution              Conv_1                   1 1 hardswish_0.tmp_0_splitncnn_1 relu_0.tmp_0 0=8 1=1 5=1 6=64 9=1\nConvolutionDepthWise     Conv_2                   1 1 relu_0.tmp_0 relu_1.tmp_0 0=8 1=3 4=1 5=1 6=72 7=8 9=1\nConvolution              Conv_3                   1 1 relu_1.tmp_0 conv2d_213.tmp_0 0=8 1=1 5=1 6=64\nBinaryOp                 Add_1                    2 1 hardswish_0.tmp_0_splitncnn_0 conv2d_213.tmp_0 elementwise_add_0\nConvolution              Conv_4                   1 1 elementwise_add_0 relu_2.tmp_0 0=32 1=1 5=1 6=256 9=1\nConvolutionDepthWise     Conv_5                   1 1 relu_2.tmp_0 relu_3.tmp_0 0=32 1=3 3=2 4=1 5=1 6=288 7=32 9=1\nConvolution              Conv_6                   1 1 relu_3.tmp_0 conv2d_215.tmp_0 0=16 1=1 5=1 6=512\nSplit                    splitncnn_1              1 2 conv2d_215.tmp_0 conv2d_215.tmp_0_splitncnn_0 conv2d_215.tmp_0_splitncnn_1\nConvolution              Conv_7                   1 1 conv2d_215.tmp_0_splitncnn_1 relu_4.tmp_0 0=40 1=1 5=1 6=640 9=1\nConvolutionDepthWise     Conv_8                   1 1 relu_4.tmp_0 relu_5.tmp_0 0=40 1=3 4=1 5=1 6=360 7=40 9=1\nConvolution              Conv_9                   1 1 relu_5.tmp_0 conv2d_217.tmp_0 0=16 1=1 5=1 6=640\nBinaryOp                 Add_2                    2 1 conv2d_215.tmp_0_splitncnn_0 conv2d_217.tmp_0 elementwise_add_1\nSplit                    splitncnn_2              1 2 elementwise_add_1 elementwise_add_1_splitncnn_0 elementwise_add_1_splitncnn_1\nConvolution              Conv_10                  1 1 elementwise_add_1_splitncnn_1 relu_6.tmp_0 0=40 1=1 5=1 6=640 9=1\nConvolutionDepthWise     Conv_11                  1 1 relu_6.tmp_0 relu_7.tmp_0 0=40 1=5 3=2 4=2 5=1 6=1000 7=40 9=1\nConvolution              Conv_12                  1 1 relu_7.tmp_0 conv2d_219.tmp_0 0=24 1=1 5=1 6=960\nSplit                    splitncnn_3              1 2 conv2d_219.tmp_0 conv2d_219.tmp_0_splitncnn_0 conv2d_219.tmp_0_splitncnn_1\nConvolution              Conv_13                  1 1 conv2d_219.tmp_0_splitncnn_1 relu_8.tmp_0 0=64 1=1 5=1 6=1536 9=1\nConvolutionDepthWise     Conv_14                  1 1 relu_8.tmp_0 relu_9.tmp_0 0=64 1=5 4=2 5=1 6=1600 7=64 9=1\nConvolution              Conv_15                  1 1 relu_9.tmp_0 conv2d_221.tmp_0 0=24 1=1 5=1 6=1536\nBinaryOp                 Add_3                    2 1 conv2d_219.tmp_0_splitncnn_0 conv2d_221.tmp_0 elementwise_add_2\nSplit                    splitncnn_4              1 2 elementwise_add_2 elementwise_add_2_splitncnn_0 elementwise_add_2_splitncnn_1\nConvolution              Conv_16                  1 1 elementwise_add_2_splitncnn_1 relu_10.tmp_0 0=64 1=1 5=1 6=1536 9=1\nConvolutionDepthWise     Conv_17                  1 1 relu_10.tmp_0 relu_11.tmp_0 0=64 1=5 4=2 5=1 6=1600 7=64 9=1\nConvolution              Conv_18                  1 1 relu_11.tmp_0 conv2d_223.tmp_0 0=24 1=1 5=1 6=1536\nBinaryOp                 Add_4                    2 1 elementwise_add_2_splitncnn_0 conv2d_223.tmp_0 elementwise_add_3\nSplit                    splitncnn_5              1 2 elementwise_add_3 elementwise_add_3_splitncnn_0 elementwise_add_3_splitncnn_1\nConvolution              Conv_19                  1 1 elementwise_add_3_splitncnn_1 hardswish_1.tmp_0 0=120 1=1 5=1 6=2880 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_20                  1 1 hardswish_1.tmp_0 hardswish_2.tmp_0 0=120 1=3 3=2 4=1 5=1 6=1080 7=120 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_21                  1 1 hardswish_2.tmp_0 conv2d_225.tmp_0 0=40 1=1 5=1 6=4800\nSplit                    splitncnn_6              1 2 conv2d_225.tmp_0 conv2d_225.tmp_0_splitncnn_0 conv2d_225.tmp_0_splitncnn_1\nConvolution              Conv_22                  1 1 conv2d_225.tmp_0_splitncnn_1 hardswish_3.tmp_0 0=104 1=1 5=1 6=4160 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_23                  1 1 hardswish_3.tmp_0 hardswish_4.tmp_0 0=104 1=3 4=1 5=1 6=936 7=104 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_24                  1 1 hardswish_4.tmp_0 conv2d_227.tmp_0 0=40 1=1 5=1 6=4160\nBinaryOp                 Add_9                    2 1 conv2d_225.tmp_0_splitncnn_0 conv2d_227.tmp_0 elementwise_add_4\nSplit                    splitncnn_7              1 2 elementwise_add_4 elementwise_add_4_splitncnn_0 elementwise_add_4_splitncnn_1\nConvolution              Conv_25                  1 1 elementwise_add_4_splitncnn_1 hardswish_5.tmp_0 0=96 1=1 5=1 6=3840 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_26                  1 1 hardswish_5.tmp_0 hardswish_6.tmp_0 0=96 1=3 4=1 5=1 6=864 7=96 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_27                  1 1 hardswish_6.tmp_0 conv2d_229.tmp_0 0=40 1=1 5=1 6=3840\nBinaryOp                 Add_12                   2 1 elementwise_add_4_splitncnn_0 conv2d_229.tmp_0 elementwise_add_5\nSplit                    splitncnn_8              1 2 elementwise_add_5 elementwise_add_5_splitncnn_0 elementwise_add_5_splitncnn_1\nConvolution              Conv_28                  1 1 elementwise_add_5_splitncnn_1 hardswish_7.tmp_0 0=96 1=1 5=1 6=3840 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_29                  1 1 hardswish_7.tmp_0 hardswish_8.tmp_0 0=96 1=3 4=1 5=1 6=864 7=96 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_30                  1 1 hardswish_8.tmp_0 conv2d_231.tmp_0 0=40 1=1 5=1 6=3840\nBinaryOp                 Add_15                   2 1 elementwise_add_5_splitncnn_0 conv2d_231.tmp_0 elementwise_add_6\nConvolution              Conv_31                  1 1 elementwise_add_6 hardswish_9.tmp_0 0=240 1=1 5=1 6=9600 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_32                  1 1 hardswish_9.tmp_0 hardswish_10.tmp_0 0=240 1=3 4=1 5=1 6=2160 7=240 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_33                  1 1 hardswish_10.tmp_0 conv2d_233.tmp_0 0=56 1=1 5=1 6=13440\nSplit                    splitncnn_9              1 2 conv2d_233.tmp_0 conv2d_233.tmp_0_splitncnn_0 conv2d_233.tmp_0_splitncnn_1\nConvolution              Conv_34                  1 1 conv2d_233.tmp_0_splitncnn_1 hardswish_11.tmp_0 0=336 1=1 5=1 6=18816 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_35                  1 1 hardswish_11.tmp_0 hardswish_12.tmp_0 0=336 1=3 4=1 5=1 6=3024 7=336 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_36                  1 1 hardswish_12.tmp_0 conv2d_235.tmp_0 0=56 1=1 5=1 6=18816\nBinaryOp                 Add_20                   2 1 conv2d_233.tmp_0_splitncnn_0 conv2d_235.tmp_0 elementwise_add_7\nSplit                    splitncnn_10             1 2 elementwise_add_7 elementwise_add_7_splitncnn_0 elementwise_add_7_splitncnn_1\nConvolution              Conv_37                  1 1 elementwise_add_7_splitncnn_1 hardswish_13.tmp_0 0=336 1=1 5=1 6=18816 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_38                  1 1 hardswish_13.tmp_0 hardswish_14.tmp_0 0=336 1=5 3=2 4=2 5=1 6=8400 7=336 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_39                  1 1 hardswish_14.tmp_0 conv2d_237.tmp_0 0=80 1=1 5=1 6=26880\nSplit                    splitncnn_11             1 2 conv2d_237.tmp_0 conv2d_237.tmp_0_splitncnn_0 conv2d_237.tmp_0_splitncnn_1\nConvolution              Conv_40                  1 1 conv2d_237.tmp_0_splitncnn_1 hardswish_15.tmp_0 0=480 1=1 5=1 6=38400 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_41                  1 1 hardswish_15.tmp_0 hardswish_16.tmp_0 0=480 1=5 4=2 5=1 6=12000 7=480 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_42                  1 1 hardswish_16.tmp_0 conv2d_239.tmp_0 0=80 1=1 5=1 6=38400\nBinaryOp                 Add_25                   2 1 conv2d_237.tmp_0_splitncnn_0 conv2d_239.tmp_0 elementwise_add_8\nSplit                    splitncnn_12             1 2 elementwise_add_8 elementwise_add_8_splitncnn_0 elementwise_add_8_splitncnn_1\nConvolution              Conv_43                  1 1 elementwise_add_8_splitncnn_1 hardswish_17.tmp_0 0=480 1=1 5=1 6=38400 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_44                  1 1 hardswish_17.tmp_0 hardswish_18.tmp_0 0=480 1=5 4=2 5=1 6=12000 7=480 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_45                  1 1 hardswish_18.tmp_0 conv2d_241.tmp_0 0=80 1=1 5=1 6=38400\nBinaryOp                 Add_28                   2 1 elementwise_add_8_splitncnn_0 conv2d_241.tmp_0 elementwise_add_9\nConvolution              Conv_46                  1 1 elementwise_add_9 hardswish_19.tmp_0 0=480 1=1 5=1 6=38400 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_47                  1 1 hardswish_19.tmp_0 conv2d_243.tmp_0 0=96 1=1 6=46080\nSplit                    splitncnn_13             1 3 conv2d_243.tmp_0 conv2d_243.tmp_0_splitncnn_0 conv2d_243.tmp_0_splitncnn_1 conv2d_243.tmp_0_splitncnn_2\nPooling                  GlobalAveragePool_0      1 1 conv2d_243.tmp_0_splitncnn_2 pool2d_0.tmp_0 0=1 4=1\nInnerProduct             Conv_48                  1 1 pool2d_0.tmp_0 relu_12.tmp_0 0=24 1=1 2=2304 9=1\nInnerProduct             Conv_49                  1 1 relu_12.tmp_0 conv2d_245.tmp_0 0=96 1=1 2=2304\nHardSigmoid              HardSigmoid_0            1 1 conv2d_245.tmp_0 hardsigmoid_0.tmp_0\nBinaryOp                 Mul_20                   2 1 conv2d_243.tmp_0_splitncnn_1 hardsigmoid_0.tmp_0 tmp_0 0=2\nBinaryOp                 Add_32                   2 1 conv2d_243.tmp_0_splitncnn_0 tmp_0 tmp_1\nSplit                    splitncnn_14             1 2 tmp_1 tmp_1_splitncnn_0 tmp_1_splitncnn_1\nConvolution              Conv_50                  1 1 elementwise_add_7_splitncnn_0 conv2d_246.tmp_0 0=96 1=1 6=5376\nSplit                    splitncnn_15             1 3 conv2d_246.tmp_0 conv2d_246.tmp_0_splitncnn_0 conv2d_246.tmp_0_splitncnn_1 conv2d_246.tmp_0_splitncnn_2\nPooling                  GlobalAveragePool_1      1 1 conv2d_246.tmp_0_splitncnn_2 pool2d_1.tmp_0 0=1 4=1\nInnerProduct             Conv_51                  1 1 pool2d_1.tmp_0 relu_13.tmp_0 0=24 1=1 2=2304 9=1\nInnerProduct             Conv_52                  1 1 relu_13.tmp_0 conv2d_248.tmp_0 0=96 1=1 2=2304\nHardSigmoid              HardSigmoid_1            1 1 conv2d_248.tmp_0 hardsigmoid_1.tmp_0\nBinaryOp                 Mul_21                   2 1 conv2d_246.tmp_0_splitncnn_1 hardsigmoid_1.tmp_0 tmp_2 0=2\nBinaryOp                 Add_35                   2 1 conv2d_246.tmp_0_splitncnn_0 tmp_2 tmp_3\nConvolution              Conv_53                  1 1 elementwise_add_3_splitncnn_0 conv2d_249.tmp_0 0=96 1=1 6=2304\nSplit                    splitncnn_16             1 3 conv2d_249.tmp_0 conv2d_249.tmp_0_splitncnn_0 conv2d_249.tmp_0_splitncnn_1 conv2d_249.tmp_0_splitncnn_2\nPooling                  GlobalAveragePool_2      1 1 conv2d_249.tmp_0_splitncnn_2 pool2d_2.tmp_0 0=1 4=1\nInnerProduct             Conv_54                  1 1 pool2d_2.tmp_0 relu_14.tmp_0 0=24 1=1 2=2304 9=1\nInnerProduct             Conv_55                  1 1 relu_14.tmp_0 conv2d_251.tmp_0 0=96 1=1 2=2304\nHardSigmoid              HardSigmoid_2            1 1 conv2d_251.tmp_0 hardsigmoid_2.tmp_0\nBinaryOp                 Mul_22                   2 1 conv2d_249.tmp_0_splitncnn_1 hardsigmoid_2.tmp_0 tmp_4 0=2\nBinaryOp                 Add_38                   2 1 conv2d_249.tmp_0_splitncnn_0 tmp_4 tmp_5\nConvolution              Conv_56                  1 1 elementwise_add_1_splitncnn_0 conv2d_252.tmp_0 0=96 1=1 6=1536\nSplit                    splitncnn_17             1 3 conv2d_252.tmp_0 conv2d_252.tmp_0_splitncnn_0 conv2d_252.tmp_0_splitncnn_1 conv2d_252.tmp_0_splitncnn_2\nPooling                  GlobalAveragePool_3      1 1 conv2d_252.tmp_0_splitncnn_2 pool2d_3.tmp_0 0=1 4=1\nInnerProduct             Conv_57                  1 1 pool2d_3.tmp_0 relu_15.tmp_0 0=24 1=1 2=2304 9=1\nInnerProduct             Conv_58                  1 1 relu_15.tmp_0 conv2d_254.tmp_0 0=96 1=1 2=2304\nHardSigmoid              HardSigmoid_3            1 1 conv2d_254.tmp_0 hardsigmoid_3.tmp_0\nBinaryOp                 Mul_23                   2 1 conv2d_252.tmp_0_splitncnn_1 hardsigmoid_3.tmp_0 tmp_6 0=2\nBinaryOp                 Add_41                   2 1 conv2d_252.tmp_0_splitncnn_0 tmp_6 tmp_7\nInterp                   Resize_0                 1 1 tmp_1_splitncnn_1 nearest_interp_v2_0.tmp_0 0=1 1=2.000000e+00 2=2.000000e+00\nBinaryOp                 Add_42                   2 1 tmp_3 nearest_interp_v2_0.tmp_0 tmp_8\nSplit                    splitncnn_18             1 2 tmp_8 tmp_8_splitncnn_0 tmp_8_splitncnn_1\nInterp                   Resize_1                 1 1 tmp_8_splitncnn_1 nearest_interp_v2_1.tmp_0 0=1 1=2.000000e+00 2=2.000000e+00\nBinaryOp                 Add_43                   2 1 tmp_5 nearest_interp_v2_1.tmp_0 tmp_9\nSplit                    splitncnn_19             1 2 tmp_9 tmp_9_splitncnn_0 tmp_9_splitncnn_1\nInterp                   Resize_2                 1 1 tmp_9_splitncnn_1 nearest_interp_v2_2.tmp_0 0=1 1=2.000000e+00 2=2.000000e+00\nBinaryOp                 Add_44                   2 1 tmp_7 nearest_interp_v2_2.tmp_0 tmp_10\nConvolution              Conv_59                  1 1 tmp_1_splitncnn_0 conv2d_255.tmp_0 0=24 1=3 4=1 6=20736\nSplit                    splitncnn_20             1 3 conv2d_255.tmp_0 conv2d_255.tmp_0_splitncnn_0 conv2d_255.tmp_0_splitncnn_1 conv2d_255.tmp_0_splitncnn_2\nPooling                  GlobalAveragePool_4      1 1 conv2d_255.tmp_0_splitncnn_2 pool2d_4.tmp_0 0=1 4=1\nInnerProduct             Conv_60                  1 1 pool2d_4.tmp_0 relu_16.tmp_0 0=6 1=1 2=144 9=1\nInnerProduct             Conv_61                  1 1 relu_16.tmp_0 conv2d_257.tmp_0 0=24 1=1 2=144\nHardSigmoid              HardSigmoid_4            1 1 conv2d_257.tmp_0 hardsigmoid_4.tmp_0\nBinaryOp                 Mul_24                   2 1 conv2d_255.tmp_0_splitncnn_1 hardsigmoid_4.tmp_0 tmp_11 0=2\nBinaryOp                 Add_47                   2 1 conv2d_255.tmp_0_splitncnn_0 tmp_11 tmp_12\nConvolution              Conv_62                  1 1 tmp_8_splitncnn_0 conv2d_258.tmp_0 0=24 1=3 4=1 6=20736\nSplit                    splitncnn_21             1 3 conv2d_258.tmp_0 conv2d_258.tmp_0_splitncnn_0 conv2d_258.tmp_0_splitncnn_1 conv2d_258.tmp_0_splitncnn_2\nPooling                  GlobalAveragePool_5      1 1 conv2d_258.tmp_0_splitncnn_2 pool2d_5.tmp_0 0=1 4=1\nInnerProduct             Conv_63                  1 1 pool2d_5.tmp_0 relu_17.tmp_0 0=6 1=1 2=144 9=1\nInnerProduct             Conv_64                  1 1 relu_17.tmp_0 conv2d_260.tmp_0 0=24 1=1 2=144\nHardSigmoid              HardSigmoid_5            1 1 conv2d_260.tmp_0 hardsigmoid_5.tmp_0\nBinaryOp                 Mul_25                   2 1 conv2d_258.tmp_0_splitncnn_1 hardsigmoid_5.tmp_0 tmp_13 0=2\nBinaryOp                 Add_50                   2 1 conv2d_258.tmp_0_splitncnn_0 tmp_13 tmp_14\nConvolution              Conv_65                  1 1 tmp_9_splitncnn_0 conv2d_261.tmp_0 0=24 1=3 4=1 6=20736\nSplit                    splitncnn_22             1 3 conv2d_261.tmp_0 conv2d_261.tmp_0_splitncnn_0 conv2d_261.tmp_0_splitncnn_1 conv2d_261.tmp_0_splitncnn_2\nPooling                  GlobalAveragePool_6      1 1 conv2d_261.tmp_0_splitncnn_2 pool2d_6.tmp_0 0=1 4=1\nInnerProduct             Conv_66                  1 1 pool2d_6.tmp_0 relu_18.tmp_0 0=6 1=1 2=144 9=1\nInnerProduct             Conv_67                  1 1 relu_18.tmp_0 conv2d_263.tmp_0 0=24 1=1 2=144\nHardSigmoid              HardSigmoid_6            1 1 conv2d_263.tmp_0 hardsigmoid_6.tmp_0\nBinaryOp                 Mul_26                   2 1 conv2d_261.tmp_0_splitncnn_1 hardsigmoid_6.tmp_0 tmp_15 0=2\nBinaryOp                 Add_53                   2 1 conv2d_261.tmp_0_splitncnn_0 tmp_15 tmp_16\nConvolution              Conv_68                  1 1 tmp_10 conv2d_264.tmp_0 0=24 1=3 4=1 6=20736\nSplit                    splitncnn_23             1 3 conv2d_264.tmp_0 conv2d_264.tmp_0_splitncnn_0 conv2d_264.tmp_0_splitncnn_1 conv2d_264.tmp_0_splitncnn_2\nPooling                  GlobalAveragePool_7      1 1 conv2d_264.tmp_0_splitncnn_2 pool2d_7.tmp_0 0=1 4=1\nInnerProduct             Conv_69                  1 1 pool2d_7.tmp_0 relu_19.tmp_0 0=6 1=1 2=144 9=1\nInnerProduct             Conv_70                  1 1 relu_19.tmp_0 conv2d_266.tmp_0 0=24 1=1 2=144\nHardSigmoid              HardSigmoid_7            1 1 conv2d_266.tmp_0 hardsigmoid_7.tmp_0\nBinaryOp                 Mul_27                   2 1 conv2d_264.tmp_0_splitncnn_1 hardsigmoid_7.tmp_0 tmp_17 0=2\nBinaryOp                 Add_56                   2 1 conv2d_264.tmp_0_splitncnn_0 tmp_17 tmp_18\nInterp                   Resize_3                 1 1 tmp_12 nearest_interp_v2_3.tmp_0 0=1 1=8.000000e+00 2=8.000000e+00\nInterp                   Resize_4                 1 1 tmp_14 nearest_interp_v2_4.tmp_0 0=1 1=4.000000e+00 2=4.000000e+00\nInterp                   Resize_5                 1 1 tmp_16 nearest_interp_v2_5.tmp_0 0=1 1=2.000000e+00 2=2.000000e+00\nConcat                   Concat_0                 4 1 nearest_interp_v2_3.tmp_0 nearest_interp_v2_4.tmp_0 nearest_interp_v2_5.tmp_0 tmp_18 concat_0.tmp_0\nConvolution              Conv_71                  1 1 concat_0.tmp_0 batch_norm_47.tmp_4 0=24 1=3 4=1 5=1 6=20736 9=1\nDeconvolution            ConvTranspose_0          1 1 batch_norm_47.tmp_4 elementwise_add_10.tmp_0 0=24 1=2 3=2 5=1 6=2304\nBatchNorm                BatchNormalization_48    1 1 elementwise_add_10.tmp_0 batch_norm_48.tmp_3 0=24\nReLU                     Relu_21                  1 1 batch_norm_48.tmp_3 batch_norm_48.tmp_4\nDeconvolution            ConvTranspose_1          1 1 batch_norm_48.tmp_4 output 0=1 1=2 3=2 5=1 6=96 9=4\n"
  },
  {
    "path": "app/src/main/assets/ch_PP-OCRv3_rec.param",
    "content": "7767517\n116 127\nInput                    input                    0 1 input\nConvolution              Conv_0                   1 1 input batch_norm_27.tmp_4 0=16 1=3 3=2 4=1 5=1 6=432 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_1                   1 1 batch_norm_27.tmp_4 batch_norm_28.tmp_4 0=16 1=3 4=1 5=1 6=144 7=16 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_2                   1 1 batch_norm_28.tmp_4 batch_norm_29.tmp_4 0=32 1=1 5=1 6=512 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_3                   1 1 batch_norm_29.tmp_4 batch_norm_30.tmp_4 0=32 1=3 4=1 5=1 6=288 7=32 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_4                   1 1 batch_norm_30.tmp_4 batch_norm_31.tmp_4 0=64 1=1 5=1 6=2048 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_5                   1 1 batch_norm_31.tmp_4 batch_norm_32.tmp_4 0=64 1=3 4=1 5=1 6=576 7=64 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_6                   1 1 batch_norm_32.tmp_4 batch_norm_33.tmp_4 0=64 1=1 5=1 6=4096 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_7                   1 1 batch_norm_33.tmp_4 batch_norm_34.tmp_4 0=64 1=3 13=2 4=1 5=1 6=576 7=64 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_8                   1 1 batch_norm_34.tmp_4 batch_norm_35.tmp_4 0=128 1=1 5=1 6=8192 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_9                   1 1 batch_norm_35.tmp_4 batch_norm_36.tmp_4 0=128 1=3 4=1 5=1 6=1152 7=128 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_10                  1 1 batch_norm_36.tmp_4 batch_norm_37.tmp_4 0=128 1=1 5=1 6=16384 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_11                  1 1 batch_norm_37.tmp_4 batch_norm_38.tmp_4 0=128 1=3 13=2 4=1 5=1 6=1152 7=128 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_12                  1 1 batch_norm_38.tmp_4 batch_norm_39.tmp_4 0=256 1=1 5=1 6=32768 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_13                  1 1 batch_norm_39.tmp_4 batch_norm_40.tmp_4 0=256 1=5 4=2 5=1 6=6400 7=256 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_14                  1 1 batch_norm_40.tmp_4 batch_norm_41.tmp_4 0=256 1=1 5=1 6=65536 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_15                  1 1 batch_norm_41.tmp_4 batch_norm_42.tmp_4 0=256 1=5 4=2 5=1 6=6400 7=256 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_16                  1 1 batch_norm_42.tmp_4 batch_norm_43.tmp_4 0=256 1=1 5=1 6=65536 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_17                  1 1 batch_norm_43.tmp_4 batch_norm_44.tmp_4 0=256 1=5 4=2 5=1 6=6400 7=256 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_18                  1 1 batch_norm_44.tmp_4 batch_norm_45.tmp_4 0=256 1=1 5=1 6=65536 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_19                  1 1 batch_norm_45.tmp_4 batch_norm_46.tmp_4 0=256 1=5 4=2 5=1 6=6400 7=256 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_20                  1 1 batch_norm_46.tmp_4 batch_norm_47.tmp_4 0=256 1=1 5=1 6=65536 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_21                  1 1 batch_norm_47.tmp_4 batch_norm_48.tmp_4 0=256 1=5 4=2 5=1 6=6400 7=256 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolution              Conv_22                  1 1 batch_norm_48.tmp_4 batch_norm_49.tmp_4 0=256 1=1 5=1 6=65536 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_23                  1 1 batch_norm_49.tmp_4 batch_norm_50.tmp_4 0=256 1=5 13=2 4=2 5=1 6=6400 7=256 9=6 -23310=2,1.666667e-01,5.000000e-01\nSplit                    splitncnn_0              1 2 batch_norm_50.tmp_4 batch_norm_50.tmp_4_splitncnn_0 batch_norm_50.tmp_4_splitncnn_1\nPooling                  GlobalAveragePool_0      1 1 batch_norm_50.tmp_4_splitncnn_1 pool2d_3.tmp_0 0=1 4=1\nInnerProduct             Conv_24                  1 1 pool2d_3.tmp_0 relu_2.tmp_0 0=64 1=1 2=16384 9=1\nInnerProduct             Conv_25                  1 1 relu_2.tmp_0 conv2d_110.tmp_0 0=256 1=1 2=16384\nHardSigmoid              HardSigmoid_0            1 1 conv2d_110.tmp_0 hardsigmoid_2.tmp_0 0=1.666667e-01\nBinaryOp                 Mul_24                   2 1 batch_norm_50.tmp_4_splitncnn_0 hardsigmoid_2.tmp_0 elementwise_mul_2 0=2\nConvolution              Conv_26                  1 1 elementwise_mul_2 batch_norm_51.tmp_4 0=512 1=1 5=1 6=131072 9=6 -23310=2,1.666667e-01,5.000000e-01\nConvolutionDepthWise     Conv_27                  1 1 batch_norm_51.tmp_4 batch_norm_52.tmp_4 0=512 1=5 3=2 13=1 4=2 5=1 6=12800 7=512 9=6 -23310=2,1.666667e-01,5.000000e-01\nSplit                    splitncnn_1              1 2 batch_norm_52.tmp_4 batch_norm_52.tmp_4_splitncnn_0 batch_norm_52.tmp_4_splitncnn_1\nPooling                  GlobalAveragePool_1      1 1 batch_norm_52.tmp_4_splitncnn_1 pool2d_4.tmp_0 0=1 4=1\nInnerProduct             Conv_28                  1 1 pool2d_4.tmp_0 relu_3.tmp_0 0=128 1=1 2=65536 9=1\nInnerProduct             Conv_29                  1 1 relu_3.tmp_0 conv2d_113.tmp_0 0=512 1=1 2=65536\nHardSigmoid              HardSigmoid_1            1 1 conv2d_113.tmp_0 hardsigmoid_3.tmp_0 0=1.666667e-01\nBinaryOp                 Mul_27                   2 1 batch_norm_52.tmp_4_splitncnn_0 hardsigmoid_3.tmp_0 elementwise_mul_3 0=2\nConvolution              Conv_30                  1 1 elementwise_mul_3 batch_norm_53.tmp_4 0=512 1=1 5=1 6=262144 9=6 -23310=2,1.666667e-01,5.000000e-01\nPooling                  AveragePool_0            1 1 batch_norm_53.tmp_4 pool2d_5.tmp_0 0=1 1=2 2=2 5=1\nSplit                    splitncnn_2              1 2 pool2d_5.tmp_0 pool2d_5.tmp_0_splitncnn_0 pool2d_5.tmp_0_splitncnn_1\nConvolution              Conv_31                  1 1 pool2d_5.tmp_0_splitncnn_1 conv2d_115.tmp_0 0=64 1=3 4=1 5=1 6=294912\nSwish                    Mul_29                   1 1 conv2d_115.tmp_0 swish_21.tmp_0\nConvolution              Conv_32                  1 1 swish_21.tmp_0 conv2d_116.tmp_0 0=120 1=1 5=1 6=7680\nSwish                    Mul_30                   1 1 conv2d_116.tmp_0 swish_22.tmp_0\nReshape                  Reshape_4                1 1 swish_22.tmp_0 flatten_1.tmp_0 0=-1 1=120\nPermute                  Transpose_0              1 1 flatten_1.tmp_0 transpose_9.tmp_0 0=1\nSplit                    splitncnn_3              1 2 transpose_9.tmp_0 transpose_9.tmp_0_splitncnn_0 transpose_9.tmp_0_splitncnn_1\nLayerNorm                Add_32                   1 1 transpose_9.tmp_0_splitncnn_1 layer_norm_15.tmp_2 0=120 1=1.000000e-05\nInnerProduct             MatMul_0                 1 1 layer_norm_15.tmp_2 linear_35.tmp_1 0=360 1=1 2=43200\nReshape                  Reshape_7                1 1 linear_35.tmp_1 reshape2_5.tmp_0 0=15 1=8 2=-1 11=3\nPermute                  Transpose_1              1 1 reshape2_5.tmp_0 transpose_10.tmp_0 0=8\nSplit                    splitncnn_4              1 3 transpose_10.tmp_0 transpose_10.tmp_0_splitncnn_0 transpose_10.tmp_0_splitncnn_1 transpose_10.tmp_0_splitncnn_2\nCrop                     Slice_2                  1 1 transpose_10.tmp_0_splitncnn_2 Slice_2 -23309=1,0 -23310=1,1 -23311=1,0\nReshape                  Squeeze_0                1 1 Slice_2 transpose_10.tmp_0_slice_0 0=15 1=-1 2=8\nBinaryOp                 Mul_32                   1 1 transpose_10.tmp_0_slice_0 Mul_32 0=2 1=1 2=2.581989e-01\nCrop                     Slice_3                  1 1 transpose_10.tmp_0_splitncnn_1 Slice_3 -23309=1,1 -23310=1,2 -23311=1,0\nReshape                  Squeeze_1                1 1 Slice_3 transpose_10.tmp_0_slice_1 0=15 1=-1 2=8\nCrop                     Slice_4                  1 1 transpose_10.tmp_0_splitncnn_0 Slice_4 -23309=1,2 -23310=1,3 -23311=1,0\nReshape                  Squeeze_2                1 1 Slice_4 transpose_10.tmp_0_slice_2 0=15 1=-1 2=8\nPermute                  Transpose_2              1 1 transpose_10.tmp_0_slice_1 transpose_11.tmp_0 0=1\nMatMul                   MatMul_1                 2 1 Mul_32 transpose_11.tmp_0 matmul_v2_4.tmp_0\nSoftmax                  Softmax_0                1 1 matmul_v2_4.tmp_0 softmax_3.tmp_0 0=-1 1=1\nMatMul                   MatMul_2                 2 1 softmax_3.tmp_0 transpose_10.tmp_0_slice_2 matmul_v2_5.tmp_0\nPermute                  Transpose_3              1 1 matmul_v2_5.tmp_0 transpose_12.tmp_0 0=2\nReshape                  Reshape_8                1 1 transpose_12.tmp_0 reshape2_6.tmp_0 0=120 1=-1\nInnerProduct             MatMul_3                 1 1 reshape2_6.tmp_0 linear_36.tmp_1 0=120 1=1 2=14400\nBinaryOp                 Add_36                   2 1 transpose_9.tmp_0_splitncnn_0 linear_36.tmp_1 tmp_7\nSplit                    splitncnn_5              1 2 tmp_7 tmp_7_splitncnn_0 tmp_7_splitncnn_1\nLayerNorm                Add_38                   1 1 tmp_7_splitncnn_1 layer_norm_16.tmp_2 0=120 1=1.000000e-05\nInnerProduct             MatMul_4                 1 1 layer_norm_16.tmp_2 linear_37.tmp_1 0=240 1=1 2=28800\nSwish                    Mul_34                   1 1 linear_37.tmp_1 swish_23.tmp_0\nInnerProduct             MatMul_5                 1 1 swish_23.tmp_0 linear_38.tmp_1 0=120 1=1 2=28800\nBinaryOp                 Add_41                   2 1 tmp_7_splitncnn_0 linear_38.tmp_1 tmp_8\nSplit                    splitncnn_6              1 2 tmp_8 tmp_8_splitncnn_0 tmp_8_splitncnn_1\nLayerNorm                Add_43                   1 1 tmp_8_splitncnn_1 layer_norm_17.tmp_2 0=120 1=1.000000e-05\nInnerProduct             MatMul_6                 1 1 layer_norm_17.tmp_2 linear_39.tmp_1 0=360 1=1 2=43200\nReshape                  Reshape_13               1 1 linear_39.tmp_1 reshape2_7.tmp_0 0=15 1=8 2=-1 11=3\nPermute                  Transpose_4              1 1 reshape2_7.tmp_0 transpose_13.tmp_0 0=8\nSplit                    splitncnn_7              1 3 transpose_13.tmp_0 transpose_13.tmp_0_splitncnn_0 transpose_13.tmp_0_splitncnn_1 transpose_13.tmp_0_splitncnn_2\nCrop                     Slice_7                  1 1 transpose_13.tmp_0_splitncnn_2 Slice_7 -23309=1,0 -23310=1,1 -23311=1,0\nReshape                  Squeeze_3                1 1 Slice_7 transpose_13.tmp_0_slice_0 0=15 1=-1 2=8\nBinaryOp                 Mul_36                   1 1 transpose_13.tmp_0_slice_0 Mul_36 0=2 1=1 2=2.581989e-01\nCrop                     Slice_8                  1 1 transpose_13.tmp_0_splitncnn_1 Slice_8 -23309=1,1 -23310=1,2 -23311=1,0\nReshape                  Squeeze_4                1 1 Slice_8 transpose_13.tmp_0_slice_1 0=15 1=-1 2=8\nCrop                     Slice_9                  1 1 transpose_13.tmp_0_splitncnn_0 Slice_9 -23309=1,2 -23310=1,3 -23311=1,0\nReshape                  Squeeze_5                1 1 Slice_9 transpose_13.tmp_0_slice_2 0=15 1=-1 2=8\nPermute                  Transpose_5              1 1 transpose_13.tmp_0_slice_1 transpose_14.tmp_0 0=1\nMatMul                   MatMul_7                 2 1 Mul_36 transpose_14.tmp_0 matmul_v2_6.tmp_0\nSoftmax                  Softmax_1                1 1 matmul_v2_6.tmp_0 softmax_4.tmp_0 0=2 1=1\nMatMul                   MatMul_8                 2 1 softmax_4.tmp_0 transpose_13.tmp_0_slice_2 matmul_v2_7.tmp_0\nPermute                  Transpose_6              1 1 matmul_v2_7.tmp_0 transpose_15.tmp_0 0=2\nReshape                  Reshape_14               1 1 transpose_15.tmp_0 reshape2_8.tmp_0 0=120 1=-1\nInnerProduct             MatMul_9                 1 1 reshape2_8.tmp_0 linear_40.tmp_1 0=120 1=1 2=14400\nBinaryOp                 Add_47                   2 1 tmp_8_splitncnn_0 linear_40.tmp_1 tmp_10\nSplit                    splitncnn_8              1 2 tmp_10 tmp_10_splitncnn_0 tmp_10_splitncnn_1\nLayerNorm                Add_49                   1 1 tmp_10_splitncnn_1 layer_norm_18.tmp_2 0=120 1=1.000000e-05\nInnerProduct             MatMul_10                1 1 layer_norm_18.tmp_2 linear_41.tmp_1 0=240 1=1 2=28800\nSwish                    Mul_38                   1 1 linear_41.tmp_1 swish_24.tmp_0\nInnerProduct             MatMul_11                1 1 swish_24.tmp_0 linear_42.tmp_1 0=120 1=1 2=28800\nBinaryOp                 Add_52                   2 1 tmp_10_splitncnn_0 linear_42.tmp_1 tmp_11\nLayerNorm                Add_54                   1 1 tmp_11 layer_norm_19.tmp_2 0=120 1=1.000000e-06\nReshape                  Reshape_19               1 1 layer_norm_19.tmp_2 reshape2_9.tmp_0 0=120 1=-1 2=1\nPermute                  Transpose_7              1 1 reshape2_9.tmp_0 transpose_16.tmp_0 0=4\nConvolution              Conv_33                  1 1 transpose_16.tmp_0 conv2d_117.tmp_0 0=512 1=1 5=1 6=61440\nSwish                    Mul_40                   1 1 conv2d_117.tmp_0 swish_25.tmp_0\nConcat                   Concat_1                 2 1 pool2d_5.tmp_0_splitncnn_0 swish_25.tmp_0 concat_1.tmp_0\nConvolution              Conv_34                  1 1 concat_1.tmp_0 conv2d_118.tmp_0 0=64 1=3 4=1 5=1 6=589824\nSwish                    Mul_41                   1 1 conv2d_118.tmp_0 swish_26.tmp_0\nConvolution              Conv_35                  1 1 swish_26.tmp_0 conv2d_119.tmp_0 0=64 1=1 5=1 6=4096\nSwish                    Mul_42                   1 1 conv2d_119.tmp_0 swish_27.tmp_0\nPermute                  Transpose_8              1 1 swish_27.tmp_0 squeeze_1.tmp_0 0=3\nSqueeze                  Squeeze_6                1 1 squeeze_1.tmp_0 transpose_17.tmp_0 -23303=1,0\nInnerProduct             MatMul_12                1 1 transpose_17.tmp_0 linear_43.tmp_1 0=6625 1=1 2=424000\nSoftmax                  Softmax_2                1 1 linear_43.tmp_1 out 0=-1 1=1\n"
  },
  {
    "path": "app/src/main/assets/cls-sim-op.param",
    "content": "7767517\n135 151\nInput                    input                    0 1 input\nConvolution              Conv_0                   1 1 input 250 0=8 1=3 3=2 4=1 5=1 6=216\nHardSwish                Div_9                    1 1 250 258 0=1.666667e-01\nConvolution              Conv_10                  1 1 258 261 0=8 1=1 5=1 6=64 9=1\nConvolutionDepthWise     Conv_13                  1 1 261 264 0=8 1=3 13=2 4=1 5=1 6=72 7=8 9=1\nSplit                    splitncnn_0              1 2 264 264_splitncnn_0 264_splitncnn_1\nPooling                  GlobalAveragePool_16     1 1 264_splitncnn_1 265 0=1 4=1\nInnerProduct             Conv_17                  1 1 265 267 0=2 1=1 2=16 9=1\nInnerProduct             Conv_19                  1 1 267 268 0=8 1=1 2=16\nBinaryOp                 Mul_21                   1 1 268 270 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_28                   1 1 270 277 0=1.666667e-01\nBinaryOp                 Mul_29                   2 1 264_splitncnn_0 277 278 0=2\nConvolution              Conv_30                  1 1 278 280 0=8 1=1 5=1 6=64\nConvolution              Conv_32                  1 1 280 283 0=24 1=1 5=1 6=192 9=1\nConvolutionDepthWise     Conv_35                  1 1 283 286 0=24 1=3 13=2 4=1 5=1 6=216 7=24 9=1\nConvolution              Conv_38                  1 1 286 288 0=8 1=1 5=1 6=192\nSplit                    splitncnn_1              1 2 288 288_splitncnn_0 288_splitncnn_1\nConvolution              Conv_40                  1 1 288_splitncnn_1 291 0=32 1=1 5=1 6=256 9=1\nConvolutionDepthWise     Conv_43                  1 1 291 294 0=32 1=3 4=1 5=1 6=288 7=32 9=1\nConvolution              Conv_46                  1 1 294 296 0=8 1=1 5=1 6=256\nBinaryOp                 Add_48                   2 1 288_splitncnn_0 296 297\nConvolution              Conv_49                  1 1 297 299 0=32 1=1 5=1 6=256\nHardSwish                Div_58                   1 1 299 307 0=1.666667e-01\nConvolutionDepthWise     Conv_59                  1 1 307 309 0=32 1=5 13=2 4=2 5=1 6=800 7=32\nHardSwish                Div_68                   1 1 309 317 0=1.666667e-01\nSplit                    splitncnn_2              1 2 317 317_splitncnn_0 317_splitncnn_1\nPooling                  GlobalAveragePool_69     1 1 317_splitncnn_1 318 0=1 4=1\nInnerProduct             Conv_70                  1 1 318 320 0=8 1=1 2=256 9=1\nInnerProduct             Conv_72                  1 1 320 321 0=32 1=1 2=256\nBinaryOp                 Mul_74                   1 1 321 323 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_81                   1 1 323 330 0=1.666667e-01\nBinaryOp                 Mul_82                   2 1 317_splitncnn_0 330 331 0=2\nConvolution              Conv_83                  1 1 331 333 0=16 1=1 5=1 6=512\nSplit                    splitncnn_3              1 2 333 333_splitncnn_0 333_splitncnn_1\nConvolution              Conv_85                  1 1 333_splitncnn_1 335 0=88 1=1 5=1 6=1408\nHardSwish                Div_94                   1 1 335 343 0=1.666667e-01\nConvolutionDepthWise     Conv_95                  1 1 343 345 0=88 1=5 4=2 5=1 6=2200 7=88\nHardSwish                Div_104                  1 1 345 353 0=1.666667e-01\nSplit                    splitncnn_4              1 2 353 353_splitncnn_0 353_splitncnn_1\nPooling                  GlobalAveragePool_105    1 1 353_splitncnn_1 354 0=1 4=1\nInnerProduct             Conv_106                 1 1 354 356 0=22 1=1 2=1936 9=1\nInnerProduct             Conv_108                 1 1 356 357 0=88 1=1 2=1936\nBinaryOp                 Mul_110                  1 1 357 359 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_117                  1 1 359 366 0=1.666667e-01\nBinaryOp                 Mul_118                  2 1 353_splitncnn_0 366 367 0=2\nConvolution              Conv_119                 1 1 367 369 0=16 1=1 5=1 6=1408\nBinaryOp                 Add_121                  2 1 333_splitncnn_0 369 370\nSplit                    splitncnn_5              1 2 370 370_splitncnn_0 370_splitncnn_1\nConvolution              Conv_122                 1 1 370_splitncnn_1 372 0=88 1=1 5=1 6=1408\nHardSwish                Div_131                  1 1 372 380 0=1.666667e-01\nConvolutionDepthWise     Conv_132                 1 1 380 382 0=88 1=5 4=2 5=1 6=2200 7=88\nHardSwish                Div_141                  1 1 382 390 0=1.666667e-01\nSplit                    splitncnn_6              1 2 390 390_splitncnn_0 390_splitncnn_1\nPooling                  GlobalAveragePool_142    1 1 390_splitncnn_1 391 0=1 4=1\nInnerProduct             Conv_143                 1 1 391 393 0=22 1=1 2=1936 9=1\nInnerProduct             Conv_145                 1 1 393 394 0=88 1=1 2=1936\nBinaryOp                 Mul_147                  1 1 394 396 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_154                  1 1 396 403 0=1.666667e-01\nBinaryOp                 Mul_155                  2 1 390_splitncnn_0 403 404 0=2\nConvolution              Conv_156                 1 1 404 406 0=16 1=1 5=1 6=1408\nBinaryOp                 Add_158                  2 1 370_splitncnn_0 406 407\nSplit                    splitncnn_7              1 2 407 407_splitncnn_0 407_splitncnn_1\nConvolution              Conv_159                 1 1 407_splitncnn_1 409 0=40 1=1 5=1 6=640\nHardSwish                Div_168                  1 1 409 417 0=1.666667e-01\nConvolutionDepthWise     Conv_169                 1 1 417 419 0=40 1=5 4=2 5=1 6=1000 7=40\nHardSwish                Div_178                  1 1 419 427 0=1.666667e-01\nSplit                    splitncnn_8              1 2 427 427_splitncnn_0 427_splitncnn_1\nPooling                  GlobalAveragePool_179    1 1 427_splitncnn_1 428 0=1 4=1\nInnerProduct             Conv_180                 1 1 428 430 0=10 1=1 2=400 9=1\nInnerProduct             Conv_182                 1 1 430 431 0=40 1=1 2=400\nBinaryOp                 Mul_184                  1 1 431 433 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_191                  1 1 433 440 0=1.666667e-01\nBinaryOp                 Mul_192                  2 1 427_splitncnn_0 440 441 0=2\nConvolution              Conv_193                 1 1 441 443 0=16 1=1 5=1 6=640\nBinaryOp                 Add_195                  2 1 407_splitncnn_0 443 444\nSplit                    splitncnn_9              1 2 444 444_splitncnn_0 444_splitncnn_1\nConvolution              Conv_196                 1 1 444_splitncnn_1 446 0=48 1=1 5=1 6=768\nHardSwish                Div_205                  1 1 446 454 0=1.666667e-01\nConvolutionDepthWise     Conv_206                 1 1 454 456 0=48 1=5 4=2 5=1 6=1200 7=48\nHardSwish                Div_215                  1 1 456 464 0=1.666667e-01\nSplit                    splitncnn_10             1 2 464 464_splitncnn_0 464_splitncnn_1\nPooling                  GlobalAveragePool_216    1 1 464_splitncnn_1 465 0=1 4=1\nInnerProduct             Conv_217                 1 1 465 467 0=12 1=1 2=576 9=1\nInnerProduct             Conv_219                 1 1 467 468 0=48 1=1 2=576\nBinaryOp                 Mul_221                  1 1 468 470 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_228                  1 1 470 477 0=1.666667e-01\nBinaryOp                 Mul_229                  2 1 464_splitncnn_0 477 478 0=2\nConvolution              Conv_230                 1 1 478 480 0=16 1=1 5=1 6=768\nBinaryOp                 Add_232                  2 1 444_splitncnn_0 480 481\nConvolution              Conv_233                 1 1 481 483 0=104 1=1 5=1 6=1664\nHardSwish                Div_242                  1 1 483 491 0=1.666667e-01\nConvolutionDepthWise     Conv_243                 1 1 491 493 0=104 1=5 13=2 4=2 5=1 6=2600 7=104\nHardSwish                Div_252                  1 1 493 501 0=1.666667e-01\nSplit                    splitncnn_11             1 2 501 501_splitncnn_0 501_splitncnn_1\nPooling                  GlobalAveragePool_253    1 1 501_splitncnn_1 502 0=1 4=1\nInnerProduct             Conv_254                 1 1 502 504 0=26 1=1 2=2704 9=1\nInnerProduct             Conv_256                 1 1 504 505 0=104 1=1 2=2704\nBinaryOp                 Mul_258                  1 1 505 507 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_265                  1 1 507 514 0=1.666667e-01\nBinaryOp                 Mul_266                  2 1 501_splitncnn_0 514 515 0=2\nConvolution              Conv_267                 1 1 515 517 0=32 1=1 5=1 6=3328\nSplit                    splitncnn_12             1 2 517 517_splitncnn_0 517_splitncnn_1\nConvolution              Conv_269                 1 1 517_splitncnn_1 519 0=200 1=1 5=1 6=6400\nHardSwish                Div_278                  1 1 519 527 0=1.666667e-01\nConvolutionDepthWise     Conv_279                 1 1 527 529 0=200 1=5 4=2 5=1 6=5000 7=200\nHardSwish                Div_288                  1 1 529 537 0=1.666667e-01\nSplit                    splitncnn_13             1 2 537 537_splitncnn_0 537_splitncnn_1\nPooling                  GlobalAveragePool_289    1 1 537_splitncnn_1 538 0=1 4=1\nInnerProduct             Conv_290                 1 1 538 540 0=50 1=1 2=10000 9=1\nInnerProduct             Conv_292                 1 1 540 541 0=200 1=1 2=10000\nBinaryOp                 Mul_294                  1 1 541 543 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_301                  1 1 543 550 0=1.666667e-01\nBinaryOp                 Mul_302                  2 1 537_splitncnn_0 550 551 0=2\nConvolution              Conv_303                 1 1 551 553 0=32 1=1 5=1 6=6400\nBinaryOp                 Add_305                  2 1 517_splitncnn_0 553 554\nSplit                    splitncnn_14             1 2 554 554_splitncnn_0 554_splitncnn_1\nConvolution              Conv_306                 1 1 554_splitncnn_1 556 0=200 1=1 5=1 6=6400\nHardSwish                Div_315                  1 1 556 564 0=1.666667e-01\nConvolutionDepthWise     Conv_316                 1 1 564 566 0=200 1=5 4=2 5=1 6=5000 7=200\nHardSwish                Div_325                  1 1 566 574 0=1.666667e-01\nSplit                    splitncnn_15             1 2 574 574_splitncnn_0 574_splitncnn_1\nPooling                  GlobalAveragePool_326    1 1 574_splitncnn_1 575 0=1 4=1\nInnerProduct             Conv_327                 1 1 575 577 0=50 1=1 2=10000 9=1\nInnerProduct             Conv_329                 1 1 577 578 0=200 1=1 2=10000\nBinaryOp                 Mul_331                  1 1 578 580 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_338                  1 1 580 587 0=1.666667e-01\nBinaryOp                 Mul_339                  2 1 574_splitncnn_0 587 588 0=2\nConvolution              Conv_340                 1 1 588 590 0=32 1=1 5=1 6=6400\nBinaryOp                 Add_342                  2 1 554_splitncnn_0 590 591\nConvolution              Conv_343                 1 1 591 593 0=200 1=1 5=1 6=6400\nHardSwish                Div_352                  1 1 593 601 0=1.666667e-01\nPooling                  MaxPool_353              1 1 601 602 1=2 2=2 5=1\nPooling                  GlobalAveragePool_354    1 1 602 613 0=1 4=1\nInnerProduct             Gemm_365                 1 1 613 614 0=2 1=1 2=400\nSoftmax                  Softmax_366              1 1 614 out\n"
  },
  {
    "path": "app/src/main/assets/det-sim-op.param",
    "content": "7767517\n115 131\nInput                    input                    0 1 input0\nConvolution              Conv_0                   1 1 input0 322 0=8 1=3 3=2 4=1 5=1 6=216\nHardSwish                Div_9                    1 1 322 330 0=1.666667e-01\nSplit                    splitncnn_0              1 2 330 330_splitncnn_0 330_splitncnn_1\nConvolution              Conv_10                  1 1 330_splitncnn_1 333 0=8 1=1 5=1 6=64 9=1\nConvolutionDepthWise     Conv_13                  1 1 333 336 0=8 1=3 4=1 5=1 6=72 7=8 9=1\nConvolution              Conv_16                  1 1 336 338 0=8 1=1 5=1 6=64\nBinaryOp                 Add_18                   2 1 330_splitncnn_0 338 339\nConvolution              Conv_19                  1 1 339 342 0=32 1=1 5=1 6=256 9=1\nConvolutionDepthWise     Conv_22                  1 1 342 345 0=32 1=3 3=2 4=1 5=1 6=288 7=32 9=1\nConvolution              Conv_25                  1 1 345 347 0=16 1=1 5=1 6=512\nSplit                    splitncnn_1              1 2 347 347_splitncnn_0 347_splitncnn_1\nConvolution              Conv_27                  1 1 347_splitncnn_1 350 0=40 1=1 5=1 6=640 9=1\nConvolutionDepthWise     Conv_30                  1 1 350 353 0=40 1=3 4=1 5=1 6=360 7=40 9=1\nConvolution              Conv_33                  1 1 353 355 0=16 1=1 5=1 6=640\nBinaryOp                 Add_35                   2 1 347_splitncnn_0 355 356\nSplit                    splitncnn_2              1 2 356 356_splitncnn_0 356_splitncnn_1\nConvolution              Conv_36                  1 1 356_splitncnn_1 359 0=40 1=1 5=1 6=640 9=1\nConvolutionDepthWise     Conv_39                  1 1 359 362 0=40 1=5 3=2 4=2 5=1 6=1000 7=40 9=1\nConvolution              Conv_42                  1 1 362 364 0=24 1=1 5=1 6=960\nSplit                    splitncnn_3              1 2 364 364_splitncnn_0 364_splitncnn_1\nConvolution              Conv_44                  1 1 364_splitncnn_1 367 0=64 1=1 5=1 6=1536 9=1\nConvolutionDepthWise     Conv_47                  1 1 367 370 0=64 1=5 4=2 5=1 6=1600 7=64 9=1\nConvolution              Conv_50                  1 1 370 372 0=24 1=1 5=1 6=1536\nBinaryOp                 Add_52                   2 1 364_splitncnn_0 372 373\nSplit                    splitncnn_4              1 2 373 373_splitncnn_0 373_splitncnn_1\nConvolution              Conv_53                  1 1 373_splitncnn_1 376 0=64 1=1 5=1 6=1536 9=1\nConvolutionDepthWise     Conv_56                  1 1 376 379 0=64 1=5 4=2 5=1 6=1600 7=64 9=1\nConvolution              Conv_59                  1 1 379 381 0=24 1=1 5=1 6=1536\nBinaryOp                 Add_61                   2 1 373_splitncnn_0 381 382\nSplit                    splitncnn_5              1 2 382 382_splitncnn_0 382_splitncnn_1\nConvolution              Conv_62                  1 1 382_splitncnn_1 384 0=120 1=1 5=1 6=2880\nHardSwish                Div_71                   1 1 384 392 0=1.666667e-01\nConvolutionDepthWise     Conv_72                  1 1 392 394 0=120 1=3 3=2 4=1 5=1 6=1080 7=120\nHardSwish                Div_81                   1 1 394 402 0=1.666667e-01\nConvolution              Conv_82                  1 1 402 404 0=40 1=1 5=1 6=4800\nSplit                    splitncnn_6              1 2 404 404_splitncnn_0 404_splitncnn_1\nConvolution              Conv_84                  1 1 404_splitncnn_1 406 0=104 1=1 5=1 6=4160\nHardSwish                Div_93                   1 1 406 414 0=1.666667e-01\nConvolutionDepthWise     Conv_94                  1 1 414 416 0=104 1=3 4=1 5=1 6=936 7=104\nHardSwish                Div_103                  1 1 416 424 0=1.666667e-01\nConvolution              Conv_104                 1 1 424 426 0=40 1=1 5=1 6=4160\nBinaryOp                 Add_106                  2 1 404_splitncnn_0 426 427\nSplit                    splitncnn_7              1 2 427 427_splitncnn_0 427_splitncnn_1\nConvolution              Conv_107                 1 1 427_splitncnn_1 429 0=96 1=1 5=1 6=3840\nHardSwish                Div_116                  1 1 429 437 0=1.666667e-01\nConvolutionDepthWise     Conv_117                 1 1 437 439 0=96 1=3 4=1 5=1 6=864 7=96\nHardSwish                Div_126                  1 1 439 447 0=1.666667e-01\nConvolution              Conv_127                 1 1 447 449 0=40 1=1 5=1 6=3840\nBinaryOp                 Add_129                  2 1 427_splitncnn_0 449 450\nSplit                    splitncnn_8              1 2 450 450_splitncnn_0 450_splitncnn_1\nConvolution              Conv_130                 1 1 450_splitncnn_1 452 0=96 1=1 5=1 6=3840\nHardSwish                Div_139                  1 1 452 460 0=1.666667e-01\nConvolutionDepthWise     Conv_140                 1 1 460 462 0=96 1=3 4=1 5=1 6=864 7=96\nHardSwish                Div_149                  1 1 462 470 0=1.666667e-01\nConvolution              Conv_150                 1 1 470 472 0=40 1=1 5=1 6=3840\nBinaryOp                 Add_152                  2 1 450_splitncnn_0 472 473\nConvolution              Conv_153                 1 1 473 475 0=240 1=1 5=1 6=9600\nHardSwish                Div_162                  1 1 475 483 0=1.666667e-01\nConvolutionDepthWise     Conv_163                 1 1 483 485 0=240 1=3 4=1 5=1 6=2160 7=240\nHardSwish                Div_172                  1 1 485 493 0=1.666667e-01\nConvolution              Conv_173                 1 1 493 495 0=56 1=1 5=1 6=13440\nSplit                    splitncnn_9              1 2 495 495_splitncnn_0 495_splitncnn_1\nConvolution              Conv_175                 1 1 495_splitncnn_1 497 0=336 1=1 5=1 6=18816\nHardSwish                Div_184                  1 1 497 505 0=1.666667e-01\nConvolutionDepthWise     Conv_185                 1 1 505 507 0=336 1=3 4=1 5=1 6=3024 7=336\nHardSwish                Div_194                  1 1 507 515 0=1.666667e-01\nConvolution              Conv_195                 1 1 515 517 0=56 1=1 5=1 6=18816\nBinaryOp                 Add_197                  2 1 495_splitncnn_0 517 518\nSplit                    splitncnn_10             1 2 518 518_splitncnn_0 518_splitncnn_1\nConvolution              Conv_198                 1 1 518_splitncnn_1 520 0=336 1=1 5=1 6=18816\nHardSwish                Div_207                  1 1 520 528 0=1.666667e-01\nConvolutionDepthWise     Conv_208                 1 1 528 530 0=336 1=5 3=2 4=2 5=1 6=8400 7=336\nHardSwish                Div_217                  1 1 530 538 0=1.666667e-01\nConvolution              Conv_218                 1 1 538 540 0=80 1=1 5=1 6=26880\nSplit                    splitncnn_11             1 2 540 540_splitncnn_0 540_splitncnn_1\nConvolution              Conv_220                 1 1 540_splitncnn_1 542 0=480 1=1 5=1 6=38400\nHardSwish                Div_229                  1 1 542 550 0=1.666667e-01\nConvolutionDepthWise     Conv_230                 1 1 550 552 0=480 1=5 4=2 5=1 6=12000 7=480\nHardSwish                Div_239                  1 1 552 560 0=1.666667e-01\nConvolution              Conv_240                 1 1 560 562 0=80 1=1 5=1 6=38400\nBinaryOp                 Add_242                  2 1 540_splitncnn_0 562 563\nSplit                    splitncnn_12             1 2 563 563_splitncnn_0 563_splitncnn_1\nConvolution              Conv_243                 1 1 563_splitncnn_1 565 0=480 1=1 5=1 6=38400\nHardSwish                Div_252                  1 1 565 573 0=1.666667e-01\nConvolutionDepthWise     Conv_253                 1 1 573 575 0=480 1=5 4=2 5=1 6=12000 7=480\nHardSwish                Div_262                  1 1 575 583 0=1.666667e-01\nConvolution              Conv_263                 1 1 583 585 0=80 1=1 5=1 6=38400\nBinaryOp                 Add_265                  2 1 563_splitncnn_0 585 586\nConvolution              Conv_266                 1 1 586 588 0=480 1=1 5=1 6=38400\nHardSwish                Div_275                  1 1 588 596 0=1.666667e-01\nConvolution              Conv_276                 1 1 596 597 0=96 1=1 6=46080\nSplit                    splitncnn_13             1 2 597 597_splitncnn_0 597_splitncnn_1\nConvolution              Conv_277                 1 1 518_splitncnn_0 598 0=96 1=1 6=5376\nConvolution              Conv_278                 1 1 382_splitncnn_0 599 0=96 1=1 6=2304\nConvolution              Conv_279                 1 1 356_splitncnn_0 600 0=96 1=1 6=1536\nInterp                   Resize_281               1 1 597_splitncnn_1 610 0=1 1=2.000000e+00 2=2.000000e+00\nBinaryOp                 Add_282                  2 1 598 610 611\nSplit                    splitncnn_14             1 2 611 611_splitncnn_0 611_splitncnn_1\nInterp                   Resize_284               1 1 611_splitncnn_1 621 0=1 1=2.000000e+00 2=2.000000e+00\nBinaryOp                 Add_285                  2 1 599 621 622\nSplit                    splitncnn_15             1 2 622 622_splitncnn_0 622_splitncnn_1\nInterp                   Resize_287               1 1 622_splitncnn_1 632 0=1 1=2.000000e+00 2=2.000000e+00\nBinaryOp                 Add_288                  2 1 600 632 633\nConvolution              Conv_289                 1 1 597_splitncnn_0 634 0=24 1=3 4=1 6=20736\nConvolution              Conv_290                 1 1 611_splitncnn_0 635 0=24 1=3 4=1 6=20736\nConvolution              Conv_291                 1 1 622_splitncnn_0 636 0=24 1=3 4=1 6=20736\nConvolution              Conv_292                 1 1 633 637 0=24 1=3 4=1 6=20736\nInterp                   Resize_294               1 1 634 647 0=1 1=8.000000e+00 2=8.000000e+00\nInterp                   Resize_296               1 1 635 657 0=1 1=4.000000e+00 2=4.000000e+00\nInterp                   Resize_298               1 1 636 667 0=1 1=2.000000e+00 2=2.000000e+00\nConcat                   Concat_299               4 1 647 657 667 637 668\nConvolution              Conv_300                 1 1 668 671 0=24 1=3 4=1 5=1 6=20736 9=1\nDeconvolution            ConvTranspose_303        1 1 671 674 0=24 1=2 3=2 5=1 6=2304 9=1\nDeconvolution            ConvTranspose_306        1 1 674 out1 0=1 1=2 3=2 5=1 6=96 9=4\n"
  },
  {
    "path": "app/src/main/assets/paddleocr_keys.txt",
    "content": "'\n疗\n绚\n诚\n娇\n溜\n题\n贿\n者\n廖\n更\n纳\n加\n奉\n公\n一\n就\n汴\n计\n与\n路\n房\n原\n妇\n2\n0\n8\n-\n7\n其\n>\n:\n]\n,\n，\n骑\n刈\n全\n消\n昏\n傈\n安\n久\n钟\n嗅\n不\n影\n处\n驽\n蜿\n资\n关\n椤\n地\n瘸\n专\n问\n忖\n票\n嫉\n炎\n韵\n要\n月\n田\n节\n陂\n鄙\n捌\n备\n拳\n伺\n眼\n网\n盎\n大\n傍\n心\n东\n愉\n汇\n蹿\n科\n每\n业\n里\n航\n晏\n字\n平\n录\n先\n1\n3\n彤\n鲶\n产\n稍\n督\n腴\n有\n象\n岳\n注\n绍\n在\n泺\n文\n定\n核\n名\n水\n过\n理\n让\n偷\n率\n等\n这\n发\n”\n为\n含\n肥\n酉\n相\n鄱\n七\n编\n猥\n锛\n日\n镀\n蒂\n掰\n倒\n辆\n栾\n栗\n综\n涩\n州\n雌\n滑\n馀\n了\n机\n块\n司\n宰\n甙\n兴\n矽\n抚\n保\n用\n沧\n秩\n如\n收\n息\n滥\n页\n疑\n埠\n!\n！\n姥\n异\n橹\n钇\n向\n下\n跄\n的\n椴\n沫\n国\n绥\n獠\n报\n开\n民\n蜇\n何\n分\n凇\n长\n讥\n藏\n掏\n施\n羽\n中\n讲\n派\n嘟\n人\n提\n浼\n间\n世\n而\n古\n多\n倪\n唇\n饯\n控\n庚\n首\n赛\n蜓\n味\n断\n制\n觉\n技\n替\n艰\n溢\n潮\n夕\n钺\n外\n摘\n枋\n动\n双\n单\n啮\n户\n枇\n确\n锦\n曜\n杜\n或\n能\n效\n霜\n盒\n然\n侗\n电\n晁\n放\n步\n鹃\n新\n杖\n蜂\n吒\n濂\n瞬\n评\n总\n隍\n对\n独\n合\n也\n是\n府\n青\n天\n诲\n墙\n组\n滴\n级\n邀\n帘\n示\n已\n时\n骸\n仄\n泅\n和\n遨\n店\n雇\n疫\n持\n巍\n踮\n境\n只\n亨\n目\n鉴\n崤\n闲\n体\n泄\n杂\n作\n般\n轰\n化\n解\n迂\n诿\n蛭\n璀\n腾\n告\n版\n服\n省\n师\n小\n规\n程\n线\n海\n办\n引\n二\n桧\n牌\n砺\n洄\n裴\n修\n图\n痫\n胡\n许\n犊\n事\n郛\n基\n柴\n呼\n食\n研\n奶\n律\n蛋\n因\n葆\n察\n戏\n褒\n戒\n再\n李\n骁\n工\n貂\n油\n鹅\n章\n啄\n休\n场\n给\n睡\n纷\n豆\n器\n捎\n说\n敏\n学\n会\n浒\n设\n诊\n格\n廓\n查\n来\n霓\n室\n溆\n￠\n诡\n寥\n焕\n舜\n柒\n狐\n回\n戟\n砾\n厄\n实\n翩\n尿\n五\n入\n径\n惭\n喹\n股\n宇\n篝\n|\n;\n美\n期\n云\n九\n祺\n扮\n靠\n锝\n槌\n系\n企\n酰\n阊\n暂\n蚕\n忻\n豁\n本\n羹\n执\n条\n钦\nH\n獒\n限\n进\n季\n楦\n于\n芘\n玖\n铋\n茯\n未\n答\n粘\n括\n样\n精\n欠\n矢\n甥\n帷\n嵩\n扣\n令\n仔\n风\n皈\n行\n支\n部\n蓉\n刮\n站\n蜡\n救\n钊\n汗\n松\n嫌\n成\n可\n.\n鹤\n院\n从\n交\n政\n怕\n活\n调\n球\n局\n验\n髌\n第\n韫\n谗\n串\n到\n圆\n年\n米\n/\n*\n友\n忿\n检\n区\n看\n自\n敢\n刃\n个\n兹\n弄\n流\n留\n同\n没\n齿\n星\n聆\n轼\n湖\n什\n三\n建\n蛔\n儿\n椋\n汕\n震\n颧\n鲤\n跟\n力\n情\n璺\n铨\n陪\n务\n指\n族\n训\n滦\n鄣\n濮\n扒\n商\n箱\n十\n召\n慷\n辗\n所\n莞\n管\n护\n臭\n横\n硒\n嗓\n接\n侦\n六\n露\n党\n馋\n驾\n剖\n高\n侬\n妪\n幂\n猗\n绺\n骐\n央\n酐\n孝\n筝\n课\n徇\n缰\n门\n男\n西\n项\n句\n谙\n瞒\n秃\n篇\n教\n碲\n罚\n声\n呐\n景\n前\n富\n嘴\n鳌\n稀\n免\n朋\n啬\n睐\n去\n赈\n鱼\n住\n肩\n愕\n速\n旁\n波\n厅\n健\n茼\n厥\n鲟\n谅\n投\n攸\n炔\n数\n方\n击\n呋\n谈\n绩\n别\n愫\n僚\n躬\n鹧\n胪\n炳\n招\n喇\n膨\n泵\n蹦\n毛\n结\n5\n4\n谱\n识\n陕\n粽\n婚\n拟\n构\n且\n搜\n任\n潘\n比\n郢\n妨\n醪\n陀\n桔\n碘\n扎\n选\n哈\n骷\n楷\n亿\n明\n缆\n脯\n监\n睫\n逻\n婵\n共\n赴\n淝\n凡\n惦\n及\n达\n揖\n谩\n澹\n减\n焰\n蛹\n番\n祁\n柏\n员\n禄\n怡\n峤\n龙\n白\n叽\n生\n闯\n起\n细\n装\n谕\n竟\n聚\n钙\n上\n导\n渊\n按\n艾\n辘\n挡\n耒\n盹\n饪\n臀\n记\n邮\n蕙\n受\n各\n医\n搂\n普\n滇\n朗\n茸\n带\n翻\n酚\n(\n光\n堤\n墟\n蔷\n万\n幻\n〓\n瑙\n辈\n昧\n盏\n亘\n蛀\n吉\n铰\n请\n子\n假\n闻\n税\n井\n诩\n哨\n嫂\n好\n面\n琐\n校\n馊\n鬣\n缂\n营\n访\n炖\n占\n农\n缀\n否\n经\n钚\n棵\n趟\n张\n亟\n吏\n茶\n谨\n捻\n论\n迸\n堂\n玉\n信\n吧\n瞠\n乡\n姬\n寺\n咬\n溏\n苄\n皿\n意\n赉\n宝\n尔\n钰\n艺\n特\n唳\n踉\n都\n荣\n倚\n登\n荐\n丧\n奇\n涵\n批\n炭\n近\n符\n傩\n感\n道\n着\n菊\n虹\n仲\n众\n懈\n濯\n颞\n眺\n南\n释\n北\n缝\n标\n既\n茗\n整\n撼\n迤\n贲\n挎\n耱\n拒\n某\n妍\n卫\n哇\n英\n矶\n藩\n治\n他\n元\n领\n膜\n遮\n穗\n蛾\n飞\n荒\n棺\n劫\n么\n市\n火\n温\n拈\n棚\n洼\n转\n果\n奕\n卸\n迪\n伸\n泳\n斗\n邡\n侄\n涨\n屯\n萋\n胭\n氡\n崮\n枞\n惧\n冒\n彩\n斜\n手\n豚\n随\n旭\n淑\n妞\n形\n菌\n吲\n沱\n争\n驯\n歹\n挟\n兆\n柱\n传\n至\n包\n内\n响\n临\n红\n功\n弩\n衡\n寂\n禁\n老\n棍\n耆\n渍\n织\n害\n氵\n渑\n布\n载\n靥\n嗬\n虽\n苹\n咨\n娄\n库\n雉\n榜\n帜\n嘲\n套\n瑚\n亲\n簸\n欧\n边\n6\n腿\n旮\n抛\n吹\n瞳\n得\n镓\n梗\n厨\n继\n漾\n愣\n憨\n士\n策\n窑\n抑\n躯\n襟\n脏\n参\n贸\n言\n干\n绸\n鳄\n穷\n藜\n音\n折\n详\n)\n举\n悍\n甸\n癌\n黎\n谴\n死\n罩\n迁\n寒\n驷\n袖\n媒\n蒋\n掘\n模\n纠\n恣\n观\n祖\n蛆\n碍\n位\n稿\n主\n澧\n跌\n筏\n京\n锏\n帝\n贴\n证\n糠\n才\n黄\n鲸\n略\n炯\n饱\n四\n出\n园\n犀\n牧\n容\n汉\n杆\n浈\n汰\n瑷\n造\n虫\n瘩\n怪\n驴\n济\n应\n花\n沣\n谔\n夙\n旅\n价\n矿\n以\n考\ns\nu\n呦\n晒\n巡\n茅\n准\n肟\n瓴\n詹\n仟\n褂\n译\n桌\n混\n宁\n怦\n郑\n抿\n些\n余\n鄂\n饴\n攒\n珑\n群\n阖\n岔\n琨\n藓\n预\n环\n洮\n岌\n宀\n杲\n瀵\n最\n常\n囡\n周\n踊\n女\n鼓\n袭\n喉\n简\n范\n薯\n遐\n疏\n粱\n黜\n禧\n法\n箔\n斤\n遥\n汝\n奥\n直\n贞\n撑\n置\n绱\n集\n她\n馅\n逗\n钧\n橱\n魉\n[\n恙\n躁\n唤\n9\n旺\n膘\n待\n脾\n惫\n购\n吗\n依\n盲\n度\n瘿\n蠖\n俾\n之\n镗\n拇\n鲵\n厝\n簧\n续\n款\n展\n啃\n表\n剔\n品\n钻\n腭\n损\n清\n锶\n统\n涌\n寸\n滨\n贪\n链\n吠\n冈\n伎\n迥\n咏\n吁\n览\n防\n迅\n失\n汾\n阔\n逵\n绀\n蔑\n列\n川\n凭\n努\n熨\n揪\n利\n俱\n绉\n抢\n鸨\n我\n即\n责\n膦\n易\n毓\n鹊\n刹\n玷\n岿\n空\n嘞\n绊\n排\n术\n估\n锷\n违\n们\n苟\n铜\n播\n肘\n件\n烫\n审\n鲂\n广\n像\n铌\n惰\n铟\n巳\n胍\n鲍\n康\n憧\n色\n恢\n想\n拷\n尤\n疳\n知\nS\nY\nF\nD\nA\n峄\n裕\n帮\n握\n搔\n氐\n氘\n难\n墒\n沮\n雨\n叁\n缥\n悴\n藐\n湫\n娟\n苑\n稠\n颛\n簇\n后\n阕\n闭\n蕤\n缚\n怎\n佞\n码\n嘤\n蔡\n痊\n舱\n螯\n帕\n赫\n昵\n升\n烬\n岫\n、\n疵\n蜻\n髁\n蕨\n隶\n烛\n械\n丑\n盂\n梁\n强\n鲛\n由\n拘\n揉\n劭\n龟\n撤\n钩\n呕\n孛\n费\n妻\n漂\n求\n阑\n崖\n秤\n甘\n通\n深\n补\n赃\n坎\n床\n啪\n承\n吼\n量\n暇\n钼\n烨\n阂\n擎\n脱\n逮\n称\nP\n神\n属\n矗\n华\n届\n狍\n葑\n汹\n育\n患\n窒\n蛰\n佼\n静\n槎\n运\n鳗\n庆\n逝\n曼\n疱\n克\n代\n官\n此\n麸\n耧\n蚌\n晟\n例\n础\n榛\n副\n测\n唰\n缢\n迹\n灬\n霁\n身\n岁\n赭\n扛\n又\n菡\n乜\n雾\n板\n读\n陷\n徉\n贯\n郁\n虑\n变\n钓\n菜\n圾\n现\n琢\n式\n乐\n维\n渔\n浜\n左\n吾\n脑\n钡\n警\nT\n啵\n拴\n偌\n漱\n湿\n硕\n止\n骼\n魄\n积\n燥\n联\n踢\n玛\n则\n窿\n见\n振\n畿\n送\n班\n钽\n您\n赵\n刨\n印\n讨\n踝\n籍\n谡\n舌\n崧\n汽\n蔽\n沪\n酥\n绒\n怖\n财\n帖\n肱\n私\n莎\n勋\n羔\n霸\n励\n哼\n帐\n将\n帅\n渠\n纪\n婴\n娩\n岭\n厘\n滕\n吻\n伤\n坝\n冠\n戊\n隆\n瘁\n介\n涧\n物\n黍\n并\n姗\n奢\n蹑\n掣\n垸\n锴\n命\n箍\n捉\n病\n辖\n琰\n眭\n迩\n艘\n绌\n繁\n寅\n若\n毋\n思\n诉\n类\n诈\n燮\n轲\n酮\n狂\n重\n反\n职\n筱\n县\n委\n磕\n绣\n奖\n晋\n濉\n志\n徽\n肠\n呈\n獐\n坻\n口\n片\n碰\n几\n村\n柿\n劳\n料\n获\n亩\n惕\n晕\n厌\n号\n罢\n池\n正\n鏖\n煨\n家\n棕\n复\n尝\n懋\n蜥\n锅\n岛\n扰\n队\n坠\n瘾\n钬\n@\n卧\n疣\n镇\n譬\n冰\n彷\n频\n黯\n据\n垄\n采\n八\n缪\n瘫\n型\n熹\n砰\n楠\n襁\n箐\n但\n嘶\n绳\n啤\n拍\n盥\n穆\n傲\n洗\n盯\n塘\n怔\n筛\n丿\n台\n恒\n喂\n葛\n永\n￥\n烟\n酒\n桦\n书\n砂\n蚝\n缉\n态\n瀚\n袄\n圳\n轻\n蛛\n超\n榧\n遛\n姒\n奘\n铮\n右\n荽\n望\n偻\n卡\n丶\n氰\n附\n做\n革\n索\n戚\n坨\n桷\n唁\n垅\n榻\n岐\n偎\n坛\n莨\n山\n殊\n微\n骇\n陈\n爨\n推\n嗝\n驹\n澡\n藁\n呤\n卤\n嘻\n糅\n逛\n侵\n郓\n酌\n德\n摇\n※\n鬃\n被\n慨\n殡\n羸\n昌\n泡\n戛\n鞋\n河\n宪\n沿\n玲\n鲨\n翅\n哽\n源\n铅\n语\n照\n邯\n址\n荃\n佬\n顺\n鸳\n町\n霭\n睾\n瓢\n夸\n椁\n晓\n酿\n痈\n咔\n侏\n券\n噎\n湍\n签\n嚷\n离\n午\n尚\n社\n锤\n背\n孟\n使\n浪\n缦\n潍\n鞅\n军\n姹\n驶\n笑\n鳟\n鲁\n》\n孽\n钜\n绿\n洱\n礴\n焯\n椰\n颖\n囔\n乌\n孔\n巴\n互\n性\n椽\n哞\n聘\n昨\n早\n暮\n胶\n炀\n隧\n低\n彗\n昝\n铁\n呓\n氽\n藉\n喔\n癖\n瑗\n姨\n权\n胱\n韦\n堑\n蜜\n酋\n楝\n砝\n毁\n靓\n歙\n锲\n究\n屋\n喳\n骨\n辨\n碑\n武\n鸠\n宫\n辜\n烊\n适\n坡\n殃\n培\n佩\n供\n走\n蜈\n迟\n翼\n况\n姣\n凛\n浔\n吃\n飘\n债\n犟\n金\n促\n苛\n崇\n坂\n莳\n畔\n绂\n兵\n蠕\n斋\n根\n砍\n亢\n欢\n恬\n崔\n剁\n餐\n榫\n快\n扶\n‖\n濒\n缠\n鳜\n当\n彭\n驭\n浦\n篮\n昀\n锆\n秸\n钳\n弋\n娣\n瞑\n夷\n龛\n苫\n拱\n致\n%\n嵊\n障\n隐\n弑\n初\n娓\n抉\n汩\n累\n蓖\n\"\n唬\n助\n苓\n昙\n押\n毙\n破\n城\n郧\n逢\n嚏\n獭\n瞻\n溱\n婿\n赊\n跨\n恼\n璧\n萃\n姻\n貉\n灵\n炉\n密\n氛\n陶\n砸\n谬\n衔\n点\n琛\n沛\n枳\n层\n岱\n诺\n脍\n榈\n埂\n征\n冷\n裁\n打\n蹴\n素\n瘘\n逞\n蛐\n聊\n激\n腱\n萘\n踵\n飒\n蓟\n吆\n取\n咙\n簋\n涓\n矩\n曝\n挺\n揣\n座\n你\n史\n舵\n焱\n尘\n苏\n笈\n脚\n溉\n榨\n诵\n樊\n邓\n焊\n义\n庶\n儋\n蟋\n蒲\n赦\n呷\n杞\n诠\n豪\n还\n试\n颓\n茉\n太\n除\n紫\n逃\n痴\n草\n充\n鳕\n珉\n祗\n墨\n渭\n烩\n蘸\n慕\n璇\n镶\n穴\n嵘\n恶\n骂\n险\n绋\n幕\n碉\n肺\n戳\n刘\n潞\n秣\n纾\n潜\n銮\n洛\n须\n罘\n销\n瘪\n汞\n兮\n屉\nr\n林\n厕\n质\n探\n划\n狸\n殚\n善\n煊\n烹\n〒\n锈\n逯\n宸\n辍\n泱\n柚\n袍\n远\n蹋\n嶙\n绝\n峥\n娥\n缍\n雀\n徵\n认\n镱\n谷\n=\n贩\n勉\n撩\n鄯\n斐\n洋\n非\n祚\n泾\n诒\n饿\n撬\n威\n晷\n搭\n芍\n锥\n笺\n蓦\n候\n琊\n档\n礁\n沼\n卵\n荠\n忑\n朝\n凹\n瑞\n头\n仪\n弧\n孵\n畏\n铆\n突\n衲\n车\n浩\n气\n茂\n悖\n厢\n枕\n酝\n戴\n湾\n邹\n飚\n攘\n锂\n写\n宵\n翁\n岷\n无\n喜\n丈\n挑\n嗟\n绛\n殉\n议\n槽\n具\n醇\n淞\n笃\n郴\n阅\n饼\n底\n壕\n砚\n弈\n询\n缕\n庹\n翟\n零\n筷\n暨\n舟\n闺\n甯\n撞\n麂\n茌\n蔼\n很\n珲\n捕\n棠\n角\n阉\n媛\n娲\n诽\n剿\n尉\n爵\n睬\n韩\n诰\n匣\n危\n糍\n镯\n立\n浏\n阳\n少\n盆\n舔\n擘\n匪\n申\n尬\n铣\n旯\n抖\n赘\n瓯\n居\nˇ\n哮\n游\n锭\n茏\n歌\n坏\n甚\n秒\n舞\n沙\n仗\n劲\n潺\n阿\n燧\n郭\n嗖\n霏\n忠\n材\n奂\n耐\n跺\n砀\n输\n岖\n媳\n氟\n极\n摆\n灿\n今\n扔\n腻\n枝\n奎\n药\n熄\n吨\n话\nq\n额\n慑\n嘌\n协\n喀\n壳\n埭\n视\n著\n於\n愧\n陲\n翌\n峁\n颅\n佛\n腹\n聋\n侯\n咎\n叟\n秀\n颇\n存\n较\n罪\n哄\n岗\n扫\n栏\n钾\n羌\n己\n璨\n枭\n霉\n煌\n涸\n衿\n键\n镝\n益\n岢\n奏\n连\n夯\n睿\n冥\n均\n糖\n狞\n蹊\n稻\n爸\n刿\n胥\n煜\n丽\n肿\n璃\n掸\n跚\n灾\n垂\n樾\n濑\n乎\n莲\n窄\n犹\n撮\n战\n馄\n软\n络\n显\n鸢\n胸\n宾\n妲\n恕\n埔\n蝌\n份\n遇\n巧\n瞟\n粒\n恰\n剥\n桡\n博\n讯\n凯\n堇\n阶\n滤\n卖\n斌\n骚\n彬\n兑\n磺\n樱\n舷\n两\n娱\n福\n仃\n差\n找\n桁\n÷\n净\n把\n阴\n污\n戬\n雷\n碓\n蕲\n楚\n罡\n焖\n抽\n妫\n咒\n仑\n闱\n尽\n邑\n菁\n爱\n贷\n沥\n鞑\n牡\n嗉\n崴\n骤\n塌\n嗦\n订\n拮\n滓\n捡\n锻\n次\n坪\n杩\n臃\n箬\n融\n珂\n鹗\n宗\n枚\n降\n鸬\n妯\n阄\n堰\n盐\n毅\n必\n杨\n崃\n俺\n甬\n状\n莘\n货\n耸\n菱\n腼\n铸\n唏\n痤\n孚\n澳\n懒\n溅\n翘\n疙\n杷\n淼\n缙\n骰\n喊\n悉\n砻\n坷\n艇\n赁\n界\n谤\n纣\n宴\n晃\n茹\n归\n饭\n梢\n铡\n街\n抄\n肼\n鬟\n苯\n颂\n撷\n戈\n炒\n咆\n茭\n瘙\n负\n仰\n客\n琉\n铢\n封\n卑\n珥\n椿\n镧\n窨\n鬲\n寿\n御\n袤\n铃\n萎\n砖\n餮\n脒\n裳\n肪\n孕\n嫣\n馗\n嵇\n恳\n氯\n江\n石\n褶\n冢\n祸\n阻\n狈\n羞\n银\n靳\n透\n咳\n叼\n敷\n芷\n啥\n它\n瓤\n兰\n痘\n懊\n逑\n肌\n往\n捺\n坊\n甩\n呻\n〃\n沦\n忘\n膻\n祟\n菅\n剧\n崆\n智\n坯\n臧\n霍\n墅\n攻\n眯\n倘\n拢\n骠\n铐\n庭\n岙\n瓠\n′\n缺\n泥\n迢\n捶\n?\n？\n郏\n喙\n掷\n沌\n纯\n秘\n种\n听\n绘\n固\n螨\n团\n香\n盗\n妒\n埚\n蓝\n拖\n旱\n荞\n铀\n血\n遏\n汲\n辰\n叩\n拽\n幅\n硬\n惶\n桀\n漠\n措\n泼\n唑\n齐\n肾\n念\n酱\n虚\n屁\n耶\n旗\n砦\n闵\n婉\n馆\n拭\n绅\n韧\n忏\n窝\n醋\n葺\n顾\n辞\n倜\n堆\n辋\n逆\n玟\n贱\n疾\n董\n惘\n倌\n锕\n淘\n嘀\n莽\n俭\n笏\n绑\n鲷\n杈\n择\n蟀\n粥\n嗯\n驰\n逾\n案\n谪\n褓\n胫\n哩\n昕\n颚\n鲢\n绠\n躺\n鹄\n崂\n儒\n俨\n丝\n尕\n泌\n啊\n萸\n彰\n幺\n吟\n骄\n苣\n弦\n脊\n瑰\n〈\n诛\n镁\n析\n闪\n剪\n侧\n哟\n框\n螃\n守\n嬗\n燕\n狭\n铈\n缮\n概\n迳\n痧\n鲲\n俯\n售\n笼\n痣\n扉\n挖\n满\n咋\n援\n邱\n扇\n歪\n便\n玑\n绦\n峡\n蛇\n叨\n〖\n泽\n胃\n斓\n喋\n怂\n坟\n猪\n该\n蚬\n炕\n弥\n赞\n棣\n晔\n娠\n挲\n狡\n创\n疖\n铕\n镭\n稷\n挫\n弭\n啾\n翔\n粉\n履\n苘\n哦\n楼\n秕\n铂\n土\n锣\n瘟\n挣\n栉\n习\n享\n桢\n袅\n磨\n桂\n谦\n延\n坚\n蔚\n噗\n署\n谟\n猬\n钎\n恐\n嬉\n雒\n倦\n衅\n亏\n璩\n睹\n刻\n殿\n王\n算\n雕\n麻\n丘\n柯\n骆\n丸\n塍\n谚\n添\n鲈\n垓\n桎\n蚯\n芥\n予\n飕\n镦\n谌\n窗\n醚\n菀\n亮\n搪\n莺\n蒿\n羁\n足\nJ\n真\n轶\n悬\n衷\n靛\n翊\n掩\n哒\n炅\n掐\n冼\n妮\nl\n谐\n稚\n荆\n擒\n犯\n陵\n虏\n浓\n崽\n刍\n陌\n傻\n孜\n千\n靖\n演\n矜\n钕\n煽\n杰\n酗\n渗\n伞\n栋\n俗\n泫\n戍\n罕\n沾\n疽\n灏\n煦\n芬\n磴\n叱\n阱\n榉\n湃\n蜀\n叉\n醒\n彪\n租\n郡\n篷\n屎\n良\n垢\n隗\n弱\n陨\n峪\n砷\n掴\n颁\n胎\n雯\n绵\n贬\n沐\n撵\n隘\n篙\n暖\n曹\n陡\n栓\n填\n臼\n彦\n瓶\n琪\n潼\n哪\n鸡\n摩\n啦\n俟\n锋\n域\n耻\n蔫\n疯\n纹\n撇\n毒\n绶\n痛\n酯\n忍\n爪\n赳\n歆\n嘹\n辕\n烈\n册\n朴\n钱\n吮\n毯\n癜\n娃\n谀\n邵\n厮\n炽\n璞\n邃\n丐\n追\n词\n瓒\n忆\n轧\n芫\n谯\n喷\n弟\n半\n冕\n裙\n掖\n墉\n绮\n寝\n苔\n势\n顷\n褥\n切\n衮\n君\n佳\n嫒\n蚩\n霞\n佚\n洙\n逊\n镖\n暹\n唛\n&\n殒\n顶\n碗\n獗\n轭\n铺\n蛊\n废\n恹\n汨\n崩\n珍\n那\n杵\n曲\n纺\n夏\n薰\n傀\n闳\n淬\n姘\n舀\n拧\n卷\n楂\n恍\n讪\n厩\n寮\n篪\n赓\n乘\n灭\n盅\n鞣\n沟\n慎\n挂\n饺\n鼾\n杳\n树\n缨\n丛\n絮\n娌\n臻\n嗳\n篡\n侩\n述\n衰\n矛\n圈\n蚜\n匕\n筹\n匿\n濞\n晨\n叶\n骋\n郝\n挚\n蚴\n滞\n增\n侍\n描\n瓣\n吖\n嫦\n蟒\n匾\n圣\n赌\n毡\n癞\n恺\n百\n曳\n需\n篓\n肮\n庖\n帏\n卿\n驿\n遗\n蹬\n鬓\n骡\n歉\n芎\n胳\n屐\n禽\n烦\n晌\n寄\n媾\n狄\n翡\n苒\n船\n廉\n终\n痞\n殇\n々\n畦\n饶\n改\n拆\n悻\n萄\n￡\n瓿\n乃\n訾\n桅\n匮\n溧\n拥\n纱\n铍\n骗\n蕃\n龋\n缬\n父\n佐\n疚\n栎\n醍\n掳\n蓄\nx\n惆\n颜\n鲆\n榆\n〔\n猎\n敌\n暴\n谥\n鲫\n贾\n罗\n玻\n缄\n扦\n芪\n癣\n落\n徒\n臾\n恿\n猩\n托\n邴\n肄\n牵\n春\n陛\n耀\n刊\n拓\n蓓\n邳\n堕\n寇\n枉\n淌\n啡\n湄\n兽\n酷\n萼\n碚\n濠\n萤\n夹\n旬\n戮\n梭\n琥\n椭\n昔\n勺\n蜊\n绐\n晚\n孺\n僵\n宣\n摄\n冽\n旨\n萌\n忙\n蚤\n眉\n噼\n蟑\n付\n契\n瓜\n悼\n颡\n壁\n曾\n窕\n颢\n澎\n仿\n俑\n浑\n嵌\n浣\n乍\n碌\n褪\n乱\n蔟\n隙\n玩\n剐\n葫\n箫\n纲\n围\n伐\n决\n伙\n漩\n瑟\n刑\n肓\n镳\n缓\n蹭\n氨\n皓\n典\n畲\n坍\n铑\n檐\n塑\n洞\n倬\n储\n胴\n淳\n戾\n吐\n灼\n惺\n妙\n毕\n珐\n缈\n虱\n盖\n羰\n鸿\n磅\n谓\n髅\n娴\n苴\n唷\n蚣\n霹\n抨\n贤\n唠\n犬\n誓\n逍\n庠\n逼\n麓\n籼\n釉\n呜\n碧\n秧\n氩\n摔\n霄\n穸\n纨\n辟\n妈\n映\n完\n牛\n缴\n嗷\n炊\n恩\n荔\n茆\n掉\n紊\n慌\n莓\n羟\n阙\n萁\n磐\n另\n蕹\n辱\n鳐\n湮\n吡\n吩\n唐\n睦\n垠\n舒\n圜\n冗\n瞿\n溺\n芾\n囱\n匠\n僳\n汐\n菩\n饬\n漓\n黑\n霰\n浸\n濡\n窥\n毂\n蒡\n兢\n驻\n鹉\n芮\n诙\n迫\n雳\n厂\n忐\n臆\n猴\n鸣\n蚪\n栈\n箕\n羡\n渐\n莆\n捍\n眈\n哓\n趴\n蹼\n埕\n嚣\n骛\n宏\n淄\n斑\n噜\n严\n瑛\n垃\n椎\n诱\n压\n庾\n绞\n焘\n廿\n抡\n迄\n棘\n夫\n纬\n锹\n眨\n瞌\n侠\n脐\n竞\n瀑\n孳\n骧\n遁\n姜\n颦\n荪\n滚\n萦\n伪\n逸\n粳\n爬\n锁\n矣\n役\n趣\n洒\n颔\n诏\n逐\n奸\n甭\n惠\n攀\n蹄\n泛\n尼\n拼\n阮\n鹰\n亚\n颈\n惑\n勒\n〉\n际\n肛\n爷\n刚\n钨\n丰\n养\n冶\n鲽\n辉\n蔻\n画\n覆\n皴\n妊\n麦\n返\n醉\n皂\n擀\n〗\n酶\n凑\n粹\n悟\n诀\n硖\n港\n卜\nz\n杀\n涕\n±\n舍\n铠\n抵\n弛\n段\n敝\n镐\n奠\n拂\n轴\n跛\n袱\ne\nt\n沉\n菇\n俎\n薪\n峦\n秭\n蟹\n历\n盟\n菠\n寡\n液\n肢\n喻\n染\n裱\n悱\n抱\n氙\n赤\n捅\n猛\n跑\n氮\n谣\n仁\n尺\n辊\n窍\n烙\n衍\n架\n擦\n倏\n璐\n瑁\n币\n楞\n胖\n夔\n趸\n邛\n惴\n饕\n虔\n蝎\n§\n哉\n贝\n宽\n辫\n炮\n扩\n饲\n籽\n魏\n菟\n锰\n伍\n猝\n末\n琳\n哚\n蛎\n邂\n呀\n姿\n鄞\n却\n歧\n仙\n恸\n椐\n森\n牒\n寤\n袒\n婆\n虢\n雅\n钉\n朵\n贼\n欲\n苞\n寰\n故\n龚\n坭\n嘘\n咫\n礼\n硷\n兀\n睢\n汶\n’\n铲\n烧\n绕\n诃\n浃\n钿\n哺\n柜\n讼\n颊\n璁\n腔\n洽\n咐\n脲\n簌\n筠\n镣\n玮\n鞠\n谁\n兼\n姆\n挥\n梯\n蝴\n谘\n漕\n刷\n躏\n宦\n弼\nb\n垌\n劈\n麟\n莉\n揭\n笙\n渎\n仕\n嗤\n仓\n配\n怏\n抬\n错\n泯\n镊\n孰\n猿\n邪\n仍\n秋\n鼬\n壹\n歇\n吵\n炼\n<\n尧\n射\n柬\n廷\n胧\n霾\n凳\n隋\n肚\n浮\n梦\n祥\n株\n堵\n退\nL\n鹫\n跎\n凶\n毽\n荟\n炫\n栩\n玳\n甜\n沂\n鹿\n顽\n伯\n爹\n赔\n蛴\n徐\n匡\n欣\n狰\n缸\n雹\n蟆\n疤\n默\n沤\n啜\n痂\n衣\n禅\nw\ni\nh\n辽\n葳\n黝\n钗\n停\n沽\n棒\n馨\n颌\n肉\n吴\n硫\n悯\n劾\n娈\n马\n啧\n吊\n悌\n镑\n峭\n帆\n瀣\n涉\n咸\n疸\n滋\n泣\n翦\n拙\n癸\n钥\n蜒\n+\n尾\n庄\n凝\n泉\n婢\n渴\n谊\n乞\n陆\n锉\n糊\n鸦\n淮\nI\nB\nN\n晦\n弗\n乔\n庥\n葡\n尻\n席\n橡\n傣\n渣\n拿\n惩\n麋\n斛\n缃\n矮\n蛏\n岘\n鸽\n姐\n膏\n催\n奔\n镒\n喱\n蠡\n摧\n钯\n胤\n柠\n拐\n璋\n鸥\n卢\n荡\n倾\n^\n_\n珀\n逄\n萧\n塾\n掇\n贮\n笆\n聂\n圃\n冲\n嵬\nM\n滔\n笕\n值\n炙\n偶\n蜱\n搐\n梆\n汪\n蔬\n腑\n鸯\n蹇\n敞\n绯\n仨\n祯\n谆\n梧\n糗\n鑫\n啸\n豺\n囹\n猾\n巢\n柄\n瀛\n筑\n踌\n沭\n暗\n苁\n鱿\n蹉\n脂\n蘖\n牢\n热\n木\n吸\n溃\n宠\n序\n泞\n偿\n拜\n檩\n厚\n朐\n毗\n螳\n吞\n媚\n朽\n担\n蝗\n橘\n畴\n祈\n糟\n盱\n隼\n郜\n惜\n珠\n裨\n铵\n焙\n琚\n唯\n咚\n噪\n骊\n丫\n滢\n勤\n棉\n呸\n咣\n淀\n隔\n蕾\n窈\n饨\n挨\n煅\n短\n匙\n粕\n镜\n赣\n撕\n墩\n酬\n馁\n豌\n颐\n抗\n酣\n氓\n佑\n搁\n哭\n递\n耷\n涡\n桃\n贻\n碣\n截\n瘦\n昭\n镌\n蔓\n氚\n甲\n猕\n蕴\n蓬\n散\n拾\n纛\n狼\n猷\n铎\n埋\n旖\n矾\n讳\n囊\n糜\n迈\n粟\n蚂\n紧\n鲳\n瘢\n栽\n稼\n羊\n锄\n斟\n睁\n桥\n瓮\n蹙\n祉\n醺\n鼻\n昱\n剃\n跳\n篱\n跷\n蒜\n翎\n宅\n晖\n嗑\n壑\n峻\n癫\n屏\n狠\n陋\n袜\n途\n憎\n祀\n莹\n滟\n佶\n溥\n臣\n约\n盛\n峰\n磁\n慵\n婪\n拦\n莅\n朕\n鹦\n粲\n裤\n哎\n疡\n嫖\n琵\n窟\n堪\n谛\n嘉\n儡\n鳝\n斩\n郾\n驸\n酊\n妄\n胜\n贺\n徙\n傅\n噌\n钢\n栅\n庇\n恋\n匝\n巯\n邈\n尸\n锚\n粗\n佟\n蛟\n薹\n纵\n蚊\n郅\n绢\n锐\n苗\n俞\n篆\n淆\n膀\n鲜\n煎\n诶\n秽\n寻\n涮\n刺\n怀\n噶\n巨\n褰\n魅\n灶\n灌\n桉\n藕\n谜\n舸\n薄\n搀\n恽\n借\n牯\n痉\n渥\n愿\n亓\n耘\n杠\n柩\n锔\n蚶\n钣\n珈\n喘\n蹒\n幽\n赐\n稗\n晤\n莱\n泔\n扯\n肯\n菪\n裆\n腩\n豉\n疆\n骜\n腐\n倭\n珏\n唔\n粮\n亡\n润\n慰\n伽\n橄\n玄\n誉\n醐\n胆\n龊\n粼\n塬\n陇\n彼\n削\n嗣\n绾\n芽\n妗\n垭\n瘴\n爽\n薏\n寨\n龈\n泠\n弹\n赢\n漪\n猫\n嘧\n涂\n恤\n圭\n茧\n烽\n屑\n痕\n巾\n赖\n荸\n凰\n腮\n畈\n亵\n蹲\n偃\n苇\n澜\n艮\n换\n骺\n烘\n苕\n梓\n颉\n肇\n哗\n悄\n氤\n涠\n葬\n屠\n鹭\n植\n竺\n佯\n诣\n鲇\n瘀\n鲅\n邦\n移\n滁\n冯\n耕\n癔\n戌\n茬\n沁\n巩\n悠\n湘\n洪\n痹\n锟\n循\n谋\n腕\n鳃\n钠\n捞\n焉\n迎\n碱\n伫\n急\n榷\n奈\n邝\n卯\n辄\n皲\n卟\n醛\n畹\n忧\n稳\n雄\n昼\n缩\n阈\n睑\n扌\n耗\n曦\n涅\n捏\n瞧\n邕\n淖\n漉\n铝\n耦\n禹\n湛\n喽\n莼\n琅\n诸\n苎\n纂\n硅\n始\n嗨\n傥\n燃\n臂\n赅\n嘈\n呆\n贵\n屹\n壮\n肋\n亍\n蚀\n卅\n豹\n腆\n邬\n迭\n浊\n}\n童\n螂\n捐\n圩\n勐\n触\n寞\n汊\n壤\n荫\n膺\n渌\n芳\n懿\n遴\n螈\n泰\n蓼\n蛤\n茜\n舅\n枫\n朔\n膝\n眙\n避\n梅\n判\n鹜\n璜\n牍\n缅\n垫\n藻\n黔\n侥\n惚\n懂\n踩\n腰\n腈\n札\n丞\n唾\n慈\n顿\n摹\n荻\n琬\n~\n斧\n沈\n滂\n胁\n胀\n幄\n莜\nZ\n匀\n鄄\n掌\n绰\n茎\n焚\n赋\n萱\n谑\n汁\n铒\n瞎\n夺\n蜗\n野\n娆\n冀\n弯\n篁\n懵\n灞\n隽\n芡\n脘\n俐\n辩\n芯\n掺\n喏\n膈\n蝈\n觐\n悚\n踹\n蔗\n熠\n鼠\n呵\n抓\n橼\n峨\n畜\n缔\n禾\n崭\n弃\n熊\n摒\n凸\n拗\n穹\n蒙\n抒\n祛\n劝\n闫\n扳\n阵\n醌\n踪\n喵\n侣\n搬\n仅\n荧\n赎\n蝾\n琦\n买\n婧\n瞄\n寓\n皎\n冻\n赝\n箩\n莫\n瞰\n郊\n笫\n姝\n筒\n枪\n遣\n煸\n袋\n舆\n痱\n涛\n母\n〇\n启\n践\n耙\n绲\n盘\n遂\n昊\n搞\n槿\n诬\n纰\n泓\n惨\n檬\n亻\n越\nC\no\n憩\n熵\n祷\n钒\n暧\n塔\n阗\n胰\n咄\n娶\n魔\n琶\n钞\n邻\n扬\n杉\n殴\n咽\n弓\n〆\n髻\n】\n吭\n揽\n霆\n拄\n殖\n脆\n彻\n岩\n芝\n勃\n辣\n剌\n钝\n嘎\n甄\n佘\n皖\n伦\n授\n徕\n憔\n挪\n皇\n庞\n稔\n芜\n踏\n溴\n兖\n卒\n擢\n饥\n鳞\n煲\n‰\n账\n颗\n叻\n斯\n捧\n鳍\n琮\n讹\n蛙\n纽\n谭\n酸\n兔\n莒\n睇\n伟\n觑\n羲\n嗜\n宜\n褐\n旎\n辛\n卦\n诘\n筋\n鎏\n溪\n挛\n熔\n阜\n晰\n鳅\n丢\n奚\n灸\n呱\n献\n陉\n黛\n鸪\n甾\n萨\n疮\n拯\n洲\n疹\n辑\n叙\n恻\n谒\n允\n柔\n烂\n氏\n逅\n漆\n拎\n惋\n扈\n湟\n纭\n啕\n掬\n擞\n哥\n忽\n涤\n鸵\n靡\n郗\n瓷\n扁\n廊\n怨\n雏\n钮\n敦\nE\n懦\n憋\n汀\n拚\n啉\n腌\n岸\nf\n痼\n瞅\n尊\n咀\n眩\n飙\n忌\n仝\n迦\n熬\n毫\n胯\n篑\n茄\n腺\n凄\n舛\n碴\n锵\n诧\n羯\n後\n漏\n汤\n宓\n仞\n蚁\n壶\n谰\n皑\n铄\n棰\n罔\n辅\n晶\n苦\n牟\n闽\n\\\n烃\n饮\n聿\n丙\n蛳\n朱\n煤\n涔\n鳖\n犁\n罐\n荼\n砒\n淦\n妤\n黏\n戎\n孑\n婕\n瑾\n戢\n钵\n枣\n捋\n砥\n衩\n狙\n桠\n稣\n阎\n肃\n梏\n诫\n孪\n昶\n婊\n衫\n嗔\n侃\n塞\n蜃\n樵\n峒\n貌\n屿\n欺\n缫\n阐\n栖\n诟\n珞\n荭\n吝\n萍\n嗽\n恂\n啻\n蜴\n磬\n峋\n俸\n豫\n谎\n徊\n镍\n韬\n魇\n晴\nU\n囟\n猜\n蛮\n坐\n囿\n伴\n亭\n肝\n佗\n蝠\n妃\n胞\n滩\n榴\n氖\n垩\n苋\n砣\n扪\n馏\n姓\n轩\n厉\n夥\n侈\n禀\n垒\n岑\n赏\n钛\n辐\n痔\n披\n纸\n碳\n“\n坞\n蠓\n挤\n荥\n沅\n悔\n铧\n帼\n蒌\n蝇\na\np\ny\nn\ng\n哀\n浆\n瑶\n凿\n桶\n馈\n皮\n奴\n苜\n佤\n伶\n晗\n铱\n炬\n优\n弊\n氢\n恃\n甫\n攥\n端\n锌\n灰\n稹\n炝\n曙\n邋\n亥\n眶\n碾\n拉\n萝\n绔\n捷\n浍\n腋\n姑\n菖\n凌\n涞\n麽\n锢\n桨\n潢\n绎\n镰\n殆\n锑\n渝\n铬\n困\n绽\n觎\n匈\n糙\n暑\n裹\n鸟\n盔\n肽\n迷\n綦\n『\n亳\n佝\n俘\n钴\n觇\n骥\n仆\n疝\n跪\n婶\n郯\n瀹\n唉\n脖\n踞\n针\n晾\n忒\n扼\n瞩\n叛\n椒\n疟\n嗡\n邗\n肆\n跆\n玫\n忡\n捣\n咧\n唆\n艄\n蘑\n潦\n笛\n阚\n沸\n泻\n掊\n菽\n贫\n斥\n髂\n孢\n镂\n赂\n麝\n鸾\n屡\n衬\n苷\n恪\n叠\n希\n粤\n爻\n喝\n茫\n惬\n郸\n绻\n庸\n撅\n碟\n宄\n妹\n膛\n叮\n饵\n崛\n嗲\n椅\n冤\n搅\n咕\n敛\n尹\n垦\n闷\n蝉\n霎\n勰\n败\n蓑\n泸\n肤\n鹌\n幌\n焦\n浠\n鞍\n刁\n舰\n乙\n竿\n裔\n。\n茵\n函\n伊\n兄\n丨\n娜\n匍\n謇\n莪\n宥\n似\n蝽\n翳\n酪\n翠\n粑\n薇\n祢\n骏\n赠\n叫\nQ\n噤\n噻\n竖\n芗\n莠\n潭\n俊\n羿\n耜\nO\n郫\n趁\n嗪\n囚\n蹶\n芒\n洁\n笋\n鹑\n敲\n硝\n啶\n堡\n渲\n揩\n』\n携\n宿\n遒\n颍\n扭\n棱\n割\n萜\n蔸\n葵\n琴\n捂\n饰\n衙\n耿\n掠\n募\n岂\n窖\n涟\n蔺\n瘤\n柞\n瞪\n怜\n匹\n距\n楔\n炜\n哆\n秦\n缎\n幼\n茁\n绪\n痨\n恨\n楸\n娅\n瓦\n桩\n雪\n嬴\n伏\n榔\n妥\n铿\n拌\n眠\n雍\n缇\n‘\n卓\n搓\n哌\n觞\n噩\n屈\n哧\n髓\n咦\n巅\n娑\n侑\n淫\n膳\n祝\n勾\n姊\n莴\n胄\n疃\n薛\n蜷\n胛\n巷\n芙\n芋\n熙\n闰\n勿\n窃\n狱\n剩\n钏\n幢\n陟\n铛\n慧\n靴\n耍\nk\n浙\n浇\n飨\n惟\n绗\n祜\n澈\n啼\n咪\n磷\n摞\n诅\n郦\n抹\n跃\n壬\n吕\n肖\n琏\n颤\n尴\n剡\n抠\n凋\n赚\n泊\n津\n宕\n殷\n倔\n氲\n漫\n邺\n涎\n怠\n$\n垮\n荬\n遵\n俏\n叹\n噢\n饽\n蜘\n孙\n筵\n疼\n鞭\n羧\n牦\n箭\n潴\nc\n眸\n祭\n髯\n啖\n坳\n愁\n芩\n驮\n倡\n巽\n穰\n沃\n胚\n怒\n凤\n槛\n剂\n趵\n嫁\nv\n邢\n灯\n鄢\n桐\n睽\n檗\n锯\n槟\n婷\n嵋\n圻\n诗\n蕈\n颠\n遭\n痢\n芸\n怯\n馥\n竭\n锗\n徜\n恭\n遍\n籁\n剑\n嘱\n苡\n龄\n僧\n桑\n潸\n弘\n澶\n楹\n悲\n讫\n愤\n腥\n悸\n谍\n椹\n呢\n桓\n葭\n攫\n阀\n翰\n躲\n敖\n柑\n郎\n笨\n橇\n呃\n魁\n燎\n脓\n葩\n磋\n垛\n玺\n狮\n沓\n砜\n蕊\n锺\n罹\n蕉\n翱\n虐\n闾\n巫\n旦\n茱\n嬷\n枯\n鹏\n贡\n芹\n汛\n矫\n绁\n拣\n禺\n佃\n讣\n舫\n惯\n乳\n趋\n疲\n挽\n岚\n虾\n衾\n蠹\n蹂\n飓\n氦\n铖\n孩\n稞\n瑜\n壅\n掀\n勘\n妓\n畅\n髋\nW\n庐\n牲\n蓿\n榕\n练\n垣\n唱\n邸\n菲\n昆\n婺\n穿\n绡\n麒\n蚱\n掂\n愚\n泷\n涪\n漳\n妩\n娉\n榄\n讷\n觅\n旧\n藤\n煮\n呛\n柳\n腓\n叭\n庵\n烷\n阡\n罂\n蜕\n擂\n猖\n咿\n媲\n脉\n【\n沏\n貅\n黠\n熏\n哲\n烁\n坦\n酵\n兜\n×\n潇\n撒\n剽\n珩\n圹\n乾\n摸\n樟\n帽\n嗒\n襄\n魂\n轿\n憬\n锡\n〕\n喃\n皆\n咖\n隅\n脸\n残\n泮\n袂\n鹂\n珊\n囤\n捆\n咤\n误\n徨\n闹\n淙\n芊\n淋\n怆\n囗\n拨\n梳\n渤\nR\nG\n绨\n蚓\n婀\n幡\n狩\n麾\n谢\n唢\n裸\n旌\n伉\n纶\n裂\n驳\n砼\n咛\n澄\n樨\n蹈\n宙\n澍\n倍\n貔\n操\n勇\n蟠\n摈\n砧\n虬\n够\n缁\n悦\n藿\n撸\n艹\n摁\n淹\n豇\n虎\n榭\nˉ\n吱\nd\n°\n喧\n荀\n踱\n侮\n奋\n偕\n饷\n犍\n惮\n坑\n璎\n徘\n宛\n妆\n袈\n倩\n窦\n昂\n荏\n乖\nK\n怅\n撰\n鳙\n牙\n袁\n酞\nX\n痿\n琼\n闸\n雁\n趾\n荚\n虻\n涝\n《\n杏\n韭\n偈\n烤\n绫\n鞘\n卉\n症\n遢\n蓥\n诋\n杭\n荨\n匆\n竣\n簪\n辙\n敕\n虞\n丹\n缭\n咩\n黟\nm\n淤\n瑕\n咂\n铉\n硼\n茨\n嶂\n痒\n畸\n敬\n涿\n粪\n窘\n熟\n叔\n嫔\n盾\n忱\n裘\n憾\n梵\n赡\n珙\n咯\n娘\n庙\n溯\n胺\n葱\n痪\n摊\n荷\n卞\n乒\n髦\n寐\n铭\n坩\n胗\n枷\n爆\n溟\n嚼\n羚\n砬\n轨\n惊\n挠\n罄\n竽\n菏\n氧\n浅\n楣\n盼\n枢\n炸\n阆\n杯\n谏\n噬\n淇\n渺\n俪\n秆\n墓\n泪\n跻\n砌\n痰\n垡\n渡\n耽\n釜\n讶\n鳎\n煞\n呗\n韶\n舶\n绷\n鹳\n缜\n旷\n铊\n皱\n龌\n檀\n霖\n奄\n槐\n艳\n蝶\n旋\n哝\n赶\n骞\n蚧\n腊\n盈\n丁\n`\n蜚\n矸\n蝙\n睨\n嚓\n僻\n鬼\n醴\n夜\n彝\n磊\n笔\n拔\n栀\n糕\n厦\n邰\n纫\n逭\n纤\n眦\n膊\n馍\n躇\n烯\n蘼\n冬\n诤\n暄\n骶\n哑\n瘠\n」\n臊\n丕\n愈\n咱\n螺\n擅\n跋\n搏\n硪\n谄\n笠\n淡\n嘿\n骅\n谧\n鼎\n皋\n姚\n歼\n蠢\n驼\n耳\n胬\n挝\n涯\n狗\n蒽\n孓\n犷\n凉\n芦\n箴\n铤\n孤\n嘛\n坤\nV\n茴\n朦\n挞\n尖\n橙\n诞\n搴\n碇\n洵\n浚\n帚\n蜍\n漯\n柘\n嚎\n讽\n芭\n荤\n咻\n祠\n秉\n跖\n埃\n吓\n糯\n眷\n馒\n惹\n娼\n鲑\n嫩\n讴\n轮\n瞥\n靶\n褚\n乏\n缤\n宋\n帧\n删\n驱\n碎\n扑\n俩\n俄\n偏\n涣\n竹\n噱\n皙\n佰\n渚\n唧\n斡\n#\n镉\n刀\n崎\n筐\n佣\n夭\n贰\n肴\n峙\n哔\n艿\n匐\n牺\n镛\n缘\n仡\n嫡\n劣\n枸\n堀\n梨\n簿\n鸭\n蒸\n亦\n稽\n浴\n{\n衢\n束\n槲\nj\n阁\n揍\n疥\n棋\n潋\n聪\n窜\n乓\n睛\n插\n冉\n阪\n苍\n搽\n「\n蟾\n螟\n幸\n仇\n樽\n撂\n慢\n跤\n幔\n俚\n淅\n覃\n觊\n溶\n妖\n帛\n侨\n曰\n妾\n泗\n·\n：\n瀘\n風\nË\n（\n）\n∶\n紅\n紗\n瑭\n雲\n頭\n鶏\n財\n許\n•\n¥\n樂\n焗\n麗\n—\n；\n滙\n東\n榮\n繪\n興\n…\n門\n業\nπ\n楊\n國\n顧\né\n盤\n寳\nΛ\n龍\n鳳\n島\n誌\n緣\n結\n銭\n萬\n勝\n祎\n璟\n優\n歡\n臨\n時\n購\n＝\n★\n藍\n昇\n鐵\n觀\n勅\n農\n聲\n畫\n兿\n術\n發\n劉\n記\n專\n耑\n園\n書\n壴\n種\nΟ\n●\n褀\n號\n銀\n匯\n敟\n锘\n葉\n橪\n廣\n進\n蒄\n鑽\n阝\n祙\n貢\n鍋\n豊\n夬\n喆\n團\n閣\n開\n燁\n賓\n館\n酡\n沔\n順\n＋\n硚\n劵\n饸\n陽\n車\n湓\n復\n萊\n氣\n軒\n華\n堃\n迮\n纟\n戶\n馬\n學\n裡\n電\n嶽\n獨\nマ\nシ\nサ\nジ\n燘\n袪\n環\n❤\n臺\n灣\n専\n賣\n孖\n聖\n攝\n線\n▪\nα\n傢\n俬\n夢\n達\n莊\n喬\n貝\n薩\n劍\n羅\n壓\n棛\n饦\n尃\n璈\n囍\n醫\nＧ\nＩ\nＡ\n＃\nＮ\n鷄\n髙\n嬰\n啓\n約\n隹\n潔\n賴\n藝\n～\n寶\n籣\n麺\n　\n嶺\n√\n義\n網\n峩\n長\n∧\n魚\n機\n構\n②\n鳯\n偉\nＬ\nＢ\n㙟\n畵\n鴿\n＇\n詩\n溝\n嚞\n屌\n藔\n佧\n玥\n蘭\n織\n１\n３\n９\n０\n７\n點\n砭\n鴨\n鋪\n銘\n廳\n弍\n‧\n創\n湯\n坶\n℃\n卩\n骝\n＆\n烜\n荘\n當\n潤\n扞\n係\n懷\n碶\n钅\n蚨\n讠\n☆\n叢\n爲\n埗\n涫\n塗\n→\n楽\n現\n鯨\n愛\n瑪\n鈺\n忄\n悶\n藥\n飾\n樓\n視\n孬\nㆍ\n燚\n苪\n師\n①\n丼\n锽\n│\n韓\n標\nè\n兒\n閏\n匋\n張\n漢\nÜ\n髪\n會\n閑\n檔\n習\n裝\nの\n峯\n菘\n輝\nИ\n雞\n釣\n億\n浐\nＫ\nＯ\nＲ\n８\nＨ\nＥ\nＰ\nＴ\nＷ\nＤ\nＳ\nＣ\nＭ\nＦ\n姌\n饹\n»\n晞\n廰\nä\n嵯\n鷹\n負\n飲\n絲\n冚\n楗\n澤\n綫\n區\n❋\n←\n質\n靑\n揚\n③\n滬\n統\n産\n協\n﹑\n乸\n畐\n經\n運\n際\n洺\n岽\n為\n粵\n諾\n崋\n豐\n碁\nɔ\nＶ\n２\n６\n齋\n誠\n訂\n´\n勑\n雙\n陳\n無\ní\n泩\n媄\n夌\n刂\nｉ\nｃ\nｔ\nｏ\nｒ\nａ\n嘢\n耄\n燴\n暃\n壽\n媽\n靈\n抻\n體\n唻\nÉ\n冮\n甹\n鎮\n錦\nʌ\n蜛\n蠄\n尓\n駕\n戀\n飬\n逹\n倫\n貴\n極\nЯ\nЙ\n寬\n磚\n嶪\n郎\n職\n｜\n間\nｎ\nｄ\n剎\n伈\n課\n飛\n橋\n瘊\n№\n譜\n骓\n圗\n滘\n縣\n粿\n咅\n養\n濤\n彳\n®\n％\nⅡ\n啰\n㴪\n見\n矞\n薬\n糁\n邨\n鲮\n顔\n罱\nЗ\n選\n話\n贏\n氪\n俵\n競\n瑩\n繡\n枱\nβ\n綉\ná\n獅\n爾\n™\n麵\n戋\n淩\n徳\n個\n劇\n場\n務\n簡\n寵\nｈ\n實\n膠\n轱\n圖\n築\n嘣\n樹\n㸃\n營\n耵\n孫\n饃\n鄺\n飯\n麯\n遠\n輸\n坫\n孃\n乚\n閃\n鏢\n㎡\n題\n廠\n關\n↑\n爺\n將\n軍\n連\n篦\n覌\n參\n箸\n－\n窠\n棽\n寕\n夀\n爰\n歐\n呙\n閥\n頡\n熱\n雎\n垟\n裟\n凬\n勁\n帑\n馕\n夆\n疌\n枼\n馮\n貨\n蒤\n樸\n彧\n旸\n靜\n龢\n暢\n㐱\n鳥\n珺\n鏡\n灡\n爭\n堷\n廚\nÓ\n騰\n診\n┅\n蘇\n褔\n凱\n頂\n豕\n亞\n帥\n嘬\n⊥\n仺\n桖\n複\n饣\n絡\n穂\n顏\n棟\n納\n▏\n濟\n親\n設\n計\n攵\n埌\n烺\nò\n頤\n燦\n蓮\n撻\n節\n講\n濱\n濃\n娽\n洳\n朿\n燈\n鈴\n護\n膚\n铔\n過\n補\nＺ\nＵ\n５\n４\n坋\n闿\n䖝\n餘\n缐\n铞\n貿\n铪\n桼\n趙\n鍊\n［\n㐂\n垚\n菓\n揸\n捲\n鐘\n滏\n𣇉\n爍\n輪\n燜\n鴻\n鮮\n動\n鹞\n鷗\n丄\n慶\n鉌\n翥\n飮\n腸\n⇋\n漁\n覺\n來\n熘\n昴\n翏\n鲱\n圧\n鄉\n萭\n頔\n爐\n嫚\nг\n貭\n類\n聯\n幛\n輕\n訓\n鑒\n夋\n锨\n芃\n珣\n䝉\n扙\n嵐\n銷\n處\nㄱ\n語\n誘\n苝\n歸\n儀\n燒\n楿\n內\n粢\n葒\n奧\n麥\n礻\n滿\n蠔\n穵\n瞭\n態\n鱬\n榞\n硂\n鄭\n黃\n煙\n祐\n奓\n逺\n＊\n瑄\n獲\n聞\n薦\n讀\n這\n樣\n決\n問\n啟\n們\n執\n説\n轉\n單\n隨\n唘\n帶\n倉\n庫\n還\n贈\n尙\n皺\n■\n餅\n產\n○\n∈\n報\n狀\n楓\n賠\n琯\n嗮\n禮\n｀\n傳\n＞\n≤\n嗞\nΦ\n≥\n換\n咭\n∣\n↓\n曬\nε\n応\n寫\n″\n終\n様\n純\n費\n療\n聨\n凍\n壐\n郵\nü\n黒\n∫\n製\n塊\n調\n軽\n確\n撃\n級\n馴\nⅢ\n涇\n繹\n數\n碼\n證\n狒\n処\n劑\n＜\n晧\n賀\n衆\n］\n櫥\n兩\n陰\n絶\n對\n鯉\n憶\n◎\nｐ\nｅ\nＹ\n蕒\n煖\n頓\n測\n試\n鼽\n僑\n碩\n妝\n帯\n≈\n鐡\n舖\n權\n喫\n倆\nˋ\n該\n悅\nā\n俫\n．\nｆ\nｓ\nｂ\nｍ\nｋ\nｇ\nｕ\nｊ\n貼\n淨\n濕\n針\n適\n備\nｌ\n／\n給\n謢\n強\n觸\n衛\n與\n⊙\n＄\n緯\n變\n⑴\n⑵\n⑶\n㎏\n殺\n∩\n幚\n─\n價\n▲\n離\nú\nó\n飄\n烏\n関\n閟\n﹝\n﹞\n邏\n輯\n鍵\n驗\n訣\n導\n歷\n屆\n層\n▼\n儱\n錄\n熳\nē\n艦\n吋\n錶\n辧\n飼\n顯\n④\n禦\n販\n気\n対\n枰\n閩\n紀\n幹\n瞓\n貊\n淚\n△\n眞\n墊\nΩ\n獻\n褲\n縫\n緑\n亜\n鉅\n餠\n｛\n｝\n◆\n蘆\n薈\n█\n◇\n溫\n彈\n晳\n粧\n犸\n穩\n訊\n崬\n凖\n熥\nП\n舊\n條\n紋\n圍\nⅣ\n筆\n尷\n難\n雜\n錯\n綁\n識\n頰\n鎖\n艶\n□\n殁\n殼\n⑧\n├\n▕\n鵬\nǐ\nō\nǒ\n糝\n綱\n▎\nμ\n盜\n饅\n醬\n籤\n蓋\n釀\n鹽\n據\nà\nɡ\n辦\n◥\n彐\n┌\n婦\n獸\n鲩\n伱\nī\n蒟\n蒻\n齊\n袆\n腦\n寧\n凈\n妳\n煥\n詢\n偽\n謹\n啫\n鯽\n騷\n鱸\n損\n傷\n鎻\n髮\n買\n冏\n儥\n両\n﹢\n∞\n載\n喰\nｚ\n羙\n悵\n燙\n曉\n員\n組\n徹\n艷\n痠\n鋼\n鼙\n縮\n細\n嚒\n爯\n≠\n維\n＂\n鱻\n壇\n厍\n帰\n浥\n犇\n薡\n軎\n²\n應\n醜\n刪\n緻\n鶴\n賜\n噁\n軌\n尨\n镔\n鷺\n槗\n彌\n葚\n濛\n請\n溇\n緹\n賢\n訪\n獴\n瑅\n資\n縤\n陣\n蕟\n栢\n韻\n祼\n恁\n伢\n謝\n劃\n涑\n總\n衖\n踺\n砋\n凉\n籃\n駿\n苼\n瘋\n昽\n紡\n驊\n腎\n﹗\n響\n杋\n剛\n嚴\n禪\n歓\n槍\n傘\n檸\n檫\n炣\n勢\n鏜\n鎢\n銑\n尐\n減\n奪\n惡\nθ\n僮\n婭\n臘\nū\nì\n殻\n鉄\n∑\n蛲\n焼\n緖\n續\n紹\n懮"
  },
  {
    "path": "app/src/main/assets/pdocrv2.0_det-op.param",
    "content": "7767517\n115 131\nInput                    input                    0 1 input0\nConvolution              Conv_0                   1 1 input0 647 0=8 1=3 3=2 4=1 5=1 6=216\nHardSwish                Div_8                    1 1 647 330 0=1.666667e-01\nSplit                    splitncnn_0              1 2 330 330_splitncnn_0 330_splitncnn_1\nConvolution              Conv_9                   1 1 330_splitncnn_1 333 0=8 1=1 5=1 6=64 9=1\nConvolutionDepthWise     Conv_11                  1 1 333 336 0=8 1=3 4=1 5=1 6=72 7=8 9=1\nConvolution              Conv_13                  1 1 336 656 0=8 1=1 5=1 6=64\nBinaryOp                 Add_14                   2 1 330_splitncnn_0 656 339\nConvolution              Conv_15                  1 1 339 342 0=32 1=1 5=1 6=256 9=1\nConvolutionDepthWise     Conv_17                  1 1 342 345 0=32 1=3 3=2 4=1 5=1 6=288 7=32 9=1\nConvolution              Conv_19                  1 1 345 665 0=16 1=1 5=1 6=512\nSplit                    splitncnn_1              1 2 665 665_splitncnn_0 665_splitncnn_1\nConvolution              Conv_20                  1 1 665_splitncnn_1 350 0=40 1=1 5=1 6=640 9=1\nConvolutionDepthWise     Conv_22                  1 1 350 353 0=40 1=3 4=1 5=1 6=360 7=40 9=1\nConvolution              Conv_24                  1 1 353 674 0=16 1=1 5=1 6=640\nBinaryOp                 Add_25                   2 1 665_splitncnn_0 674 356\nSplit                    splitncnn_2              1 2 356 356_splitncnn_0 356_splitncnn_1\nConvolution              Conv_26                  1 1 356_splitncnn_1 359 0=40 1=1 5=1 6=640 9=1\nConvolutionDepthWise     Conv_28                  1 1 359 362 0=40 1=5 3=2 4=2 5=1 6=1000 7=40 9=1\nConvolution              Conv_30                  1 1 362 683 0=24 1=1 5=1 6=960\nSplit                    splitncnn_3              1 2 683 683_splitncnn_0 683_splitncnn_1\nConvolution              Conv_31                  1 1 683_splitncnn_1 367 0=64 1=1 5=1 6=1536 9=1\nConvolutionDepthWise     Conv_33                  1 1 367 370 0=64 1=5 4=2 5=1 6=1600 7=64 9=1\nConvolution              Conv_35                  1 1 370 692 0=24 1=1 5=1 6=1536\nBinaryOp                 Add_36                   2 1 683_splitncnn_0 692 373\nSplit                    splitncnn_4              1 2 373 373_splitncnn_0 373_splitncnn_1\nConvolution              Conv_37                  1 1 373_splitncnn_1 376 0=64 1=1 5=1 6=1536 9=1\nConvolutionDepthWise     Conv_39                  1 1 376 379 0=64 1=5 4=2 5=1 6=1600 7=64 9=1\nConvolution              Conv_41                  1 1 379 701 0=24 1=1 5=1 6=1536\nBinaryOp                 Add_42                   2 1 373_splitncnn_0 701 382\nSplit                    splitncnn_5              1 2 382 382_splitncnn_0 382_splitncnn_1\nConvolution              Conv_43                  1 1 382_splitncnn_1 704 0=120 1=1 5=1 6=2880\nHardSwish                Div_51                   1 1 704 392 0=1.666667e-01\nConvolutionDepthWise     Conv_52                  1 1 392 707 0=120 1=3 3=2 4=1 5=1 6=1080 7=120\nHardSwish                Div_60                   1 1 707 402 0=1.666667e-01\nConvolution              Conv_61                  1 1 402 710 0=40 1=1 5=1 6=4800\nSplit                    splitncnn_6              1 2 710 710_splitncnn_0 710_splitncnn_1\nConvolution              Conv_62                  1 1 710_splitncnn_1 713 0=104 1=1 5=1 6=4160\nHardSwish                Div_70                   1 1 713 414 0=1.666667e-01\nConvolutionDepthWise     Conv_71                  1 1 414 716 0=104 1=3 4=1 5=1 6=936 7=104\nHardSwish                Div_79                   1 1 716 424 0=1.666667e-01\nConvolution              Conv_80                  1 1 424 719 0=40 1=1 5=1 6=4160\nBinaryOp                 Add_81                   2 1 710_splitncnn_0 719 427\nSplit                    splitncnn_7              1 2 427 427_splitncnn_0 427_splitncnn_1\nConvolution              Conv_82                  1 1 427_splitncnn_1 722 0=96 1=1 5=1 6=3840\nHardSwish                Div_90                   1 1 722 437 0=1.666667e-01\nConvolutionDepthWise     Conv_91                  1 1 437 725 0=96 1=3 4=1 5=1 6=864 7=96\nHardSwish                Div_99                   1 1 725 447 0=1.666667e-01\nConvolution              Conv_100                 1 1 447 728 0=40 1=1 5=1 6=3840\nBinaryOp                 Add_101                  2 1 427_splitncnn_0 728 450\nSplit                    splitncnn_8              1 2 450 450_splitncnn_0 450_splitncnn_1\nConvolution              Conv_102                 1 1 450_splitncnn_1 731 0=96 1=1 5=1 6=3840\nHardSwish                Div_110                  1 1 731 460 0=1.666667e-01\nConvolutionDepthWise     Conv_111                 1 1 460 734 0=96 1=3 4=1 5=1 6=864 7=96\nHardSwish                Div_119                  1 1 734 470 0=1.666667e-01\nConvolution              Conv_120                 1 1 470 737 0=40 1=1 5=1 6=3840\nBinaryOp                 Add_121                  2 1 450_splitncnn_0 737 473\nConvolution              Conv_122                 1 1 473 740 0=240 1=1 5=1 6=9600\nHardSwish                Div_130                  1 1 740 483 0=1.666667e-01\nConvolutionDepthWise     Conv_131                 1 1 483 743 0=240 1=3 4=1 5=1 6=2160 7=240\nHardSwish                Div_139                  1 1 743 493 0=1.666667e-01\nConvolution              Conv_140                 1 1 493 746 0=56 1=1 5=1 6=13440\nSplit                    splitncnn_9              1 2 746 746_splitncnn_0 746_splitncnn_1\nConvolution              Conv_141                 1 1 746_splitncnn_1 749 0=336 1=1 5=1 6=18816\nHardSwish                Div_149                  1 1 749 505 0=1.666667e-01\nConvolutionDepthWise     Conv_150                 1 1 505 752 0=336 1=3 4=1 5=1 6=3024 7=336\nHardSwish                Div_158                  1 1 752 515 0=1.666667e-01\nConvolution              Conv_159                 1 1 515 755 0=56 1=1 5=1 6=18816\nBinaryOp                 Add_160                  2 1 746_splitncnn_0 755 518\nSplit                    splitncnn_10             1 2 518 518_splitncnn_0 518_splitncnn_1\nConvolution              Conv_161                 1 1 518_splitncnn_1 758 0=336 1=1 5=1 6=18816\nHardSwish                Div_169                  1 1 758 528 0=1.666667e-01\nConvolutionDepthWise     Conv_170                 1 1 528 761 0=336 1=5 3=2 4=2 5=1 6=8400 7=336\nHardSwish                Div_178                  1 1 761 538 0=1.666667e-01\nConvolution              Conv_179                 1 1 538 764 0=80 1=1 5=1 6=26880\nSplit                    splitncnn_11             1 2 764 764_splitncnn_0 764_splitncnn_1\nConvolution              Conv_180                 1 1 764_splitncnn_1 767 0=480 1=1 5=1 6=38400\nHardSwish                Div_188                  1 1 767 550 0=1.666667e-01\nConvolutionDepthWise     Conv_189                 1 1 550 770 0=480 1=5 4=2 5=1 6=12000 7=480\nHardSwish                Div_197                  1 1 770 560 0=1.666667e-01\nConvolution              Conv_198                 1 1 560 773 0=80 1=1 5=1 6=38400\nBinaryOp                 Add_199                  2 1 764_splitncnn_0 773 563\nSplit                    splitncnn_12             1 2 563 563_splitncnn_0 563_splitncnn_1\nConvolution              Conv_200                 1 1 563_splitncnn_1 776 0=480 1=1 5=1 6=38400\nHardSwish                Div_208                  1 1 776 573 0=1.666667e-01\nConvolutionDepthWise     Conv_209                 1 1 573 779 0=480 1=5 4=2 5=1 6=12000 7=480\nHardSwish                Div_217                  1 1 779 583 0=1.666667e-01\nConvolution              Conv_218                 1 1 583 782 0=80 1=1 5=1 6=38400\nBinaryOp                 Add_219                  2 1 563_splitncnn_0 782 586\nConvolution              Conv_220                 1 1 586 785 0=480 1=1 5=1 6=38400\nHardSwish                Div_228                  1 1 785 596 0=1.666667e-01\nConvolution              Conv_229                 1 1 596 597 0=96 1=1 6=46080\nSplit                    splitncnn_13             1 2 597 597_splitncnn_0 597_splitncnn_1\nConvolution              Conv_230                 1 1 518_splitncnn_0 598 0=96 1=1 6=5376\nConvolution              Conv_231                 1 1 382_splitncnn_0 599 0=96 1=1 6=2304\nConvolution              Conv_232                 1 1 356_splitncnn_0 600 0=96 1=1 6=1536\nInterp                   Resize_234               1 1 597_splitncnn_1 605 0=1 1=2.000000e+00 2=2.000000e+00\nBinaryOp                 Add_235                  2 1 598 605 606\nSplit                    splitncnn_14             1 2 606 606_splitncnn_0 606_splitncnn_1\nInterp                   Resize_237               1 1 606_splitncnn_1 611 0=1 1=2.000000e+00 2=2.000000e+00\nBinaryOp                 Add_238                  2 1 599 611 612\nSplit                    splitncnn_15             1 2 612 612_splitncnn_0 612_splitncnn_1\nInterp                   Resize_240               1 1 612_splitncnn_1 617 0=1 1=2.000000e+00 2=2.000000e+00\nBinaryOp                 Add_241                  2 1 600 617 618\nConvolution              Conv_242                 1 1 597_splitncnn_0 619 0=24 1=3 4=1 6=20736\nConvolution              Conv_243                 1 1 606_splitncnn_0 620 0=24 1=3 4=1 6=20736\nConvolution              Conv_244                 1 1 612_splitncnn_0 621 0=24 1=3 4=1 6=20736\nConvolution              Conv_245                 1 1 618 622 0=24 1=3 4=1 6=20736\nInterp                   Resize_247               1 1 619 627 0=1 1=8.000000e+00 2=8.000000e+00\nInterp                   Resize_249               1 1 620 632 0=1 1=4.000000e+00 2=4.000000e+00\nInterp                   Resize_251               1 1 621 637 0=1 1=2.000000e+00 2=2.000000e+00\nConcat                   Concat_252               4 1 627 632 637 622 638\nConvolution              Conv_253                 1 1 638 641 0=24 1=3 4=1 5=1 6=20736 9=1\nDeconvolution            ConvTranspose_255        1 1 641 644 0=24 1=2 3=2 5=1 6=2304 9=1\nDeconvolution            ConvTranspose_258        1 1 644 out1 0=1 1=2 3=2 5=1 6=96 9=4\n"
  },
  {
    "path": "app/src/main/assets/pdocrv2.0_rec-op.param",
    "content": "7767517\n75 77\nInput                    input                    0 1 input\nConvolution              Conv_0                   1 1 input 779 0=16 1=3 3=2 4=1 5=1 6=432\nHardSwish                Div_8                    1 1 779 200 0=1.666667e-01\nConvolutionDepthWise     Conv_9                   1 1 200 782 0=16 1=3 4=1 5=1 6=144 7=16\nHardSwish                Div_17                   1 1 782 210 0=1.666667e-01\nConvolution              Conv_18                  1 1 210 785 0=32 1=1 5=1 6=512\nHardSwish                Div_26                   1 1 785 220 0=1.666667e-01\nConvolutionDepthWise     Conv_27                  1 1 220 788 0=32 1=3 4=1 5=1 6=288 7=32\nHardSwish                Div_35                   1 1 788 230 0=1.666667e-01\nConvolution              Conv_36                  1 1 230 791 0=64 1=1 5=1 6=2048\nHardSwish                Div_44                   1 1 791 240 0=1.666667e-01\nConvolutionDepthWise     Conv_45                  1 1 240 794 0=64 1=3 4=1 5=1 6=576 7=64\nHardSwish                Div_53                   1 1 794 250 0=1.666667e-01\nConvolution              Conv_54                  1 1 250 797 0=64 1=1 5=1 6=4096\nHardSwish                Div_62                   1 1 797 260 0=1.666667e-01\nConvolutionDepthWise     Conv_63                  1 1 260 800 0=64 1=3 13=2 4=1 5=1 6=576 7=64\nHardSwish                Div_71                   1 1 800 270 0=1.666667e-01\nConvolution              Conv_72                  1 1 270 803 0=128 1=1 5=1 6=8192\nHardSwish                Div_80                   1 1 803 280 0=1.666667e-01\nConvolutionDepthWise     Conv_81                  1 1 280 806 0=128 1=3 4=1 5=1 6=1152 7=128\nHardSwish                Div_89                   1 1 806 290 0=1.666667e-01\nConvolution              Conv_90                  1 1 290 809 0=128 1=1 5=1 6=16384\nHardSwish                Div_98                   1 1 809 300 0=1.666667e-01\nConvolutionDepthWise     Conv_99                  1 1 300 812 0=128 1=3 13=2 4=1 5=1 6=1152 7=128\nHardSwish                Div_107                  1 1 812 310 0=1.666667e-01\nConvolution              Conv_108                 1 1 310 815 0=256 1=1 5=1 6=32768\nHardSwish                Div_116                  1 1 815 320 0=1.666667e-01\nConvolutionDepthWise     Conv_117                 1 1 320 818 0=256 1=5 4=2 5=1 6=6400 7=256\nHardSwish                Div_125                  1 1 818 330 0=1.666667e-01\nConvolution              Conv_126                 1 1 330 821 0=256 1=1 5=1 6=65536\nHardSwish                Div_134                  1 1 821 340 0=1.666667e-01\nConvolutionDepthWise     Conv_135                 1 1 340 824 0=256 1=5 4=2 5=1 6=6400 7=256\nHardSwish                Div_143                  1 1 824 350 0=1.666667e-01\nConvolution              Conv_144                 1 1 350 827 0=256 1=1 5=1 6=65536\nHardSwish                Div_152                  1 1 827 360 0=1.666667e-01\nConvolutionDepthWise     Conv_153                 1 1 360 830 0=256 1=5 4=2 5=1 6=6400 7=256\nHardSwish                Div_161                  1 1 830 370 0=1.666667e-01\nConvolution              Conv_162                 1 1 370 833 0=256 1=1 5=1 6=65536\nHardSwish                Div_170                  1 1 833 380 0=1.666667e-01\nConvolutionDepthWise     Conv_171                 1 1 380 836 0=256 1=5 4=2 5=1 6=6400 7=256\nHardSwish                Div_179                  1 1 836 390 0=1.666667e-01\nConvolution              Conv_180                 1 1 390 839 0=256 1=1 5=1 6=65536\nHardSwish                Div_188                  1 1 839 400 0=1.666667e-01\nConvolutionDepthWise     Conv_189                 1 1 400 842 0=256 1=5 4=2 5=1 6=6400 7=256\nHardSwish                Div_197                  1 1 842 410 0=1.666667e-01\nConvolution              Conv_198                 1 1 410 845 0=256 1=1 5=1 6=65536\nHardSwish                Div_206                  1 1 845 420 0=1.666667e-01\nConvolutionDepthWise     Conv_207                 1 1 420 848 0=256 1=5 13=2 4=2 5=1 6=6400 7=256\nHardSwish                Div_215                  1 1 848 430 0=1.666667e-01\nSplit                    splitncnn_0              1 2 430 430_splitncnn_0 430_splitncnn_1\nPooling                  GlobalAveragePool_216    1 1 430_splitncnn_1 431 0=1 4=1\nInnerProduct             Conv_217                 1 1 431 433 0=64 1=1 2=16384 9=1\nInnerProduct             Conv_219                 1 1 433 434 0=256 1=1 2=16384\nHardSigmoid              Div_226                  1 1 434 441 0=1.666667e-01\nBinaryOp                 Mul_227                  2 1 430_splitncnn_0 441 442 0=2\nConvolution              Conv_228                 1 1 442 851 0=512 1=1 5=1 6=131072\nHardSwish                Div_236                  1 1 851 452 0=1.666667e-01\nConvolutionDepthWise     Conv_237                 1 1 452 854 0=512 1=5 4=2 5=1 6=12800 7=512\nHardSwish                Div_245                  1 1 854 462 0=1.666667e-01\nSplit                    splitncnn_1              1 2 462 462_splitncnn_0 462_splitncnn_1\nPooling                  GlobalAveragePool_246    1 1 462_splitncnn_1 463 0=1 4=1\nInnerProduct             Conv_247                 1 1 463 465 0=128 1=1 2=65536 9=1\nInnerProduct             Conv_249                 1 1 465 466 0=512 1=1 2=65536\nHardSigmoid              Div_256                  1 1 466 473 0=1.666667e-01\nBinaryOp                 Mul_257                  2 1 462_splitncnn_0 473 474 0=2\nConvolution              Conv_258                 1 1 474 857 0=512 1=1 5=1 6=262144\nHardSwish                Div_266                  1 1 857 484 0=1.666667e-01\nPooling                  MaxPool_267              1 1 484 485 1=2 2=2 5=1\nReshape                  Reshape_281              1 1 485 499 0=-1 1=512\nPermute                  Transpose_289            1 1 499 511 0=1\nLSTM                     LSTM_298                 1 1 511 641 0=64 1=262144 2=2\nLSTM                     LSTM_310                 1 1 641 771 0=64 1=65536 2=2\nInnerProduct             MatMul_315               1 1 771 774 0=96 1=1 2=12288\nInnerProduct             MatMul_317               1 1 774 777 0=6625 1=1 2=636000\nSoftmax                  Softmax_319              1 1 777 out 0=1 1=1\n"
  },
  {
    "path": "app/src/main/assets/rec-sim-op.param",
    "content": "7767517\n138 154\nInput                    input                    0 1 input\nConvolution              Conv_0                   1 1 input 266 0=8 1=3 3=2 4=1 5=1 6=216\nHardSwish                Div_9                    1 1 266 274 0=1.666667e-01\nConvolution              Conv_10                  1 1 274 277 0=8 1=1 5=1 6=64 9=1\nConvolutionDepthWise     Conv_13                  1 1 277 280 0=8 1=3 4=1 5=1 6=72 7=8 9=1\nSplit                    splitncnn_0              1 2 280 280_splitncnn_0 280_splitncnn_1\nPooling                  GlobalAveragePool_16     1 1 280_splitncnn_1 281 0=1 4=1\nInnerProduct             Conv_17                  1 1 281 283 0=2 1=1 2=16 9=1\nInnerProduct             Conv_19                  1 1 283 284 0=8 1=1 2=16\nBinaryOp                 Mul_21                   1 1 284 286 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_28                   1 1 286 293 0=1.666667e-01\nBinaryOp                 Mul_29                   2 1 280_splitncnn_0 293 294 0=2\nConvolution              Conv_30                  1 1 294 296 0=8 1=1 5=1 6=64\nConvolution              Conv_32                  1 1 296 299 0=40 1=1 5=1 6=320 9=1\nConvolutionDepthWise     Conv_35                  1 1 299 302 0=40 1=3 13=2 4=1 5=1 6=360 7=40 9=1\nConvolution              Conv_38                  1 1 302 304 0=16 1=1 5=1 6=640\nSplit                    splitncnn_1              1 2 304 304_splitncnn_0 304_splitncnn_1\nConvolution              Conv_40                  1 1 304_splitncnn_1 307 0=48 1=1 5=1 6=768 9=1\nConvolutionDepthWise     Conv_43                  1 1 307 310 0=48 1=3 4=1 5=1 6=432 7=48 9=1\nConvolution              Conv_46                  1 1 310 312 0=16 1=1 5=1 6=768\nBinaryOp                 Add_48                   2 1 304_splitncnn_0 312 313\nConvolution              Conv_49                  1 1 313 315 0=48 1=1 5=1 6=768\nHardSwish                Div_58                   1 1 315 323 0=1.666667e-01\nConvolutionDepthWise     Conv_59                  1 1 323 325 0=48 1=5 13=2 4=2 5=1 6=1200 7=48\nHardSwish                Div_68                   1 1 325 333 0=1.666667e-01\nSplit                    splitncnn_2              1 2 333 333_splitncnn_0 333_splitncnn_1\nPooling                  GlobalAveragePool_69     1 1 333_splitncnn_1 334 0=1 4=1\nInnerProduct             Conv_70                  1 1 334 336 0=12 1=1 2=576 9=1\nInnerProduct             Conv_72                  1 1 336 337 0=48 1=1 2=576\nBinaryOp                 Mul_74                   1 1 337 339 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_81                   1 1 339 346 0=1.666667e-01\nBinaryOp                 Mul_82                   2 1 333_splitncnn_0 346 347 0=2\nConvolution              Conv_83                  1 1 347 349 0=24 1=1 5=1 6=1152\nSplit                    splitncnn_3              1 2 349 349_splitncnn_0 349_splitncnn_1\nConvolution              Conv_85                  1 1 349_splitncnn_1 351 0=120 1=1 5=1 6=2880\nHardSwish                Div_94                   1 1 351 359 0=1.666667e-01\nConvolutionDepthWise     Conv_95                  1 1 359 361 0=120 1=5 4=2 5=1 6=3000 7=120\nHardSwish                Div_104                  1 1 361 369 0=1.666667e-01\nSplit                    splitncnn_4              1 2 369 369_splitncnn_0 369_splitncnn_1\nPooling                  GlobalAveragePool_105    1 1 369_splitncnn_1 370 0=1 4=1\nInnerProduct             Conv_106                 1 1 370 372 0=30 1=1 2=3600 9=1\nInnerProduct             Conv_108                 1 1 372 373 0=120 1=1 2=3600\nBinaryOp                 Mul_110                  1 1 373 375 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_117                  1 1 375 382 0=1.666667e-01\nBinaryOp                 Mul_118                  2 1 369_splitncnn_0 382 383 0=2\nConvolution              Conv_119                 1 1 383 385 0=24 1=1 5=1 6=2880\nBinaryOp                 Add_121                  2 1 349_splitncnn_0 385 386\nSplit                    splitncnn_5              1 2 386 386_splitncnn_0 386_splitncnn_1\nConvolution              Conv_122                 1 1 386_splitncnn_1 388 0=120 1=1 5=1 6=2880\nHardSwish                Div_131                  1 1 388 396 0=1.666667e-01\nConvolutionDepthWise     Conv_132                 1 1 396 398 0=120 1=5 4=2 5=1 6=3000 7=120\nHardSwish                Div_141                  1 1 398 406 0=1.666667e-01\nSplit                    splitncnn_6              1 2 406 406_splitncnn_0 406_splitncnn_1\nPooling                  GlobalAveragePool_142    1 1 406_splitncnn_1 407 0=1 4=1\nInnerProduct             Conv_143                 1 1 407 409 0=30 1=1 2=3600 9=1\nInnerProduct             Conv_145                 1 1 409 410 0=120 1=1 2=3600\nBinaryOp                 Mul_147                  1 1 410 412 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_154                  1 1 412 419 0=1.666667e-01\nBinaryOp                 Mul_155                  2 1 406_splitncnn_0 419 420 0=2\nConvolution              Conv_156                 1 1 420 422 0=24 1=1 5=1 6=2880\nBinaryOp                 Add_158                  2 1 386_splitncnn_0 422 423\nSplit                    splitncnn_7              1 2 423 423_splitncnn_0 423_splitncnn_1\nConvolution              Conv_159                 1 1 423_splitncnn_1 425 0=64 1=1 5=1 6=1536\nHardSwish                Div_168                  1 1 425 433 0=1.666667e-01\nConvolutionDepthWise     Conv_169                 1 1 433 435 0=64 1=5 4=2 5=1 6=1600 7=64\nHardSwish                Div_178                  1 1 435 443 0=1.666667e-01\nSplit                    splitncnn_8              1 2 443 443_splitncnn_0 443_splitncnn_1\nPooling                  GlobalAveragePool_179    1 1 443_splitncnn_1 444 0=1 4=1\nInnerProduct             Conv_180                 1 1 444 446 0=16 1=1 2=1024 9=1\nInnerProduct             Conv_182                 1 1 446 447 0=64 1=1 2=1024\nBinaryOp                 Mul_184                  1 1 447 449 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_191                  1 1 449 456 0=1.666667e-01\nBinaryOp                 Mul_192                  2 1 443_splitncnn_0 456 457 0=2\nConvolution              Conv_193                 1 1 457 459 0=24 1=1 5=1 6=1536\nBinaryOp                 Add_195                  2 1 423_splitncnn_0 459 460\nSplit                    splitncnn_9              1 2 460 460_splitncnn_0 460_splitncnn_1\nConvolution              Conv_196                 1 1 460_splitncnn_1 462 0=72 1=1 5=1 6=1728\nHardSwish                Div_205                  1 1 462 470 0=1.666667e-01\nConvolutionDepthWise     Conv_206                 1 1 470 472 0=72 1=5 4=2 5=1 6=1800 7=72\nHardSwish                Div_215                  1 1 472 480 0=1.666667e-01\nSplit                    splitncnn_10             1 2 480 480_splitncnn_0 480_splitncnn_1\nPooling                  GlobalAveragePool_216    1 1 480_splitncnn_1 481 0=1 4=1\nInnerProduct             Conv_217                 1 1 481 483 0=18 1=1 2=1296 9=1\nInnerProduct             Conv_219                 1 1 483 484 0=72 1=1 2=1296\nBinaryOp                 Mul_221                  1 1 484 486 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_228                  1 1 486 493 0=1.666667e-01\nBinaryOp                 Mul_229                  2 1 480_splitncnn_0 493 494 0=2\nConvolution              Conv_230                 1 1 494 496 0=24 1=1 5=1 6=1728\nBinaryOp                 Add_232                  2 1 460_splitncnn_0 496 497\nConvolution              Conv_233                 1 1 497 499 0=144 1=1 5=1 6=3456\nHardSwish                Div_242                  1 1 499 507 0=1.666667e-01\nConvolutionDepthWise     Conv_243                 1 1 507 509 0=144 1=5 13=2 4=2 5=1 6=3600 7=144\nHardSwish                Div_252                  1 1 509 517 0=1.666667e-01\nSplit                    splitncnn_11             1 2 517 517_splitncnn_0 517_splitncnn_1\nPooling                  GlobalAveragePool_253    1 1 517_splitncnn_1 518 0=1 4=1\nInnerProduct             Conv_254                 1 1 518 520 0=36 1=1 2=5184 9=1\nInnerProduct             Conv_256                 1 1 520 521 0=144 1=1 2=5184\nBinaryOp                 Mul_258                  1 1 521 523 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_265                  1 1 523 530 0=1.666667e-01\nBinaryOp                 Mul_266                  2 1 517_splitncnn_0 530 531 0=2\nConvolution              Conv_267                 1 1 531 533 0=48 1=1 5=1 6=6912\nSplit                    splitncnn_12             1 2 533 533_splitncnn_0 533_splitncnn_1\nConvolution              Conv_269                 1 1 533_splitncnn_1 535 0=288 1=1 5=1 6=13824\nHardSwish                Div_278                  1 1 535 543 0=1.666667e-01\nConvolutionDepthWise     Conv_279                 1 1 543 545 0=288 1=5 4=2 5=1 6=7200 7=288\nHardSwish                Div_288                  1 1 545 553 0=1.666667e-01\nSplit                    splitncnn_13             1 2 553 553_splitncnn_0 553_splitncnn_1\nPooling                  GlobalAveragePool_289    1 1 553_splitncnn_1 554 0=1 4=1\nInnerProduct             Conv_290                 1 1 554 556 0=72 1=1 2=20736 9=1\nInnerProduct             Conv_292                 1 1 556 557 0=288 1=1 2=20736\nBinaryOp                 Mul_294                  1 1 557 559 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_301                  1 1 559 566 0=1.666667e-01\nBinaryOp                 Mul_302                  2 1 553_splitncnn_0 566 567 0=2\nConvolution              Conv_303                 1 1 567 569 0=48 1=1 5=1 6=13824\nBinaryOp                 Add_305                  2 1 533_splitncnn_0 569 570\nSplit                    splitncnn_14             1 2 570 570_splitncnn_0 570_splitncnn_1\nConvolution              Conv_306                 1 1 570_splitncnn_1 572 0=288 1=1 5=1 6=13824\nHardSwish                Div_315                  1 1 572 580 0=1.666667e-01\nConvolutionDepthWise     Conv_316                 1 1 580 582 0=288 1=5 4=2 5=1 6=7200 7=288\nHardSwish                Div_325                  1 1 582 590 0=1.666667e-01\nSplit                    splitncnn_15             1 2 590 590_splitncnn_0 590_splitncnn_1\nPooling                  GlobalAveragePool_326    1 1 590_splitncnn_1 591 0=1 4=1\nInnerProduct             Conv_327                 1 1 591 593 0=72 1=1 2=20736 9=1\nInnerProduct             Conv_329                 1 1 593 594 0=288 1=1 2=20736\nBinaryOp                 Mul_331                  1 1 594 596 0=2 1=1 2=1.200000e+00\nHardSigmoid              Div_338                  1 1 596 603 0=1.666667e-01\nBinaryOp                 Mul_339                  2 1 590_splitncnn_0 603 604 0=2\nConvolution              Conv_340                 1 1 604 606 0=48 1=1 5=1 6=13824\nBinaryOp                 Add_342                  2 1 570_splitncnn_0 606 607\nConvolution              Conv_343                 1 1 607 609 0=288 1=1 5=1 6=13824\nHardSwish                Div_352                  1 1 609 617 0=1.666667e-01\nPooling                  MaxPool_353              1 1 617 618 1=2 2=2 5=1\nReshape                  Squeeze_354              1 1 618 619 0=-1 1=288\nPermute                  Transpose_362            1 1 619 631 0=1\nLSTM                     LSTM_371                 1 1 631 761 0=48 1=110592 2=2\nLSTM                     LSTM_383                 1 1 761 891 0=48 1=36864 2=2\nInnerProduct             MatMul_388               1 1 891 894 0=6625 1=1 2=636000\nSoftmax                  Softmax_390              1 1 894 out 0=1 1=1\n"
  },
  {
    "path": "app/src/main/java/com/tencent/paddleocrncnn/MainActivity.java",
    "content": "// Tencent is pleased to support the open source community by making ncnn available.\n//\n// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.\n//\n// Licensed under the BSD 3-Clause License (the \"License\"); you may not use this file except\n// in compliance with the License. You may obtain a copy of the License at\n//\n// https://opensource.org/licenses/BSD-3-Clause\n//\n// Unless required by applicable law or agreed to in writing, software distributed\n// under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR\n// CONDITIONS OF ANY KIND, either express or implied. See the License for the\n// specific language governing permissions and limitations under the License.\n\npackage com.tencent.paddleocrncnn;\n\nimport android.os.Build;\nimport android.Manifest;\nimport android.os.Environment;\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.media.ExifInterface;\nimport android.graphics.Matrix;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.File;\nimport android.support.v4.app.ActivityCompat;\nimport android.support.v4.content.ContextCompat;\nimport android.support.v4.content.FileProvider;\nimport android.provider.MediaStore;\nimport android.content.pm.PackageManager;\npublic class MainActivity extends Activity\n{\n    private static final int TAKE_PHOTO = 1;\n    private static final int SELECT_IMAGE = 2;\n\n    private ImageView imageView;\n    //private Bitmap bitmap = null;\n    private Bitmap yourSelectedImage = null;\n    private final String filePath = Environment.getExternalStorageDirectory() + File.separator + \"output_image.jpg\";\n    private Uri imageUri;\n    private PaddleOCRNcnn paddleocrncnn = new PaddleOCRNcnn();\n    @Override\n    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {\n        super.onRequestPermissionsResult(requestCode, permissions, grantResults);\n        if (grantResults != null && grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {\n            switch (requestCode) {\n                case 1: {\n                    requestCamera();\n                }\n                break;\n            }\n        }\n    }\n    /** Called when the activity is first created. */\n    @Override\n    public void onCreate(Bundle savedInstanceState)\n    {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.main);\n\n        boolean ret_init = paddleocrncnn.Init(getAssets());\n        if (!ret_init)\n        {\n            Log.e(\"MainActivity\", \"paddleocrncnn Init failed\");\n        }\n\n        imageView = (ImageView) findViewById(R.id.imageView);\n\n        Button buttonImage = (Button) findViewById(R.id.buttonImage);\n        buttonImage.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View arg0) {\n                Intent i = new Intent(Intent.ACTION_PICK);\n                i.setType(\"image/*\");\n                startActivityForResult(i, SELECT_IMAGE);\n            }\n        });\n        Button buttonCamera = (Button) findViewById(R.id.buttonCamera);\n        buttonCamera.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View arg0) {\n                requestPermission();\n            }\n        });\n        Button buttonDetect = (Button) findViewById(R.id.buttonDetect);\n        buttonDetect.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View arg0) {\n                if (yourSelectedImage == null)\n                    return;\n                Bitmap bitmap = yourSelectedImage.copy(Bitmap.Config.ARGB_8888, true);\n                PaddleOCRNcnn.Obj[] objects = paddleocrncnn.Detect(bitmap, false);\n\n                showObjects(objects);\n            }\n        });\n\n        Button buttonDetectGPU = (Button) findViewById(R.id.buttonDetectGPU);\n        buttonDetectGPU.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View arg0) {\n                if (yourSelectedImage == null)\n                    return;\n                Bitmap bitmap = yourSelectedImage.copy(Bitmap.Config.ARGB_8888, true);\n                PaddleOCRNcnn.Obj[] objects = paddleocrncnn.Detect(bitmap, true);\n\n                showObjects(objects);\n            }\n        });\n    }\n    private void requestPermission() {\n        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {\n            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, 1);\n        } else {\n            requestCamera();\n        }\n    }\n    private void requestCamera() {\n        File outputImage = new File(filePath);\n        try\n        {\n            if (!outputImage.getParentFile().exists()) {\n                outputImage.getParentFile().mkdirs();\n            }\n            if (outputImage.exists()) {\n                outputImage.delete();\n            }\n\n            outputImage.createNewFile();\n\n            if (Build.VERSION.SDK_INT >= 24) {\n                imageUri = FileProvider.getUriForFile(this,\n                        \"com.tencent.paddleocrncnn.fileprovider\", outputImage);\n            } else {\n                imageUri = Uri.fromFile(outputImage);\n            }\n            Intent intent = new Intent(\"android.media.action.IMAGE_CAPTURE\");\n            intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);\n            startActivityForResult(intent, TAKE_PHOTO);\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n\n    }\n    private void showObjects(PaddleOCRNcnn.Obj[] objects)\n    {\n        if (objects == null)\n        {\n            imageView.setImageBitmap(yourSelectedImage);\n            return;\n        }\n\n        // draw objects on bitmap\n        Bitmap rgba = yourSelectedImage.copy(Bitmap.Config.ARGB_8888, true);\n\n        final int[] colors = new int[] {\n            Color.rgb( 54,  67, 244),\n            Color.rgb( 99,  30, 233),\n            Color.rgb(176,  39, 156),\n            Color.rgb(183,  58, 103),\n            Color.rgb(181,  81,  63),\n            Color.rgb(243, 150,  33),\n            Color.rgb(244, 169,   3),\n            Color.rgb(212, 188,   0),\n            Color.rgb(136, 150,   0),\n            Color.rgb( 80, 175,  76),\n            Color.rgb( 74, 195, 139),\n            Color.rgb( 57, 220, 205),\n            Color.rgb( 59, 235, 255),\n            Color.rgb(  7, 193, 255),\n            Color.rgb(  0, 152, 255),\n            Color.rgb( 34,  87, 255),\n            Color.rgb( 72,  85, 121),\n            Color.rgb(158, 158, 158),\n            Color.rgb(139, 125,  96)\n        };\n\n        Canvas canvas = new Canvas(rgba);\n\n        Paint paint = new Paint();\n        paint.setStyle(Paint.Style.STROKE);\n        paint.setStrokeWidth(4);\n        Paint textbgpaint = new Paint();\n        textbgpaint.setColor(Color.WHITE);\n        textbgpaint.setStyle(Paint.Style.FILL);\n\n        Paint textpaint = new Paint();\n        textpaint.setColor(Color.BLACK);\n        textpaint.setTextSize(56);\n        textpaint.setTextAlign(Paint.Align.LEFT);\n\n        for (int i = 0; i < objects.length; i++)\n        {\n            paint.setColor(colors[i % 19]);\n\n            //canvas.drawRect(objects[i].x, objects[i].y, objects[i].x + objects[i].w, objects[i].y + objects[i].h, paint);\n            canvas.drawLine(objects[i].x0,objects[i].y0,objects[i].x1,objects[i].y1,paint);\n            canvas.drawLine(objects[i].x1,objects[i].y1,objects[i].x2,objects[i].y2,paint);\n            canvas.drawLine(objects[i].x2,objects[i].y2,objects[i].x3,objects[i].y3,paint);\n            canvas.drawLine(objects[i].x3,objects[i].y3,objects[i].x0,objects[i].y0,paint);\n            // draw filled text inside image\n            {\n                String text = objects[i].label;// + \" = \" + String.format(\"%.1f\", objects[i].prob * 100) + \"%\";\n\n                float text_width = textpaint.measureText(text);\n                float text_height = - textpaint.ascent() + textpaint.descent();\n\n                float x = objects[i].x0;\n                float y = objects[i].y0 - text_height;\n                if (y < 0)\n                    y = 0;\n                if (x + text_width > rgba.getWidth())\n                    x = rgba.getWidth() - text_width;\n\n                canvas.drawRect(x, y, x + text_width, y + text_height, textbgpaint);\n\n                canvas.drawText(text, x, y - textpaint.ascent(), textpaint);\n            }\n        }\n\n        imageView.setImageBitmap(rgba);\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n        switch (requestCode) {\n            case TAKE_PHOTO:\n                if (resultCode == RESULT_OK) {\n                    try {\n                        yourSelectedImage = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));\n                        imageView.setImageBitmap(yourSelectedImage);\n                    } catch (FileNotFoundException e) {\n                        e.printStackTrace();\n                        Log.e(\"MainActivity\", \"FileNotFoundException\");\n                    }\n                }\n                break;\n            case SELECT_IMAGE:\n                if (resultCode == RESULT_OK && null != data) {\n                    Uri selectedImage = data.getData();\n                    try {\n                        if (requestCode == SELECT_IMAGE) {\n                            yourSelectedImage = decodeUri(selectedImage);\n\n                            imageView.setImageBitmap(yourSelectedImage);\n                        }\n                    }\n                    catch (FileNotFoundException e) {\n                        Log.e(\"MainActivity\", \"FileNotFoundException\");\n                        return;\n                    }\n                }\n            default:\n                break;\n        }\n    }\n\n    private Bitmap decodeUri(Uri selectedImage) throws FileNotFoundException\n    {\n        // Decode image size\n        BitmapFactory.Options o = new BitmapFactory.Options();\n        o.inJustDecodeBounds = true;\n        BitmapFactory.decodeStream(getContentResolver().openInputStream(selectedImage), null, o);\n\n        // The new size we want to scale to\n        final int REQUIRED_SIZE = 640;\n\n        // Find the correct scale value. It should be the power of 2.\n        int width_tmp = o.outWidth, height_tmp = o.outHeight;\n        int scale = 1;\n        while (true) {\n            if (width_tmp / 2 < REQUIRED_SIZE\n               || height_tmp / 2 < REQUIRED_SIZE) {\n                break;\n            }\n            width_tmp /= 2;\n            height_tmp /= 2;\n            scale *= 2;\n        }\n\n        // Decode with inSampleSize\n        BitmapFactory.Options o2 = new BitmapFactory.Options();\n        o2.inSampleSize = scale;\n        Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(selectedImage), null, o2);\n\n        // Rotate according to EXIF\n        int rotate = 0;\n        try\n        {\n            ExifInterface exif = new ExifInterface(getContentResolver().openInputStream(selectedImage));\n            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);\n            switch (orientation) {\n                case ExifInterface.ORIENTATION_ROTATE_270:\n                    rotate = 270;\n                    break;\n                case ExifInterface.ORIENTATION_ROTATE_180:\n                    rotate = 180;\n                    break;\n                case ExifInterface.ORIENTATION_ROTATE_90:\n                    rotate = 90;\n                    break;\n            }\n        }\n        catch (IOException e)\n        {\n            Log.e(\"MainActivity\", \"ExifInterface IOException\");\n        }\n\n        Matrix matrix = new Matrix();\n        matrix.postRotate(rotate);\n        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/tencent/paddleocrncnn/PaddleOCRNcnn.java",
    "content": "// Tencent is pleased to support the open source community by making ncnn available.\n//\n// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.\n//\n// Licensed under the BSD 3-Clause License (the \"License\"); you may not use this file except\n// in compliance with the License. You may obtain a copy of the License at\n//\n// https://opensource.org/licenses/BSD-3-Clause\n//\n// Unless required by applicable law or agreed to in writing, software distributed\n// under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR\n// CONDITIONS OF ANY KIND, either express or implied. See the License for the\n// specific language governing permissions and limitations under the License.\n\npackage com.tencent.paddleocrncnn;\n\nimport android.content.res.AssetManager;\nimport android.graphics.Bitmap;\n\npublic class PaddleOCRNcnn\n{\n    public native boolean Init(AssetManager mgr);\n\n    public class Obj\n    {\n        public float x0;\n        public float y0;\n        public float x1;\n        public float y1;\n        public float x2;\n        public float y2;\n        public float x3;\n        public float y3;\n        public String label;\n        public float prob;\n    }\n\n    public native Obj[] Detect(Bitmap bitmap, boolean use_gpu);\n\n    static {\n        System.loadLibrary(\"paddleocrncnn\");\n    }\n}\n"
  },
  {
    "path": "app/src/main/jni/CMakeLists.txt",
    "content": "project(paddleocrncnn)\n\ncmake_minimum_required(VERSION 3.4.1)\n\nset(OpenCV_DIR ${CMAKE_SOURCE_DIR}/opencv-mobile-4.5.1-android/sdk/native/jni)\nfind_package(OpenCV REQUIRED core imgproc)\n\nset(ncnn_DIR ${CMAKE_SOURCE_DIR}/ncnn-20210720-android-vulkan/${ANDROID_ABI}/lib/cmake/ncnn)\nfind_package(ncnn REQUIRED)\n\nadd_library(paddleocrncnn SHARED paddleocr_ncnn.cpp common.cpp clipper.cpp)\n\ntarget_link_libraries(paddleocrncnn ncnn ${OpenCV_LIBS} jnigraphics)\n"
  },
  {
    "path": "app/src/main/jni/clipper.cpp",
    "content": "/*******************************************************************************\n*                                                                              *\n* Author    :  Angus Johnson                                                   *\n* Version   :  6.4.2                                                           *\n* Date      :  27 February 2017                                                *\n* Website   :  http://www.angusj.com                                           *\n* Copyright :  Angus Johnson 2010-2017                                         *\n*                                                                              *\n* License:                                                                     *\n* Use, modification & distribution is subject to Boost Software License Ver 1. *\n* http://www.boost.org/LICENSE_1_0.txt                                         *\n*                                                                              *\n* Attributions:                                                                *\n* The code in this library is an extension of Bala Vatti's clipping algorithm: *\n* \"A generic solution to polygon clipping\"                                     *\n* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63.             *\n* http://portal.acm.org/citation.cfm?id=129906                                 *\n*                                                                              *\n* Computer graphics and geometric modeling: implementation and algorithms      *\n* By Max K. Agoston                                                            *\n* Springer; 1 edition (January 4, 2005)                                        *\n* http://books.google.com/books?q=vatti+clipping+agoston                       *\n*                                                                              *\n* See also:                                                                    *\n* \"Polygon Offsetting by Computing Winding Numbers\"                            *\n* Paper no. DETC2005-85513 pp. 565-575                                         *\n* ASME 2005 International Design Engineering Technical Conferences             *\n* and Computers and Information in Engineering Conference (IDETC/CIE2005)      *\n* September 24-28, 2005 , Long Beach, California, USA                          *\n* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf              *\n*                                                                              *\n*******************************************************************************/\n\n/*******************************************************************************\n*                                                                              *\n* This is a translation of the Delphi Clipper library and the naming style     *\n* used has retained a Delphi flavour.                                          *\n*                                                                              *\n*******************************************************************************/\n\n#include \"clipper.hpp\"\n#include <cmath>\n#include <vector>\n#include <algorithm>\n#include <stdexcept>\n#include <cstring>\n#include <cstdlib>\n#include <ostream>\n#include <functional>\n\nnamespace ClipperLib {\n\n    static double const pi = 3.141592653589793238;\n    static double const two_pi = pi *2;\n    static double const def_arc_tolerance = 0.25;\n\n    enum Direction { dRightToLeft, dLeftToRight };\n\n    static int const Unassigned = -1;  //edge not currently 'owning' a solution\n    static int const Skip = -2;        //edge that would otherwise close a path\n\n#define HORIZONTAL (-1.0E+40)\n#define TOLERANCE (1.0e-20)\n#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE))\n\n    struct TEdge {\n        IntPoint Bot;\n        IntPoint Curr; //current (updated for every new scanbeam)\n        IntPoint Top;\n        double Dx;\n        PolyType PolyTyp;\n        EdgeSide Side; //side only refers to current side of solution poly\n        int WindDelta; //1 or -1 depending on winding direction\n        int WindCnt;\n        int WindCnt2; //winding count of the opposite polytype\n        int OutIdx;\n        TEdge *Next;\n        TEdge *Prev;\n        TEdge *NextInLML;\n        TEdge *NextInAEL;\n        TEdge *PrevInAEL;\n        TEdge *NextInSEL;\n        TEdge *PrevInSEL;\n    };\n\n    struct IntersectNode {\n        TEdge          *Edge1;\n        TEdge          *Edge2;\n        IntPoint        Pt;\n    };\n\n    struct LocalMinimum {\n        cInt          Y;\n        TEdge        *LeftBound;\n        TEdge        *RightBound;\n    };\n\n    struct OutPt;\n\n//OutRec: contains a path in the clipping solution. Edges in the AEL will\n//carry a pointer to an OutRec when they are part of the clipping solution.\n    struct OutRec {\n        int       Idx;\n        bool      IsHole;\n        bool      IsOpen;\n        OutRec   *FirstLeft;  //see comments in clipper.pas\n        PolyNode *PolyNd;\n        OutPt    *Pts;\n        OutPt    *BottomPt;\n    };\n\n    struct OutPt {\n        int       Idx;\n        IntPoint  Pt;\n        OutPt    *Next;\n        OutPt    *Prev;\n    };\n\n    struct Join {\n        OutPt    *OutPt1;\n        OutPt    *OutPt2;\n        IntPoint  OffPt;\n    };\n\n    struct LocMinSorter\n    {\n        inline bool operator()(const LocalMinimum& locMin1, const LocalMinimum& locMin2)\n        {\n            return locMin2.Y < locMin1.Y;\n        }\n    };\n\n//------------------------------------------------------------------------------\n//------------------------------------------------------------------------------\n\n    inline cInt Round(double val)\n    {\n        if ((val < 0)) return static_cast<cInt>(val - 0.5);\n        else return static_cast<cInt>(val + 0.5);\n    }\n//------------------------------------------------------------------------------\n\n    inline cInt Abs(cInt val)\n    {\n        return val < 0 ? -val : val;\n    }\n\n//------------------------------------------------------------------------------\n// PolyTree methods ...\n//------------------------------------------------------------------------------\n\n    void PolyTree::Clear()\n    {\n        for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i)\n            delete AllNodes[i];\n        AllNodes.resize(0);\n        Childs.resize(0);\n    }\n//------------------------------------------------------------------------------\n\n    PolyNode* PolyTree::GetFirst() const\n    {\n        if (!Childs.empty())\n            return Childs[0];\n        else\n            return 0;\n    }\n//------------------------------------------------------------------------------\n\n    int PolyTree::Total() const\n    {\n        int result = (int)AllNodes.size();\n        //with negative offsets, ignore the hidden outer polygon ...\n        if (result > 0 && Childs[0] != AllNodes[0]) result--;\n        return result;\n    }\n\n//------------------------------------------------------------------------------\n// PolyNode methods ...\n//------------------------------------------------------------------------------\n\n    PolyNode::PolyNode(): Parent(0), Index(0), m_IsOpen(false)\n    {\n    }\n//------------------------------------------------------------------------------\n\n    int PolyNode::ChildCount() const\n    {\n        return (int)Childs.size();\n    }\n//------------------------------------------------------------------------------\n\n    void PolyNode::AddChild(PolyNode& child)\n    {\n        unsigned cnt = (unsigned)Childs.size();\n        Childs.push_back(&child);\n        child.Parent = this;\n        child.Index = cnt;\n    }\n//------------------------------------------------------------------------------\n\n    PolyNode* PolyNode::GetNext() const\n    {\n        if (!Childs.empty())\n            return Childs[0];\n        else\n            return GetNextSiblingUp();\n    }\n//------------------------------------------------------------------------------\n\n    PolyNode* PolyNode::GetNextSiblingUp() const\n    {\n        if (!Parent) //protects against PolyTree.GetNextSiblingUp()\n            return 0;\n        else if (Index == Parent->Childs.size() - 1)\n            return Parent->GetNextSiblingUp();\n        else\n            return Parent->Childs[Index + 1];\n    }\n//------------------------------------------------------------------------------\n\n    bool PolyNode::IsHole() const\n    {\n        bool result = true;\n        PolyNode* node = Parent;\n        while (node)\n        {\n            result = !result;\n            node = node->Parent;\n        }\n        return result;\n    }\n//------------------------------------------------------------------------------\n\n    bool PolyNode::IsOpen() const\n    {\n        return m_IsOpen;\n    }\n//------------------------------------------------------------------------------\n\n#ifndef use_int32\n\n//------------------------------------------------------------------------------\n// Int128 class (enables safe math on signed 64bit integers)\n// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1\n//    Int128 val2((long64)9223372036854775807);\n//    Int128 val3 = val1 * val2;\n//    val3.AsString => \"85070591730234615847396907784232501249\" (8.5e+37)\n//------------------------------------------------------------------------------\n\n    class Int128\n    {\n    public:\n        ulong64 lo;\n        long64 hi;\n\n        Int128(long64 _lo = 0)\n        {\n            lo = (ulong64)_lo;\n            if (_lo < 0)  hi = -1; else hi = 0;\n        }\n\n\n        Int128(const Int128 &val): lo(val.lo), hi(val.hi){}\n\n        Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){}\n\n        Int128& operator = (const long64 &val)\n        {\n            lo = (ulong64)val;\n            if (val < 0) hi = -1; else hi = 0;\n            return *this;\n        }\n\n        bool operator == (const Int128 &val) const\n        {return (hi == val.hi && lo == val.lo);}\n\n        bool operator != (const Int128 &val) const\n        { return !(*this == val);}\n\n        bool operator > (const Int128 &val) const\n        {\n            if (hi != val.hi)\n                return hi > val.hi;\n            else\n                return lo > val.lo;\n        }\n\n        bool operator < (const Int128 &val) const\n        {\n            if (hi != val.hi)\n                return hi < val.hi;\n            else\n                return lo < val.lo;\n        }\n\n        bool operator >= (const Int128 &val) const\n        { return !(*this < val);}\n\n        bool operator <= (const Int128 &val) const\n        { return !(*this > val);}\n\n        Int128& operator += (const Int128 &rhs)\n        {\n            hi += rhs.hi;\n            lo += rhs.lo;\n            if (lo < rhs.lo) hi++;\n            return *this;\n        }\n\n        Int128 operator + (const Int128 &rhs) const\n        {\n            Int128 result(*this);\n            result+= rhs;\n            return result;\n        }\n\n        Int128& operator -= (const Int128 &rhs)\n        {\n            *this += -rhs;\n            return *this;\n        }\n\n        Int128 operator - (const Int128 &rhs) const\n        {\n            Int128 result(*this);\n            result -= rhs;\n            return result;\n        }\n\n        Int128 operator-() const //unary negation\n        {\n            if (lo == 0)\n                return Int128(-hi, 0);\n            else\n                return Int128(~hi, ~lo + 1);\n        }\n\n        operator double() const\n        {\n            const double shift64 = 18446744073709551616.0; //2^64\n            if (hi < 0)\n            {\n                if (lo == 0) return (double)hi * shift64;\n                else return -(double)(~lo + ~hi * shift64);\n            }\n            else\n                return (double)(lo + hi * shift64);\n        }\n\n    };\n//------------------------------------------------------------------------------\n\n    Int128 Int128Mul (long64 lhs, long64 rhs)\n    {\n        bool negate = (lhs < 0) != (rhs < 0);\n\n        if (lhs < 0) lhs = -lhs;\n        ulong64 int1Hi = ulong64(lhs) >> 32;\n        ulong64 int1Lo = ulong64(lhs & 0xFFFFFFFF);\n\n        if (rhs < 0) rhs = -rhs;\n        ulong64 int2Hi = ulong64(rhs) >> 32;\n        ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF);\n\n        //nb: see comments in clipper.pas\n        ulong64 a = int1Hi * int2Hi;\n        ulong64 b = int1Lo * int2Lo;\n        ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi;\n\n        Int128 tmp;\n        tmp.hi = long64(a + (c >> 32));\n        tmp.lo = long64(c << 32);\n        tmp.lo += long64(b);\n        if (tmp.lo < b) tmp.hi++;\n        if (negate) tmp = -tmp;\n        return tmp;\n    };\n#endif\n\n//------------------------------------------------------------------------------\n// Miscellaneous global functions\n//------------------------------------------------------------------------------\n\n    bool Orientation(const Path &poly)\n    {\n        return Area(poly) >= 0;\n    }\n//------------------------------------------------------------------------------\n\n    double Area(const Path &poly)\n    {\n        int size = (int)poly.size();\n        if (size < 3) return 0;\n\n        double a = 0;\n        for (int i = 0, j = size -1; i < size; ++i)\n        {\n            a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y);\n            j = i;\n        }\n        return -a * 0.5;\n    }\n//------------------------------------------------------------------------------\n\n    double Area(const OutPt *op)\n    {\n        const OutPt *startOp = op;\n        if (!op) return 0;\n        double a = 0;\n        do {\n            a +=  (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y);\n            op = op->Next;\n        } while (op != startOp);\n        return a * 0.5;\n    }\n//------------------------------------------------------------------------------\n\n    double Area(const OutRec &outRec)\n    {\n        return Area(outRec.Pts);\n    }\n//------------------------------------------------------------------------------\n\n    bool PointIsVertex(const IntPoint &Pt, OutPt *pp)\n    {\n        OutPt *pp2 = pp;\n        do\n        {\n            if (pp2->Pt == Pt) return true;\n            pp2 = pp2->Next;\n        }\n        while (pp2 != pp);\n        return false;\n    }\n//------------------------------------------------------------------------------\n\n//See \"The Point in Polygon Problem for Arbitrary Polygons\" by Hormann & Agathos\n//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf\n    int PointInPolygon(const IntPoint &pt, const Path &path)\n    {\n        //returns 0 if false, +1 if true, -1 if pt ON polygon boundary\n        int result = 0;\n        size_t cnt = path.size();\n        if (cnt < 3) return 0;\n        IntPoint ip = path[0];\n        for(size_t i = 1; i <= cnt; ++i)\n        {\n            IntPoint ipNext = (i == cnt ? path[0] : path[i]);\n            if (ipNext.Y == pt.Y)\n            {\n                if ((ipNext.X == pt.X) || (ip.Y == pt.Y &&\n                                           ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1;\n            }\n            if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y))\n            {\n                if (ip.X >= pt.X)\n                {\n                    if (ipNext.X > pt.X) result = 1 - result;\n                    else\n                    {\n                        double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) -\n                                   (double)(ipNext.X - pt.X) * (ip.Y - pt.Y);\n                        if (!d) return -1;\n                        if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result;\n                    }\n                } else\n                {\n                    if (ipNext.X > pt.X)\n                    {\n                        double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) -\n                                   (double)(ipNext.X - pt.X) * (ip.Y - pt.Y);\n                        if (!d) return -1;\n                        if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result;\n                    }\n                }\n            }\n            ip = ipNext;\n        }\n        return result;\n    }\n//------------------------------------------------------------------------------\n\n    int PointInPolygon (const IntPoint &pt, OutPt *op)\n    {\n        //returns 0 if false, +1 if true, -1 if pt ON polygon boundary\n        int result = 0;\n        OutPt* startOp = op;\n        for(;;)\n        {\n            if (op->Next->Pt.Y == pt.Y)\n            {\n                if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y &&\n                                                 ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1;\n            }\n            if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y))\n            {\n                if (op->Pt.X >= pt.X)\n                {\n                    if (op->Next->Pt.X > pt.X) result = 1 - result;\n                    else\n                    {\n                        double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) -\n                                   (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y);\n                        if (!d) return -1;\n                        if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result;\n                    }\n                } else\n                {\n                    if (op->Next->Pt.X > pt.X)\n                    {\n                        double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) -\n                                   (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y);\n                        if (!d) return -1;\n                        if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result;\n                    }\n                }\n            }\n            op = op->Next;\n            if (startOp == op) break;\n        }\n        return result;\n    }\n//------------------------------------------------------------------------------\n\n    bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2)\n    {\n        OutPt* op = OutPt1;\n        do\n        {\n            //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon\n            int res = PointInPolygon(op->Pt, OutPt2);\n            if (res >= 0) return res > 0;\n            op = op->Next;\n        }\n        while (op != OutPt1);\n        return true;\n    }\n//----------------------------------------------------------------------\n\n    bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range)\n    {\n#ifndef use_int32\n        if (UseFullInt64Range)\n            return Int128Mul(e1.Top.Y - e1.Bot.Y, e2.Top.X - e2.Bot.X) ==\n                   Int128Mul(e1.Top.X - e1.Bot.X, e2.Top.Y - e2.Bot.Y);\n        else\n#endif\n            return (e1.Top.Y - e1.Bot.Y) * (e2.Top.X - e2.Bot.X) ==\n                   (e1.Top.X - e1.Bot.X) * (e2.Top.Y - e2.Bot.Y);\n    }\n//------------------------------------------------------------------------------\n\n    bool SlopesEqual(const IntPoint pt1, const IntPoint pt2,\n                     const IntPoint pt3, bool UseFullInt64Range)\n    {\n#ifndef use_int32\n        if (UseFullInt64Range)\n            return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y);\n        else\n#endif\n            return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y);\n    }\n//------------------------------------------------------------------------------\n\n    bool SlopesEqual(const IntPoint pt1, const IntPoint pt2,\n                     const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range)\n    {\n#ifndef use_int32\n        if (UseFullInt64Range)\n            return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y);\n        else\n#endif\n            return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y);\n    }\n//------------------------------------------------------------------------------\n\n    inline bool IsHorizontal(TEdge &e)\n    {\n        return e.Dx == HORIZONTAL;\n    }\n//------------------------------------------------------------------------------\n\n    inline double GetDx(const IntPoint pt1, const IntPoint pt2)\n    {\n        return (pt1.Y == pt2.Y) ?\n               HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y);\n    }\n//---------------------------------------------------------------------------\n\n    inline void SetDx(TEdge &e)\n    {\n        cInt dy  = (e.Top.Y - e.Bot.Y);\n        if (dy == 0) e.Dx = HORIZONTAL;\n        else e.Dx = (double)(e.Top.X - e.Bot.X) / dy;\n    }\n//---------------------------------------------------------------------------\n\n    inline void SwapSides(TEdge &Edge1, TEdge &Edge2)\n    {\n        EdgeSide Side =  Edge1.Side;\n        Edge1.Side = Edge2.Side;\n        Edge2.Side = Side;\n    }\n//------------------------------------------------------------------------------\n\n    inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2)\n    {\n        int OutIdx =  Edge1.OutIdx;\n        Edge1.OutIdx = Edge2.OutIdx;\n        Edge2.OutIdx = OutIdx;\n    }\n//------------------------------------------------------------------------------\n\n    inline cInt TopX(TEdge &edge, const cInt currentY)\n    {\n        return ( currentY == edge.Top.Y ) ?\n               edge.Top.X : edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y));\n    }\n//------------------------------------------------------------------------------\n\n    void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip)\n    {\n#ifdef use_xyz\n        ip.Z = 0;\n#endif\n\n        double b1, b2;\n        if (Edge1.Dx == Edge2.Dx)\n        {\n            ip.Y = Edge1.Curr.Y;\n            ip.X = TopX(Edge1, ip.Y);\n            return;\n        }\n        else if (Edge1.Dx == 0)\n        {\n            ip.X = Edge1.Bot.X;\n            if (IsHorizontal(Edge2))\n                ip.Y = Edge2.Bot.Y;\n            else\n            {\n                b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx);\n                ip.Y = Round(ip.X / Edge2.Dx + b2);\n            }\n        }\n        else if (Edge2.Dx == 0)\n        {\n            ip.X = Edge2.Bot.X;\n            if (IsHorizontal(Edge1))\n                ip.Y = Edge1.Bot.Y;\n            else\n            {\n                b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx);\n                ip.Y = Round(ip.X / Edge1.Dx + b1);\n            }\n        }\n        else\n        {\n            b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx;\n            b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx;\n            double q = (b2-b1) / (Edge1.Dx - Edge2.Dx);\n            ip.Y = Round(q);\n            if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx))\n                ip.X = Round(Edge1.Dx * q + b1);\n            else\n                ip.X = Round(Edge2.Dx * q + b2);\n        }\n\n        if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y)\n        {\n            if (Edge1.Top.Y > Edge2.Top.Y)\n                ip.Y = Edge1.Top.Y;\n            else\n                ip.Y = Edge2.Top.Y;\n            if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx))\n                ip.X = TopX(Edge1, ip.Y);\n            else\n                ip.X = TopX(Edge2, ip.Y);\n        }\n        //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ...\n        if (ip.Y > Edge1.Curr.Y)\n        {\n            ip.Y = Edge1.Curr.Y;\n            //use the more vertical edge to derive X ...\n            if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx))\n                ip.X = TopX(Edge2, ip.Y); else\n                ip.X = TopX(Edge1, ip.Y);\n        }\n    }\n//------------------------------------------------------------------------------\n\n    void ReversePolyPtLinks(OutPt *pp)\n    {\n        if (!pp) return;\n        OutPt *pp1, *pp2;\n        pp1 = pp;\n        do {\n            pp2 = pp1->Next;\n            pp1->Next = pp1->Prev;\n            pp1->Prev = pp2;\n            pp1 = pp2;\n        } while( pp1 != pp );\n    }\n//------------------------------------------------------------------------------\n\n    void DisposeOutPts(OutPt*& pp)\n    {\n        if (pp == 0) return;\n        pp->Prev->Next = 0;\n        while( pp )\n        {\n            OutPt *tmpPp = pp;\n            pp = pp->Next;\n            delete tmpPp;\n        }\n    }\n//------------------------------------------------------------------------------\n\n    inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt)\n    {\n        std::memset(e, 0, sizeof(TEdge));\n        e->Next = eNext;\n        e->Prev = ePrev;\n        e->Curr = Pt;\n        e->OutIdx = Unassigned;\n    }\n//------------------------------------------------------------------------------\n\n    void InitEdge2(TEdge& e, PolyType Pt)\n    {\n        if (e.Curr.Y >= e.Next->Curr.Y)\n        {\n            e.Bot = e.Curr;\n            e.Top = e.Next->Curr;\n        } else\n        {\n            e.Top = e.Curr;\n            e.Bot = e.Next->Curr;\n        }\n        SetDx(e);\n        e.PolyTyp = Pt;\n    }\n//------------------------------------------------------------------------------\n\n    TEdge* RemoveEdge(TEdge* e)\n    {\n        //removes e from double_linked_list (but without removing from memory)\n        e->Prev->Next = e->Next;\n        e->Next->Prev = e->Prev;\n        TEdge* result = e->Next;\n        e->Prev = 0; //flag as removed (see ClipperBase.Clear)\n        return result;\n    }\n//------------------------------------------------------------------------------\n\n    inline void ReverseHorizontal(TEdge &e)\n    {\n        //swap horizontal edges' Top and Bottom x's so they follow the natural\n        //progression of the bounds - ie so their xbots will align with the\n        //adjoining lower edge. [Helpful in the ProcessHorizontal() method.]\n        std::swap(e.Top.X, e.Bot.X);\n#ifdef use_xyz\n        std::swap(e.Top.Z, e.Bot.Z);\n#endif\n    }\n//------------------------------------------------------------------------------\n\n    void SwapPoints(IntPoint &pt1, IntPoint &pt2)\n    {\n        IntPoint tmp = pt1;\n        pt1 = pt2;\n        pt2 = tmp;\n    }\n//------------------------------------------------------------------------------\n\n    bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a,\n                           IntPoint pt2b, IntPoint &pt1, IntPoint &pt2)\n    {\n        //precondition: segments are Collinear.\n        if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y))\n        {\n            if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b);\n            if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b);\n            if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a;\n            if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b;\n            return pt1.X < pt2.X;\n        } else\n        {\n            if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b);\n            if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b);\n            if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a;\n            if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b;\n            return pt1.Y > pt2.Y;\n        }\n    }\n//------------------------------------------------------------------------------\n\n    bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2)\n    {\n        OutPt *p = btmPt1->Prev;\n        while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev;\n        double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt));\n        p = btmPt1->Next;\n        while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next;\n        double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt));\n\n        p = btmPt2->Prev;\n        while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev;\n        double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt));\n        p = btmPt2->Next;\n        while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next;\n        double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt));\n\n        if (std::max(dx1p, dx1n) == std::max(dx2p, dx2n) &&\n            std::min(dx1p, dx1n) == std::min(dx2p, dx2n))\n            return Area(btmPt1) > 0; //if otherwise identical use orientation\n        else\n            return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n);\n    }\n//------------------------------------------------------------------------------\n\n    OutPt* GetBottomPt(OutPt *pp)\n    {\n        OutPt* dups = 0;\n        OutPt* p = pp->Next;\n        while (p != pp)\n        {\n            if (p->Pt.Y > pp->Pt.Y)\n            {\n                pp = p;\n                dups = 0;\n            }\n            else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X)\n            {\n                if (p->Pt.X < pp->Pt.X)\n                {\n                    dups = 0;\n                    pp = p;\n                } else\n                {\n                    if (p->Next != pp && p->Prev != pp) dups = p;\n                }\n            }\n            p = p->Next;\n        }\n        if (dups)\n        {\n            //there appears to be at least 2 vertices at BottomPt so ...\n            while (dups != p)\n            {\n                if (!FirstIsBottomPt(p, dups)) pp = dups;\n                dups = dups->Next;\n                while (dups->Pt != pp->Pt) dups = dups->Next;\n            }\n        }\n        return pp;\n    }\n//------------------------------------------------------------------------------\n\n    bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1,\n                               const IntPoint pt2, const IntPoint pt3)\n    {\n        if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2))\n            return false;\n        else if (pt1.X != pt3.X)\n            return (pt2.X > pt1.X) == (pt2.X < pt3.X);\n        else\n            return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y);\n    }\n//------------------------------------------------------------------------------\n\n    bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b)\n    {\n        if (seg1a > seg1b) std::swap(seg1a, seg1b);\n        if (seg2a > seg2b) std::swap(seg2a, seg2b);\n        return (seg1a < seg2b) && (seg2a < seg1b);\n    }\n\n//------------------------------------------------------------------------------\n// ClipperBase class methods ...\n//------------------------------------------------------------------------------\n\n    ClipperBase::ClipperBase() //constructor\n    {\n        m_CurrentLM = m_MinimaList.begin(); //begin() == end() here\n        m_UseFullRange = false;\n    }\n//------------------------------------------------------------------------------\n\n    ClipperBase::~ClipperBase() //destructor\n    {\n        Clear();\n    }\n//------------------------------------------------------------------------------\n\n    void RangeTest(const IntPoint& Pt, bool& useFullRange)\n    {\n        if (useFullRange)\n        {\n            if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange)\n                return;//throw clipperException(\"Coordinate outside allowed range\");\n        }\n        else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange)\n        {\n            useFullRange = true;\n            RangeTest(Pt, useFullRange);\n        }\n    }\n//------------------------------------------------------------------------------\n\n    TEdge* FindNextLocMin(TEdge* E)\n    {\n        for (;;)\n        {\n            while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next;\n            if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break;\n            while (IsHorizontal(*E->Prev)) E = E->Prev;\n            TEdge* E2 = E;\n            while (IsHorizontal(*E)) E = E->Next;\n            if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz.\n            if (E2->Prev->Bot.X < E->Bot.X) E = E2;\n            break;\n        }\n        return E;\n    }\n//------------------------------------------------------------------------------\n\n    TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward)\n    {\n        TEdge *Result = E;\n        TEdge *Horz = 0;\n\n        if (E->OutIdx == Skip)\n        {\n            //if edges still remain in the current bound beyond the skip edge then\n            //create another LocMin and call ProcessBound once more\n            if (NextIsForward)\n            {\n                while (E->Top.Y == E->Next->Bot.Y) E = E->Next;\n                //don't include top horizontals when parsing a bound a second time,\n                //they will be contained in the opposite bound ...\n                while (E != Result && IsHorizontal(*E)) E = E->Prev;\n            }\n            else\n            {\n                while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev;\n                while (E != Result && IsHorizontal(*E)) E = E->Next;\n            }\n\n            if (E == Result)\n            {\n                if (NextIsForward) Result = E->Next;\n                else Result = E->Prev;\n            }\n            else\n            {\n                //there are more edges in the bound beyond result starting with E\n                if (NextIsForward)\n                    E = Result->Next;\n                else\n                    E = Result->Prev;\n                MinimaList::value_type locMin;\n                locMin.Y = E->Bot.Y;\n                locMin.LeftBound = 0;\n                locMin.RightBound = E;\n                E->WindDelta = 0;\n                Result = ProcessBound(E, NextIsForward);\n                m_MinimaList.push_back(locMin);\n            }\n            return Result;\n        }\n\n        TEdge *EStart;\n\n        if (IsHorizontal(*E))\n        {\n            //We need to be careful with open paths because this may not be a\n            //true local minima (ie E may be following a skip edge).\n            //Also, consecutive horz. edges may start heading left before going right.\n            if (NextIsForward)\n                EStart = E->Prev;\n            else\n                EStart = E->Next;\n            if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge\n            {\n                if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X)\n                    ReverseHorizontal(*E);\n            }\n            else if (EStart->Bot.X != E->Bot.X)\n                ReverseHorizontal(*E);\n        }\n\n        EStart = E;\n        if (NextIsForward)\n        {\n            while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip)\n                Result = Result->Next;\n            if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip)\n            {\n                //nb: at the top of a bound, horizontals are added to the bound\n                //only when the preceding edge attaches to the horizontal's left vertex\n                //unless a Skip edge is encountered when that becomes the top divide\n                Horz = Result;\n                while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev;\n                if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev;\n            }\n            while (E != Result)\n            {\n                E->NextInLML = E->Next;\n                if (IsHorizontal(*E) && E != EStart &&\n                    E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E);\n                E = E->Next;\n            }\n            if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X)\n                ReverseHorizontal(*E);\n            Result = Result->Next; //move to the edge just beyond current bound\n        } else\n        {\n            while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip)\n                Result = Result->Prev;\n            if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip)\n            {\n                Horz = Result;\n                while (IsHorizontal(*Horz->Next)) Horz = Horz->Next;\n                if (Horz->Next->Top.X == Result->Prev->Top.X ||\n                    Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next;\n            }\n\n            while (E != Result)\n            {\n                E->NextInLML = E->Prev;\n                if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X)\n                    ReverseHorizontal(*E);\n                E = E->Prev;\n            }\n            if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X)\n                ReverseHorizontal(*E);\n            Result = Result->Prev; //move to the edge just beyond current bound\n        }\n\n        return Result;\n    }\n//------------------------------------------------------------------------------\n\n    bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)\n    {\n#ifdef use_lines\n        if (!Closed && PolyTyp == ptClip)\n            return false;//throw clipperException(\"AddPath: Open paths must be subject.\");\n#else\n        if (!Closed)\n    throw clipperException(\"AddPath: Open paths have been disabled.\");\n#endif\n\n        int highI = (int)pg.size() -1;\n        if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI;\n        while (highI > 0 && (pg[highI] == pg[highI -1])) --highI;\n        if ((Closed && highI < 2) || (!Closed && highI < 1)) return false;\n\n        //create a new edge array ...\n        TEdge *edges = new TEdge [highI +1];\n\n        bool IsFlat = true;\n        //1. Basic (first) edge initialization ...\n        //try\n        {\n            edges[1].Curr = pg[1];\n            RangeTest(pg[0], m_UseFullRange);\n            RangeTest(pg[highI], m_UseFullRange);\n            InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]);\n            InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]);\n            for (int i = highI - 1; i >= 1; --i)\n            {\n                RangeTest(pg[i], m_UseFullRange);\n                InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]);\n            }\n        }\n        //catch(...)\n        //{\n        //    delete [] edges;\n        //    return false;//throw; //range test fails\n        //}\n        TEdge *eStart = &edges[0];\n\n        //2. Remove duplicate vertices, and (when closed) collinear edges ...\n        TEdge *E = eStart, *eLoopStop = eStart;\n        for (;;)\n        {\n            //nb: allows matching start and end points when not Closed ...\n            if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart))\n            {\n                if (E == E->Next) break;\n                if (E == eStart) eStart = E->Next;\n                E = RemoveEdge(E);\n                eLoopStop = E;\n                continue;\n            }\n            if (E->Prev == E->Next)\n                break; //only two vertices\n            else if (Closed &&\n                     SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) &&\n                     (!m_PreserveCollinear ||\n                      !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr)))\n            {\n                //Collinear edges are allowed for open paths but in closed paths\n                //the default is to merge adjacent collinear edges into a single edge.\n                //However, if the PreserveCollinear property is enabled, only overlapping\n                //collinear edges (ie spikes) will be removed from closed paths.\n                if (E == eStart) eStart = E->Next;\n                E = RemoveEdge(E);\n                E = E->Prev;\n                eLoopStop = E;\n                continue;\n            }\n            E = E->Next;\n            if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break;\n        }\n\n        if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next)))\n        {\n            delete [] edges;\n            return false;\n        }\n\n        if (!Closed)\n        {\n            m_HasOpenPaths = true;\n            eStart->Prev->OutIdx = Skip;\n        }\n\n        //3. Do second stage of edge initialization ...\n        E = eStart;\n        do\n        {\n            InitEdge2(*E, PolyTyp);\n            E = E->Next;\n            if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false;\n        }\n        while (E != eStart);\n\n        //4. Finally, add edge bounds to LocalMinima list ...\n\n        //Totally flat paths must be handled differently when adding them\n        //to LocalMinima list to avoid endless loops etc ...\n        if (IsFlat)\n        {\n            if (Closed)\n            {\n                delete [] edges;\n                return false;\n            }\n            E->Prev->OutIdx = Skip;\n            MinimaList::value_type locMin;\n            locMin.Y = E->Bot.Y;\n            locMin.LeftBound = 0;\n            locMin.RightBound = E;\n            locMin.RightBound->Side = esRight;\n            locMin.RightBound->WindDelta = 0;\n            for (;;)\n            {\n                if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E);\n                if (E->Next->OutIdx == Skip) break;\n                E->NextInLML = E->Next;\n                E = E->Next;\n            }\n            m_MinimaList.push_back(locMin);\n            m_edges.push_back(edges);\n            return true;\n        }\n\n        m_edges.push_back(edges);\n        bool leftBoundIsForward;\n        TEdge* EMin = 0;\n\n        //workaround to avoid an endless loop in the while loop below when\n        //open paths have matching start and end points ...\n        if (E->Prev->Bot == E->Prev->Top) E = E->Next;\n\n        for (;;)\n        {\n            E = FindNextLocMin(E);\n            if (E == EMin) break;\n            else if (!EMin) EMin = E;\n\n            //E and E.Prev now share a local minima (left aligned if horizontal).\n            //Compare their slopes to find which starts which bound ...\n            MinimaList::value_type locMin;\n            locMin.Y = E->Bot.Y;\n            if (E->Dx < E->Prev->Dx)\n            {\n                locMin.LeftBound = E->Prev;\n                locMin.RightBound = E;\n                leftBoundIsForward = false; //Q.nextInLML = Q.prev\n            } else\n            {\n                locMin.LeftBound = E;\n                locMin.RightBound = E->Prev;\n                leftBoundIsForward = true; //Q.nextInLML = Q.next\n            }\n\n            if (!Closed) locMin.LeftBound->WindDelta = 0;\n            else if (locMin.LeftBound->Next == locMin.RightBound)\n                locMin.LeftBound->WindDelta = -1;\n            else locMin.LeftBound->WindDelta = 1;\n            locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta;\n\n            E = ProcessBound(locMin.LeftBound, leftBoundIsForward);\n            if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward);\n\n            TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward);\n            if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward);\n\n            if (locMin.LeftBound->OutIdx == Skip)\n                locMin.LeftBound = 0;\n            else if (locMin.RightBound->OutIdx == Skip)\n                locMin.RightBound = 0;\n            m_MinimaList.push_back(locMin);\n            if (!leftBoundIsForward) E = E2;\n        }\n        return true;\n    }\n//------------------------------------------------------------------------------\n\n    bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed)\n    {\n        bool result = false;\n        for (Paths::size_type i = 0; i < ppg.size(); ++i)\n            if (AddPath(ppg[i], PolyTyp, Closed)) result = true;\n        return result;\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperBase::Clear()\n    {\n        DisposeLocalMinimaList();\n        for (EdgeList::size_type i = 0; i < m_edges.size(); ++i)\n        {\n            TEdge* edges = m_edges[i];\n            delete [] edges;\n        }\n        m_edges.clear();\n        m_UseFullRange = false;\n        m_HasOpenPaths = false;\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperBase::Reset()\n    {\n        m_CurrentLM = m_MinimaList.begin();\n        if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process\n        std::sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter());\n\n        m_Scanbeam = ScanbeamList(); //clears/resets priority_queue\n        //reset all edges ...\n        for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm)\n        {\n            InsertScanbeam(lm->Y);\n            TEdge* e = lm->LeftBound;\n            if (e)\n            {\n                e->Curr = e->Bot;\n                e->Side = esLeft;\n                e->OutIdx = Unassigned;\n            }\n\n            e = lm->RightBound;\n            if (e)\n            {\n                e->Curr = e->Bot;\n                e->Side = esRight;\n                e->OutIdx = Unassigned;\n            }\n        }\n        m_ActiveEdges = 0;\n        m_CurrentLM = m_MinimaList.begin();\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperBase::DisposeLocalMinimaList()\n    {\n        m_MinimaList.clear();\n        m_CurrentLM = m_MinimaList.begin();\n    }\n//------------------------------------------------------------------------------\n\n    bool ClipperBase::PopLocalMinima(cInt Y, const LocalMinimum *&locMin)\n    {\n        if (m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y) return false;\n        locMin = &(*m_CurrentLM);\n        ++m_CurrentLM;\n        return true;\n    }\n//------------------------------------------------------------------------------\n\n    IntRect ClipperBase::GetBounds()\n    {\n        IntRect result;\n        MinimaList::iterator lm = m_MinimaList.begin();\n        if (lm == m_MinimaList.end())\n        {\n            result.left = result.top = result.right = result.bottom = 0;\n            return result;\n        }\n        result.left = lm->LeftBound->Bot.X;\n        result.top = lm->LeftBound->Bot.Y;\n        result.right = lm->LeftBound->Bot.X;\n        result.bottom = lm->LeftBound->Bot.Y;\n        while (lm != m_MinimaList.end())\n        {\n            //todo - needs fixing for open paths\n            result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y);\n            TEdge* e = lm->LeftBound;\n            for (;;) {\n                TEdge* bottomE = e;\n                while (e->NextInLML)\n                {\n                    if (e->Bot.X < result.left) result.left = e->Bot.X;\n                    if (e->Bot.X > result.right) result.right = e->Bot.X;\n                    e = e->NextInLML;\n                }\n                result.left = std::min(result.left, e->Bot.X);\n                result.right = std::max(result.right, e->Bot.X);\n                result.left = std::min(result.left, e->Top.X);\n                result.right = std::max(result.right, e->Top.X);\n                result.top = std::min(result.top, e->Top.Y);\n                if (bottomE == lm->LeftBound) e = lm->RightBound;\n                else break;\n            }\n            ++lm;\n        }\n        return result;\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperBase::InsertScanbeam(const cInt Y)\n    {\n        m_Scanbeam.push(Y);\n    }\n//------------------------------------------------------------------------------\n\n    bool ClipperBase::PopScanbeam(cInt &Y)\n    {\n        if (m_Scanbeam.empty()) return false;\n        Y = m_Scanbeam.top();\n        m_Scanbeam.pop();\n        while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates.\n        return true;\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperBase::DisposeAllOutRecs(){\n        for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)\n            DisposeOutRec(i);\n        m_PolyOuts.clear();\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperBase::DisposeOutRec(PolyOutList::size_type index)\n    {\n        OutRec *outRec = m_PolyOuts[index];\n        if (outRec->Pts) DisposeOutPts(outRec->Pts);\n        delete outRec;\n        m_PolyOuts[index] = 0;\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperBase::DeleteFromAEL(TEdge *e)\n    {\n        TEdge* AelPrev = e->PrevInAEL;\n        TEdge* AelNext = e->NextInAEL;\n        if (!AelPrev &&  !AelNext && (e != m_ActiveEdges)) return; //already deleted\n        if (AelPrev) AelPrev->NextInAEL = AelNext;\n        else m_ActiveEdges = AelNext;\n        if (AelNext) AelNext->PrevInAEL = AelPrev;\n        e->NextInAEL = 0;\n        e->PrevInAEL = 0;\n    }\n//------------------------------------------------------------------------------\n\n    OutRec* ClipperBase::CreateOutRec()\n    {\n        OutRec* result = new OutRec;\n        result->IsHole = false;\n        result->IsOpen = false;\n        result->FirstLeft = 0;\n        result->Pts = 0;\n        result->BottomPt = 0;\n        result->PolyNd = 0;\n        m_PolyOuts.push_back(result);\n        result->Idx = (int)m_PolyOuts.size() - 1;\n        return result;\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperBase::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2)\n    {\n        //check that one or other edge hasn't already been removed from AEL ...\n        if (Edge1->NextInAEL == Edge1->PrevInAEL ||\n            Edge2->NextInAEL == Edge2->PrevInAEL) return;\n\n        if (Edge1->NextInAEL == Edge2)\n        {\n            TEdge* Next = Edge2->NextInAEL;\n            if (Next) Next->PrevInAEL = Edge1;\n            TEdge* Prev = Edge1->PrevInAEL;\n            if (Prev) Prev->NextInAEL = Edge2;\n            Edge2->PrevInAEL = Prev;\n            Edge2->NextInAEL = Edge1;\n            Edge1->PrevInAEL = Edge2;\n            Edge1->NextInAEL = Next;\n        }\n        else if (Edge2->NextInAEL == Edge1)\n        {\n            TEdge* Next = Edge1->NextInAEL;\n            if (Next) Next->PrevInAEL = Edge2;\n            TEdge* Prev = Edge2->PrevInAEL;\n            if (Prev) Prev->NextInAEL = Edge1;\n            Edge1->PrevInAEL = Prev;\n            Edge1->NextInAEL = Edge2;\n            Edge2->PrevInAEL = Edge1;\n            Edge2->NextInAEL = Next;\n        }\n        else\n        {\n            TEdge* Next = Edge1->NextInAEL;\n            TEdge* Prev = Edge1->PrevInAEL;\n            Edge1->NextInAEL = Edge2->NextInAEL;\n            if (Edge1->NextInAEL) Edge1->NextInAEL->PrevInAEL = Edge1;\n            Edge1->PrevInAEL = Edge2->PrevInAEL;\n            if (Edge1->PrevInAEL) Edge1->PrevInAEL->NextInAEL = Edge1;\n            Edge2->NextInAEL = Next;\n            if (Edge2->NextInAEL) Edge2->NextInAEL->PrevInAEL = Edge2;\n            Edge2->PrevInAEL = Prev;\n            if (Edge2->PrevInAEL) Edge2->PrevInAEL->NextInAEL = Edge2;\n        }\n\n        if (!Edge1->PrevInAEL) m_ActiveEdges = Edge1;\n        else if (!Edge2->PrevInAEL) m_ActiveEdges = Edge2;\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperBase::UpdateEdgeIntoAEL(TEdge *&e)\n    {\n        if (!e->NextInLML)\n            return;//throw clipperException(\"UpdateEdgeIntoAEL: invalid call\");\n\n        e->NextInLML->OutIdx = e->OutIdx;\n        TEdge* AelPrev = e->PrevInAEL;\n        TEdge* AelNext = e->NextInAEL;\n        if (AelPrev) AelPrev->NextInAEL = e->NextInLML;\n        else m_ActiveEdges = e->NextInLML;\n        if (AelNext) AelNext->PrevInAEL = e->NextInLML;\n        e->NextInLML->Side = e->Side;\n        e->NextInLML->WindDelta = e->WindDelta;\n        e->NextInLML->WindCnt = e->WindCnt;\n        e->NextInLML->WindCnt2 = e->WindCnt2;\n        e = e->NextInLML;\n        e->Curr = e->Bot;\n        e->PrevInAEL = AelPrev;\n        e->NextInAEL = AelNext;\n        if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y);\n    }\n//------------------------------------------------------------------------------\n\n    bool ClipperBase::LocalMinimaPending()\n    {\n        return (m_CurrentLM != m_MinimaList.end());\n    }\n\n//------------------------------------------------------------------------------\n// TClipper methods ...\n//------------------------------------------------------------------------------\n\n    Clipper::Clipper(int initOptions) : ClipperBase() //constructor\n    {\n        m_ExecuteLocked = false;\n        m_UseFullRange = false;\n        m_ReverseOutput = ((initOptions & ioReverseSolution) != 0);\n        m_StrictSimple = ((initOptions & ioStrictlySimple) != 0);\n        m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0);\n        m_HasOpenPaths = false;\n#ifdef use_xyz\n        m_ZFill = 0;\n#endif\n    }\n//------------------------------------------------------------------------------\n\n#ifdef use_xyz\n    void Clipper::ZFillFunction(ZFillCallback zFillFunc)\n{\n  m_ZFill = zFillFunc;\n}\n//------------------------------------------------------------------------------\n#endif\n\n    bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType)\n    {\n        return Execute(clipType, solution, fillType, fillType);\n    }\n//------------------------------------------------------------------------------\n\n    bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType)\n    {\n        return Execute(clipType, polytree, fillType, fillType);\n    }\n//------------------------------------------------------------------------------\n\n    bool Clipper::Execute(ClipType clipType, Paths &solution,\n                          PolyFillType subjFillType, PolyFillType clipFillType)\n    {\n        if( m_ExecuteLocked ) return false;\n        if (m_HasOpenPaths)\n            return false;//throw clipperException(\"Error: PolyTree struct is needed for open path clipping.\");\n        m_ExecuteLocked = true;\n        solution.resize(0);\n        m_SubjFillType = subjFillType;\n        m_ClipFillType = clipFillType;\n        m_ClipType = clipType;\n        m_UsingPolyTree = false;\n        bool succeeded = ExecuteInternal();\n        if (succeeded) BuildResult(solution);\n        DisposeAllOutRecs();\n        m_ExecuteLocked = false;\n        return succeeded;\n    }\n//------------------------------------------------------------------------------\n\n    bool Clipper::Execute(ClipType clipType, PolyTree& polytree,\n                          PolyFillType subjFillType, PolyFillType clipFillType)\n    {\n        if( m_ExecuteLocked ) return false;\n        m_ExecuteLocked = true;\n        m_SubjFillType = subjFillType;\n        m_ClipFillType = clipFillType;\n        m_ClipType = clipType;\n        m_UsingPolyTree = true;\n        bool succeeded = ExecuteInternal();\n        if (succeeded) BuildResult2(polytree);\n        DisposeAllOutRecs();\n        m_ExecuteLocked = false;\n        return succeeded;\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::FixHoleLinkage(OutRec &outrec)\n    {\n        //skip OutRecs that (a) contain outermost polygons or\n        //(b) already have the correct owner/child linkage ...\n        if (!outrec.FirstLeft ||\n            (outrec.IsHole != outrec.FirstLeft->IsHole &&\n             outrec.FirstLeft->Pts)) return;\n\n        OutRec* orfl = outrec.FirstLeft;\n        while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts))\n            orfl = orfl->FirstLeft;\n        outrec.FirstLeft = orfl;\n    }\n//------------------------------------------------------------------------------\n\n    bool Clipper::ExecuteInternal()\n    {\n        bool succeeded = true;\n        //try\n        {\n            Reset();\n            m_Maxima = MaximaList();\n            m_SortedEdges = 0;\n\n            succeeded = true;\n            cInt botY, topY;\n            if (!PopScanbeam(botY)) return false;\n            InsertLocalMinimaIntoAEL(botY);\n            while (PopScanbeam(topY) || LocalMinimaPending())\n            {\n                ProcessHorizontals();\n                ClearGhostJoins();\n                if (!ProcessIntersections(topY))\n                {\n                    succeeded = false;\n                    break;\n                }\n                ProcessEdgesAtTopOfScanbeam(topY);\n                botY = topY;\n                InsertLocalMinimaIntoAEL(botY);\n            }\n        }\n        //catch(...)\n        //{\n        //    succeeded = false;\n        //}\n\n        if (succeeded)\n        {\n            //fix orientations ...\n            for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)\n            {\n                OutRec *outRec = m_PolyOuts[i];\n                if (!outRec->Pts || outRec->IsOpen) continue;\n                if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0))\n                    ReversePolyPtLinks(outRec->Pts);\n            }\n\n            if (!m_Joins.empty()) JoinCommonEdges();\n\n            //unfortunately FixupOutPolygon() must be done after JoinCommonEdges()\n            for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)\n            {\n                OutRec *outRec = m_PolyOuts[i];\n                if (!outRec->Pts) continue;\n                if (outRec->IsOpen)\n                    FixupOutPolyline(*outRec);\n                else\n                    FixupOutPolygon(*outRec);\n            }\n\n            if (m_StrictSimple) DoSimplePolygons();\n        }\n\n        ClearJoins();\n        ClearGhostJoins();\n        return succeeded;\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::SetWindingCount(TEdge &edge)\n    {\n        TEdge *e = edge.PrevInAEL;\n        //find the edge of the same polytype that immediately preceeds 'edge' in AEL\n        while (e  && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL;\n        if (!e)\n        {\n            if (edge.WindDelta == 0)\n            {\n                PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType);\n                edge.WindCnt = (pft == pftNegative ? -1 : 1);\n            }\n            else\n                edge.WindCnt = edge.WindDelta;\n            edge.WindCnt2 = 0;\n            e = m_ActiveEdges; //ie get ready to calc WindCnt2\n        }\n        else if (edge.WindDelta == 0 && m_ClipType != ctUnion)\n        {\n            edge.WindCnt = 1;\n            edge.WindCnt2 = e->WindCnt2;\n            e = e->NextInAEL; //ie get ready to calc WindCnt2\n        }\n        else if (IsEvenOddFillType(edge))\n        {\n            //EvenOdd filling ...\n            if (edge.WindDelta == 0)\n            {\n                //are we inside a subj polygon ...\n                bool Inside = true;\n                TEdge *e2 = e->PrevInAEL;\n                while (e2)\n                {\n                    if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0)\n                        Inside = !Inside;\n                    e2 = e2->PrevInAEL;\n                }\n                edge.WindCnt = (Inside ? 0 : 1);\n            }\n            else\n            {\n                edge.WindCnt = edge.WindDelta;\n            }\n            edge.WindCnt2 = e->WindCnt2;\n            e = e->NextInAEL; //ie get ready to calc WindCnt2\n        }\n        else\n        {\n            //nonZero, Positive or Negative filling ...\n            if (e->WindCnt * e->WindDelta < 0)\n            {\n                //prev edge is 'decreasing' WindCount (WC) toward zero\n                //so we're outside the previous polygon ...\n                if (Abs(e->WindCnt) > 1)\n                {\n                    //outside prev poly but still inside another.\n                    //when reversing direction of prev poly use the same WC\n                    if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt;\n                        //otherwise continue to 'decrease' WC ...\n                    else edge.WindCnt = e->WindCnt + edge.WindDelta;\n                }\n                else\n                    //now outside all polys of same polytype so set own WC ...\n                    edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta);\n            } else\n            {\n                //prev edge is 'increasing' WindCount (WC) away from zero\n                //so we're inside the previous polygon ...\n                if (edge.WindDelta == 0)\n                    edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1);\n                    //if wind direction is reversing prev then use same WC\n                else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt;\n                    //otherwise add to WC ...\n                else edge.WindCnt = e->WindCnt + edge.WindDelta;\n            }\n            edge.WindCnt2 = e->WindCnt2;\n            e = e->NextInAEL; //ie get ready to calc WindCnt2\n        }\n\n        //update WindCnt2 ...\n        if (IsEvenOddAltFillType(edge))\n        {\n            //EvenOdd filling ...\n            while (e != &edge)\n            {\n                if (e->WindDelta != 0)\n                    edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0);\n                e = e->NextInAEL;\n            }\n        } else\n        {\n            //nonZero, Positive or Negative filling ...\n            while ( e != &edge )\n            {\n                edge.WindCnt2 += e->WindDelta;\n                e = e->NextInAEL;\n            }\n        }\n    }\n//------------------------------------------------------------------------------\n\n    bool Clipper::IsEvenOddFillType(const TEdge& edge) const\n    {\n        if (edge.PolyTyp == ptSubject)\n            return m_SubjFillType == pftEvenOdd; else\n            return m_ClipFillType == pftEvenOdd;\n    }\n//------------------------------------------------------------------------------\n\n    bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const\n    {\n        if (edge.PolyTyp == ptSubject)\n            return m_ClipFillType == pftEvenOdd; else\n            return m_SubjFillType == pftEvenOdd;\n    }\n//------------------------------------------------------------------------------\n\n    bool Clipper::IsContributing(const TEdge& edge) const\n    {\n        PolyFillType pft, pft2;\n        if (edge.PolyTyp == ptSubject)\n        {\n            pft = m_SubjFillType;\n            pft2 = m_ClipFillType;\n        } else\n        {\n            pft = m_ClipFillType;\n            pft2 = m_SubjFillType;\n        }\n\n        switch(pft)\n        {\n            case pftEvenOdd:\n                //return false if a subj line has been flagged as inside a subj polygon\n                if (edge.WindDelta == 0 && edge.WindCnt != 1) return false;\n                break;\n            case pftNonZero:\n                if (Abs(edge.WindCnt) != 1) return false;\n                break;\n            case pftPositive:\n                if (edge.WindCnt != 1) return false;\n                break;\n            default: //pftNegative\n                if (edge.WindCnt != -1) return false;\n        }\n\n        switch(m_ClipType)\n        {\n            case ctIntersection:\n                switch(pft2)\n                {\n                    case pftEvenOdd:\n                    case pftNonZero:\n                        return (edge.WindCnt2 != 0);\n                    case pftPositive:\n                        return (edge.WindCnt2 > 0);\n                    default:\n                        return (edge.WindCnt2 < 0);\n                }\n                break;\n            case ctUnion:\n                switch(pft2)\n                {\n                    case pftEvenOdd:\n                    case pftNonZero:\n                        return (edge.WindCnt2 == 0);\n                    case pftPositive:\n                        return (edge.WindCnt2 <= 0);\n                    default:\n                        return (edge.WindCnt2 >= 0);\n                }\n                break;\n            case ctDifference:\n                if (edge.PolyTyp == ptSubject)\n                    switch(pft2)\n                    {\n                        case pftEvenOdd:\n                        case pftNonZero:\n                            return (edge.WindCnt2 == 0);\n                        case pftPositive:\n                            return (edge.WindCnt2 <= 0);\n                        default:\n                            return (edge.WindCnt2 >= 0);\n                    }\n                else\n                    switch(pft2)\n                    {\n                        case pftEvenOdd:\n                        case pftNonZero:\n                            return (edge.WindCnt2 != 0);\n                        case pftPositive:\n                            return (edge.WindCnt2 > 0);\n                        default:\n                            return (edge.WindCnt2 < 0);\n                    }\n                break;\n            case ctXor:\n                if (edge.WindDelta == 0) //XOr always contributing unless open\n                    switch(pft2)\n                    {\n                        case pftEvenOdd:\n                        case pftNonZero:\n                            return (edge.WindCnt2 == 0);\n                        case pftPositive:\n                            return (edge.WindCnt2 <= 0);\n                        default:\n                            return (edge.WindCnt2 >= 0);\n                    }\n                else\n                    return true;\n                break;\n            default:\n                return true;\n        }\n    }\n//------------------------------------------------------------------------------\n\n    OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt)\n    {\n        OutPt* result;\n        TEdge *e, *prevE;\n        if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx ))\n        {\n            result = AddOutPt(e1, Pt);\n            e2->OutIdx = e1->OutIdx;\n            e1->Side = esLeft;\n            e2->Side = esRight;\n            e = e1;\n            if (e->PrevInAEL == e2)\n                prevE = e2->PrevInAEL;\n            else\n                prevE = e->PrevInAEL;\n        } else\n        {\n            result = AddOutPt(e2, Pt);\n            e1->OutIdx = e2->OutIdx;\n            e1->Side = esRight;\n            e2->Side = esLeft;\n            e = e2;\n            if (e->PrevInAEL == e1)\n                prevE = e1->PrevInAEL;\n            else\n                prevE = e->PrevInAEL;\n        }\n\n        if (prevE && prevE->OutIdx >= 0 && prevE->Top.Y < Pt.Y && e->Top.Y < Pt.Y)\n        {\n            cInt xPrev = TopX(*prevE, Pt.Y);\n            cInt xE = TopX(*e, Pt.Y);\n            if (xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0) &&\n                SlopesEqual(IntPoint(xPrev, Pt.Y), prevE->Top, IntPoint(xE, Pt.Y), e->Top, m_UseFullRange))\n            {\n                OutPt* outPt = AddOutPt(prevE, Pt);\n                AddJoin(result, outPt, e->Top);\n            }\n        }\n        return result;\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt)\n    {\n        AddOutPt( e1, Pt );\n        if (e2->WindDelta == 0) AddOutPt(e2, Pt);\n        if( e1->OutIdx == e2->OutIdx )\n        {\n            e1->OutIdx = Unassigned;\n            e2->OutIdx = Unassigned;\n        }\n        else if (e1->OutIdx < e2->OutIdx)\n            AppendPolygon(e1, e2);\n        else\n            AppendPolygon(e2, e1);\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::AddEdgeToSEL(TEdge *edge)\n    {\n        //SEL pointers in PEdge are reused to build a list of horizontal edges.\n        //However, we don't need to worry about order with horizontal edge processing.\n        if( !m_SortedEdges )\n        {\n            m_SortedEdges = edge;\n            edge->PrevInSEL = 0;\n            edge->NextInSEL = 0;\n        }\n        else\n        {\n            edge->NextInSEL = m_SortedEdges;\n            edge->PrevInSEL = 0;\n            m_SortedEdges->PrevInSEL = edge;\n            m_SortedEdges = edge;\n        }\n    }\n//------------------------------------------------------------------------------\n\n    bool Clipper::PopEdgeFromSEL(TEdge *&edge)\n    {\n        if (!m_SortedEdges) return false;\n        edge = m_SortedEdges;\n        DeleteFromSEL(m_SortedEdges);\n        return true;\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::CopyAELToSEL()\n    {\n        TEdge* e = m_ActiveEdges;\n        m_SortedEdges = e;\n        while ( e )\n        {\n            e->PrevInSEL = e->PrevInAEL;\n            e->NextInSEL = e->NextInAEL;\n            e = e->NextInAEL;\n        }\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint OffPt)\n    {\n        Join* j = new Join;\n        j->OutPt1 = op1;\n        j->OutPt2 = op2;\n        j->OffPt = OffPt;\n        m_Joins.push_back(j);\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::ClearJoins()\n    {\n        for (JoinList::size_type i = 0; i < m_Joins.size(); i++)\n            delete m_Joins[i];\n        m_Joins.resize(0);\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::ClearGhostJoins()\n    {\n        for (JoinList::size_type i = 0; i < m_GhostJoins.size(); i++)\n            delete m_GhostJoins[i];\n        m_GhostJoins.resize(0);\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt)\n    {\n        Join* j = new Join;\n        j->OutPt1 = op;\n        j->OutPt2 = 0;\n        j->OffPt = OffPt;\n        m_GhostJoins.push_back(j);\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::InsertLocalMinimaIntoAEL(const cInt botY)\n    {\n        const LocalMinimum *lm;\n        while (PopLocalMinima(botY, lm))\n        {\n            TEdge* lb = lm->LeftBound;\n            TEdge* rb = lm->RightBound;\n\n            OutPt *Op1 = 0;\n            if (!lb)\n            {\n                //nb: don't insert LB into either AEL or SEL\n                InsertEdgeIntoAEL(rb, 0);\n                SetWindingCount(*rb);\n                if (IsContributing(*rb))\n                    Op1 = AddOutPt(rb, rb->Bot);\n            }\n            else if (!rb)\n            {\n                InsertEdgeIntoAEL(lb, 0);\n                SetWindingCount(*lb);\n                if (IsContributing(*lb))\n                    Op1 = AddOutPt(lb, lb->Bot);\n                InsertScanbeam(lb->Top.Y);\n            }\n            else\n            {\n                InsertEdgeIntoAEL(lb, 0);\n                InsertEdgeIntoAEL(rb, lb);\n                SetWindingCount( *lb );\n                rb->WindCnt = lb->WindCnt;\n                rb->WindCnt2 = lb->WindCnt2;\n                if (IsContributing(*lb))\n                    Op1 = AddLocalMinPoly(lb, rb, lb->Bot);\n                InsertScanbeam(lb->Top.Y);\n            }\n\n            if (rb)\n            {\n                if (IsHorizontal(*rb))\n                {\n                    AddEdgeToSEL(rb);\n                    if (rb->NextInLML)\n                        InsertScanbeam(rb->NextInLML->Top.Y);\n                }\n                else InsertScanbeam( rb->Top.Y );\n            }\n\n            if (!lb || !rb) continue;\n\n            //if any output polygons share an edge, they'll need joining later ...\n            if (Op1 && IsHorizontal(*rb) &&\n                m_GhostJoins.size() > 0 && (rb->WindDelta != 0))\n            {\n                for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i)\n                {\n                    Join* jr = m_GhostJoins[i];\n                    //if the horizontal Rb and a 'ghost' horizontal overlap, then convert\n                    //the 'ghost' join to a real join ready for later ...\n                    if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X))\n                        AddJoin(jr->OutPt1, Op1, jr->OffPt);\n                }\n            }\n\n            if (lb->OutIdx >= 0 && lb->PrevInAEL &&\n                lb->PrevInAEL->Curr.X == lb->Bot.X &&\n                lb->PrevInAEL->OutIdx >= 0 &&\n                SlopesEqual(lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top, m_UseFullRange) &&\n                (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0))\n            {\n                OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot);\n                AddJoin(Op1, Op2, lb->Top);\n            }\n\n            if(lb->NextInAEL != rb)\n            {\n\n                if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 &&\n                    SlopesEqual(rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top, m_UseFullRange) &&\n                    (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0))\n                {\n                    OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot);\n                    AddJoin(Op1, Op2, rb->Top);\n                }\n\n                TEdge* e = lb->NextInAEL;\n                if (e)\n                {\n                    while( e != rb )\n                    {\n                        //nb: For calculating winding counts etc, IntersectEdges() assumes\n                        //that param1 will be to the Right of param2 ABOVE the intersection ...\n                        IntersectEdges(rb , e , lb->Curr); //order important here\n                        e = e->NextInAEL;\n                    }\n                }\n            }\n\n        }\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::DeleteFromSEL(TEdge *e)\n    {\n        TEdge* SelPrev = e->PrevInSEL;\n        TEdge* SelNext = e->NextInSEL;\n        if( !SelPrev &&  !SelNext && (e != m_SortedEdges) ) return; //already deleted\n        if( SelPrev ) SelPrev->NextInSEL = SelNext;\n        else m_SortedEdges = SelNext;\n        if( SelNext ) SelNext->PrevInSEL = SelPrev;\n        e->NextInSEL = 0;\n        e->PrevInSEL = 0;\n    }\n//------------------------------------------------------------------------------\n\n#ifdef use_xyz\n    void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2)\n{\n  if (pt.Z != 0 || !m_ZFill) return;\n  else if (pt == e1.Bot) pt.Z = e1.Bot.Z;\n  else if (pt == e1.Top) pt.Z = e1.Top.Z;\n  else if (pt == e2.Bot) pt.Z = e2.Bot.Z;\n  else if (pt == e2.Top) pt.Z = e2.Top.Z;\n  else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt);\n}\n//------------------------------------------------------------------------------\n#endif\n\n    void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt)\n    {\n        bool e1Contributing = ( e1->OutIdx >= 0 );\n        bool e2Contributing = ( e2->OutIdx >= 0 );\n\n#ifdef use_xyz\n        SetZ(Pt, *e1, *e2);\n#endif\n\n#ifdef use_lines\n        //if either edge is on an OPEN path ...\n        if (e1->WindDelta == 0 || e2->WindDelta == 0)\n        {\n            //ignore subject-subject open path intersections UNLESS they\n            //are both open paths, AND they are both 'contributing maximas' ...\n            if (e1->WindDelta == 0 && e2->WindDelta == 0) return;\n\n                //if intersecting a subj line with a subj poly ...\n            else if (e1->PolyTyp == e2->PolyTyp &&\n                     e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion)\n            {\n                if (e1->WindDelta == 0)\n                {\n                    if (e2Contributing)\n                    {\n                        AddOutPt(e1, Pt);\n                        if (e1Contributing) e1->OutIdx = Unassigned;\n                    }\n                }\n                else\n                {\n                    if (e1Contributing)\n                    {\n                        AddOutPt(e2, Pt);\n                        if (e2Contributing) e2->OutIdx = Unassigned;\n                    }\n                }\n            }\n            else if (e1->PolyTyp != e2->PolyTyp)\n            {\n                //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ...\n                if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 &&\n                    (m_ClipType != ctUnion || e2->WindCnt2 == 0))\n                {\n                    AddOutPt(e1, Pt);\n                    if (e1Contributing) e1->OutIdx = Unassigned;\n                }\n                else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) &&\n                         (m_ClipType != ctUnion || e1->WindCnt2 == 0))\n                {\n                    AddOutPt(e2, Pt);\n                    if (e2Contributing) e2->OutIdx = Unassigned;\n                }\n            }\n            return;\n        }\n#endif\n\n        //update winding counts...\n        //assumes that e1 will be to the Right of e2 ABOVE the intersection\n        if ( e1->PolyTyp == e2->PolyTyp )\n        {\n            if ( IsEvenOddFillType( *e1) )\n            {\n                int oldE1WindCnt = e1->WindCnt;\n                e1->WindCnt = e2->WindCnt;\n                e2->WindCnt = oldE1WindCnt;\n            } else\n            {\n                if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt;\n                else e1->WindCnt += e2->WindDelta;\n                if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt;\n                else e2->WindCnt -= e1->WindDelta;\n            }\n        } else\n        {\n            if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta;\n            else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0;\n            if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta;\n            else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0;\n        }\n\n        PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2;\n        if (e1->PolyTyp == ptSubject)\n        {\n            e1FillType = m_SubjFillType;\n            e1FillType2 = m_ClipFillType;\n        } else\n        {\n            e1FillType = m_ClipFillType;\n            e1FillType2 = m_SubjFillType;\n        }\n        if (e2->PolyTyp == ptSubject)\n        {\n            e2FillType = m_SubjFillType;\n            e2FillType2 = m_ClipFillType;\n        } else\n        {\n            e2FillType = m_ClipFillType;\n            e2FillType2 = m_SubjFillType;\n        }\n\n        cInt e1Wc, e2Wc;\n        switch (e1FillType)\n        {\n            case pftPositive: e1Wc = e1->WindCnt; break;\n            case pftNegative: e1Wc = -e1->WindCnt; break;\n            default: e1Wc = Abs(e1->WindCnt);\n        }\n        switch(e2FillType)\n        {\n            case pftPositive: e2Wc = e2->WindCnt; break;\n            case pftNegative: e2Wc = -e2->WindCnt; break;\n            default: e2Wc = Abs(e2->WindCnt);\n        }\n\n        if ( e1Contributing && e2Contributing )\n        {\n            if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) ||\n                (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) )\n            {\n                AddLocalMaxPoly(e1, e2, Pt);\n            }\n            else\n            {\n                AddOutPt(e1, Pt);\n                AddOutPt(e2, Pt);\n                SwapSides( *e1 , *e2 );\n                SwapPolyIndexes( *e1 , *e2 );\n            }\n        }\n        else if ( e1Contributing )\n        {\n            if (e2Wc == 0 || e2Wc == 1)\n            {\n                AddOutPt(e1, Pt);\n                SwapSides(*e1, *e2);\n                SwapPolyIndexes(*e1, *e2);\n            }\n        }\n        else if ( e2Contributing )\n        {\n            if (e1Wc == 0 || e1Wc == 1)\n            {\n                AddOutPt(e2, Pt);\n                SwapSides(*e1, *e2);\n                SwapPolyIndexes(*e1, *e2);\n            }\n        }\n        else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1))\n        {\n            //neither edge is currently contributing ...\n\n            cInt e1Wc2, e2Wc2;\n            switch (e1FillType2)\n            {\n                case pftPositive: e1Wc2 = e1->WindCnt2; break;\n                case pftNegative : e1Wc2 = -e1->WindCnt2; break;\n                default: e1Wc2 = Abs(e1->WindCnt2);\n            }\n            switch (e2FillType2)\n            {\n                case pftPositive: e2Wc2 = e2->WindCnt2; break;\n                case pftNegative: e2Wc2 = -e2->WindCnt2; break;\n                default: e2Wc2 = Abs(e2->WindCnt2);\n            }\n\n            if (e1->PolyTyp != e2->PolyTyp)\n            {\n                AddLocalMinPoly(e1, e2, Pt);\n            }\n            else if (e1Wc == 1 && e2Wc == 1)\n                switch( m_ClipType ) {\n                    case ctIntersection:\n                        if (e1Wc2 > 0 && e2Wc2 > 0)\n                            AddLocalMinPoly(e1, e2, Pt);\n                        break;\n                    case ctUnion:\n                        if ( e1Wc2 <= 0 && e2Wc2 <= 0 )\n                            AddLocalMinPoly(e1, e2, Pt);\n                        break;\n                    case ctDifference:\n                        if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||\n                            ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))\n                            AddLocalMinPoly(e1, e2, Pt);\n                        break;\n                    case ctXor:\n                        AddLocalMinPoly(e1, e2, Pt);\n                }\n            else\n                SwapSides( *e1, *e2 );\n        }\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::SetHoleState(TEdge *e, OutRec *outrec)\n    {\n        TEdge *e2 = e->PrevInAEL;\n        TEdge *eTmp = 0;\n        while (e2)\n        {\n            if (e2->OutIdx >= 0 && e2->WindDelta != 0)\n            {\n                if (!eTmp) eTmp = e2;\n                else if (eTmp->OutIdx == e2->OutIdx) eTmp = 0;\n            }\n            e2 = e2->PrevInAEL;\n        }\n        if (!eTmp)\n        {\n            outrec->FirstLeft = 0;\n            outrec->IsHole = false;\n        }\n        else\n        {\n            outrec->FirstLeft = m_PolyOuts[eTmp->OutIdx];\n            outrec->IsHole = !outrec->FirstLeft->IsHole;\n        }\n    }\n//------------------------------------------------------------------------------\n\n    OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2)\n    {\n        //work out which polygon fragment has the correct hole state ...\n        if (!outRec1->BottomPt)\n            outRec1->BottomPt = GetBottomPt(outRec1->Pts);\n        if (!outRec2->BottomPt)\n            outRec2->BottomPt = GetBottomPt(outRec2->Pts);\n        OutPt *OutPt1 = outRec1->BottomPt;\n        OutPt *OutPt2 = outRec2->BottomPt;\n        if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1;\n        else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2;\n        else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1;\n        else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2;\n        else if (OutPt1->Next == OutPt1) return outRec2;\n        else if (OutPt2->Next == OutPt2) return outRec1;\n        else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1;\n        else return outRec2;\n    }\n//------------------------------------------------------------------------------\n\n    bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2)\n    {\n        do\n        {\n            outRec1 = outRec1->FirstLeft;\n            if (outRec1 == outRec2) return true;\n        } while (outRec1);\n        return false;\n    }\n//------------------------------------------------------------------------------\n\n    OutRec* Clipper::GetOutRec(int Idx)\n    {\n        OutRec* outrec = m_PolyOuts[Idx];\n        while (outrec != m_PolyOuts[outrec->Idx])\n            outrec = m_PolyOuts[outrec->Idx];\n        return outrec;\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::AppendPolygon(TEdge *e1, TEdge *e2)\n    {\n        //get the start and ends of both output polygons ...\n        OutRec *outRec1 = m_PolyOuts[e1->OutIdx];\n        OutRec *outRec2 = m_PolyOuts[e2->OutIdx];\n\n        OutRec *holeStateRec;\n        if (OutRec1RightOfOutRec2(outRec1, outRec2))\n            holeStateRec = outRec2;\n        else if (OutRec1RightOfOutRec2(outRec2, outRec1))\n            holeStateRec = outRec1;\n        else\n            holeStateRec = GetLowermostRec(outRec1, outRec2);\n\n        //get the start and ends of both output polygons and\n        //join e2 poly onto e1 poly and delete pointers to e2 ...\n\n        OutPt* p1_lft = outRec1->Pts;\n        OutPt* p1_rt = p1_lft->Prev;\n        OutPt* p2_lft = outRec2->Pts;\n        OutPt* p2_rt = p2_lft->Prev;\n\n        //join e2 poly onto e1 poly and delete pointers to e2 ...\n        if(  e1->Side == esLeft )\n        {\n            if(  e2->Side == esLeft )\n            {\n                //z y x a b c\n                ReversePolyPtLinks(p2_lft);\n                p2_lft->Next = p1_lft;\n                p1_lft->Prev = p2_lft;\n                p1_rt->Next = p2_rt;\n                p2_rt->Prev = p1_rt;\n                outRec1->Pts = p2_rt;\n            } else\n            {\n                //x y z a b c\n                p2_rt->Next = p1_lft;\n                p1_lft->Prev = p2_rt;\n                p2_lft->Prev = p1_rt;\n                p1_rt->Next = p2_lft;\n                outRec1->Pts = p2_lft;\n            }\n        } else\n        {\n            if(  e2->Side == esRight )\n            {\n                //a b c z y x\n                ReversePolyPtLinks(p2_lft);\n                p1_rt->Next = p2_rt;\n                p2_rt->Prev = p1_rt;\n                p2_lft->Next = p1_lft;\n                p1_lft->Prev = p2_lft;\n            } else\n            {\n                //a b c x y z\n                p1_rt->Next = p2_lft;\n                p2_lft->Prev = p1_rt;\n                p1_lft->Prev = p2_rt;\n                p2_rt->Next = p1_lft;\n            }\n        }\n\n        outRec1->BottomPt = 0;\n        if (holeStateRec == outRec2)\n        {\n            if (outRec2->FirstLeft != outRec1)\n                outRec1->FirstLeft = outRec2->FirstLeft;\n            outRec1->IsHole = outRec2->IsHole;\n        }\n        outRec2->Pts = 0;\n        outRec2->BottomPt = 0;\n        outRec2->FirstLeft = outRec1;\n\n        int OKIdx = e1->OutIdx;\n        int ObsoleteIdx = e2->OutIdx;\n\n        e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly\n        e2->OutIdx = Unassigned;\n\n        TEdge* e = m_ActiveEdges;\n        while( e )\n        {\n            if( e->OutIdx == ObsoleteIdx )\n            {\n                e->OutIdx = OKIdx;\n                e->Side = e1->Side;\n                break;\n            }\n            e = e->NextInAEL;\n        }\n\n        outRec2->Idx = outRec1->Idx;\n    }\n//------------------------------------------------------------------------------\n\n    OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)\n    {\n        if(  e->OutIdx < 0 )\n        {\n            OutRec *outRec = CreateOutRec();\n            outRec->IsOpen = (e->WindDelta == 0);\n            OutPt* newOp = new OutPt;\n            outRec->Pts = newOp;\n            newOp->Idx = outRec->Idx;\n            newOp->Pt = pt;\n            newOp->Next = newOp;\n            newOp->Prev = newOp;\n            if (!outRec->IsOpen)\n                SetHoleState(e, outRec);\n            e->OutIdx = outRec->Idx;\n            return newOp;\n        } else\n        {\n            OutRec *outRec = m_PolyOuts[e->OutIdx];\n            //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most'\n            OutPt* op = outRec->Pts;\n\n            bool ToFront = (e->Side == esLeft);\n            if (ToFront && (pt == op->Pt)) return op;\n            else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev;\n\n            OutPt* newOp = new OutPt;\n            newOp->Idx = outRec->Idx;\n            newOp->Pt = pt;\n            newOp->Next = op;\n            newOp->Prev = op->Prev;\n            newOp->Prev->Next = newOp;\n            op->Prev = newOp;\n            if (ToFront) outRec->Pts = newOp;\n            return newOp;\n        }\n    }\n//------------------------------------------------------------------------------\n\n    OutPt* Clipper::GetLastOutPt(TEdge *e)\n    {\n        OutRec *outRec = m_PolyOuts[e->OutIdx];\n        if (e->Side == esLeft)\n            return outRec->Pts;\n        else\n            return outRec->Pts->Prev;\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::ProcessHorizontals()\n    {\n        TEdge* horzEdge;\n        while (PopEdgeFromSEL(horzEdge))\n            ProcessHorizontal(horzEdge);\n    }\n//------------------------------------------------------------------------------\n\n    inline bool IsMinima(TEdge *e)\n    {\n        return e  && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e);\n    }\n//------------------------------------------------------------------------------\n\n    inline bool IsMaxima(TEdge *e, const cInt Y)\n    {\n        return e && e->Top.Y == Y && !e->NextInLML;\n    }\n//------------------------------------------------------------------------------\n\n    inline bool IsIntermediate(TEdge *e, const cInt Y)\n    {\n        return e->Top.Y == Y && e->NextInLML;\n    }\n//------------------------------------------------------------------------------\n\n    TEdge *GetMaximaPair(TEdge *e)\n    {\n        if ((e->Next->Top == e->Top) && !e->Next->NextInLML)\n            return e->Next;\n        else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML)\n            return e->Prev;\n        else return 0;\n    }\n//------------------------------------------------------------------------------\n\n    TEdge *GetMaximaPairEx(TEdge *e)\n    {\n        //as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal)\n        TEdge* result = GetMaximaPair(e);\n        if (result && (result->OutIdx == Skip ||\n                       (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) return 0;\n        return result;\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2)\n    {\n        if(  !( Edge1->NextInSEL ) &&  !( Edge1->PrevInSEL ) ) return;\n        if(  !( Edge2->NextInSEL ) &&  !( Edge2->PrevInSEL ) ) return;\n\n        if(  Edge1->NextInSEL == Edge2 )\n        {\n            TEdge* Next = Edge2->NextInSEL;\n            if( Next ) Next->PrevInSEL = Edge1;\n            TEdge* Prev = Edge1->PrevInSEL;\n            if( Prev ) Prev->NextInSEL = Edge2;\n            Edge2->PrevInSEL = Prev;\n            Edge2->NextInSEL = Edge1;\n            Edge1->PrevInSEL = Edge2;\n            Edge1->NextInSEL = Next;\n        }\n        else if(  Edge2->NextInSEL == Edge1 )\n        {\n            TEdge* Next = Edge1->NextInSEL;\n            if( Next ) Next->PrevInSEL = Edge2;\n            TEdge* Prev = Edge2->PrevInSEL;\n            if( Prev ) Prev->NextInSEL = Edge1;\n            Edge1->PrevInSEL = Prev;\n            Edge1->NextInSEL = Edge2;\n            Edge2->PrevInSEL = Edge1;\n            Edge2->NextInSEL = Next;\n        }\n        else\n        {\n            TEdge* Next = Edge1->NextInSEL;\n            TEdge* Prev = Edge1->PrevInSEL;\n            Edge1->NextInSEL = Edge2->NextInSEL;\n            if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1;\n            Edge1->PrevInSEL = Edge2->PrevInSEL;\n            if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1;\n            Edge2->NextInSEL = Next;\n            if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2;\n            Edge2->PrevInSEL = Prev;\n            if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2;\n        }\n\n        if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1;\n        else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2;\n    }\n//------------------------------------------------------------------------------\n\n    TEdge* GetNextInAEL(TEdge *e, Direction dir)\n    {\n        return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL;\n    }\n//------------------------------------------------------------------------------\n\n    void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right)\n    {\n        if (HorzEdge.Bot.X < HorzEdge.Top.X)\n        {\n            Left = HorzEdge.Bot.X;\n            Right = HorzEdge.Top.X;\n            Dir = dLeftToRight;\n        } else\n        {\n            Left = HorzEdge.Top.X;\n            Right = HorzEdge.Bot.X;\n            Dir = dRightToLeft;\n        }\n    }\n//------------------------------------------------------------------------\n\n/*******************************************************************************\n* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or    *\n* Bottom of a scanbeam) are processed as if layered. The order in which HEs    *\n* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#]    *\n* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs),      *\n* and with other non-horizontal edges [*]. Once these intersections are        *\n* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into   *\n* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs.    *\n*******************************************************************************/\n\n    void Clipper::ProcessHorizontal(TEdge *horzEdge)\n    {\n        Direction dir;\n        cInt horzLeft, horzRight;\n        bool IsOpen = (horzEdge->WindDelta == 0);\n\n        GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);\n\n        TEdge* eLastHorz = horzEdge, *eMaxPair = 0;\n        while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML))\n            eLastHorz = eLastHorz->NextInLML;\n        if (!eLastHorz->NextInLML)\n            eMaxPair = GetMaximaPair(eLastHorz);\n\n        MaximaList::const_iterator maxIt;\n        MaximaList::const_reverse_iterator maxRit;\n        if (m_Maxima.size() > 0)\n        {\n            //get the first maxima in range (X) ...\n            if (dir == dLeftToRight)\n            {\n                maxIt = m_Maxima.begin();\n                while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++;\n                if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X)\n                    maxIt = m_Maxima.end();\n            }\n            else\n            {\n                maxRit = m_Maxima.rbegin();\n                while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++;\n                if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X)\n                    maxRit = m_Maxima.rend();\n            }\n        }\n\n        OutPt* op1 = 0;\n\n        for (;;) //loop through consec. horizontal edges\n        {\n\n            bool IsLastHorz = (horzEdge == eLastHorz);\n            TEdge* e = GetNextInAEL(horzEdge, dir);\n            while(e)\n            {\n\n                //this code block inserts extra coords into horizontal edges (in output\n                //polygons) whereever maxima touch these horizontal edges. This helps\n                //'simplifying' polygons (ie if the Simplify property is set).\n                if (m_Maxima.size() > 0)\n                {\n                    if (dir == dLeftToRight)\n                    {\n                        while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X)\n                        {\n                            if (horzEdge->OutIdx >= 0 && !IsOpen)\n                                AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y));\n                            maxIt++;\n                        }\n                    }\n                    else\n                    {\n                        while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X)\n                        {\n                            if (horzEdge->OutIdx >= 0 && !IsOpen)\n                                AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y));\n                            maxRit++;\n                        }\n                    }\n                };\n\n                if ((dir == dLeftToRight && e->Curr.X > horzRight) ||\n                    (dir == dRightToLeft && e->Curr.X < horzLeft)) break;\n\n                //Also break if we've got to the end of an intermediate horizontal edge ...\n                //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal.\n                if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML &&\n                    e->Dx < horzEdge->NextInLML->Dx) break;\n\n                if (horzEdge->OutIdx >= 0 && !IsOpen)  //note: may be done multiple times\n                {\n#ifdef use_xyz\n                    if (dir == dLeftToRight) SetZ(e->Curr, *horzEdge, *e);\n\t\t\telse SetZ(e->Curr, *e, *horzEdge);\n#endif\n                    op1 = AddOutPt(horzEdge, e->Curr);\n                    TEdge* eNextHorz = m_SortedEdges;\n                    while (eNextHorz)\n                    {\n                        if (eNextHorz->OutIdx >= 0 &&\n                            HorzSegmentsOverlap(horzEdge->Bot.X,\n                                                horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X))\n                        {\n                            OutPt* op2 = GetLastOutPt(eNextHorz);\n                            AddJoin(op2, op1, eNextHorz->Top);\n                        }\n                        eNextHorz = eNextHorz->NextInSEL;\n                    }\n                    AddGhostJoin(op1, horzEdge->Bot);\n                }\n\n                //OK, so far we're still in range of the horizontal Edge  but make sure\n                //we're at the last of consec. horizontals when matching with eMaxPair\n                if(e == eMaxPair && IsLastHorz)\n                {\n                    if (horzEdge->OutIdx >= 0)\n                        AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top);\n                    DeleteFromAEL(horzEdge);\n                    DeleteFromAEL(eMaxPair);\n                    return;\n                }\n\n                if(dir == dLeftToRight)\n                {\n                    IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);\n                    IntersectEdges(horzEdge, e, Pt);\n                }\n                else\n                {\n                    IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);\n                    IntersectEdges( e, horzEdge, Pt);\n                }\n                TEdge* eNext = GetNextInAEL(e, dir);\n                SwapPositionsInAEL( horzEdge, e );\n                e = eNext;\n            } //end while(e)\n\n            //Break out of loop if HorzEdge.NextInLML is not also horizontal ...\n            if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break;\n\n            UpdateEdgeIntoAEL(horzEdge);\n            if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot);\n            GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);\n\n        } //end for (;;)\n\n        if (horzEdge->OutIdx >= 0 && !op1)\n        {\n            op1 = GetLastOutPt(horzEdge);\n            TEdge* eNextHorz = m_SortedEdges;\n            while (eNextHorz)\n            {\n                if (eNextHorz->OutIdx >= 0 &&\n                    HorzSegmentsOverlap(horzEdge->Bot.X,\n                                        horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X))\n                {\n                    OutPt* op2 = GetLastOutPt(eNextHorz);\n                    AddJoin(op2, op1, eNextHorz->Top);\n                }\n                eNextHorz = eNextHorz->NextInSEL;\n            }\n            AddGhostJoin(op1, horzEdge->Top);\n        }\n\n        if (horzEdge->NextInLML)\n        {\n            if(horzEdge->OutIdx >= 0)\n            {\n                op1 = AddOutPt( horzEdge, horzEdge->Top);\n                UpdateEdgeIntoAEL(horzEdge);\n                if (horzEdge->WindDelta == 0) return;\n                //nb: HorzEdge is no longer horizontal here\n                TEdge* ePrev = horzEdge->PrevInAEL;\n                TEdge* eNext = horzEdge->NextInAEL;\n                if (ePrev && ePrev->Curr.X == horzEdge->Bot.X &&\n                    ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 &&\n                    (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y &&\n                     SlopesEqual(*horzEdge, *ePrev, m_UseFullRange)))\n                {\n                    OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot);\n                    AddJoin(op1, op2, horzEdge->Top);\n                }\n                else if (eNext && eNext->Curr.X == horzEdge->Bot.X &&\n                         eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 &&\n                         eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y &&\n                         SlopesEqual(*horzEdge, *eNext, m_UseFullRange))\n                {\n                    OutPt* op2 = AddOutPt(eNext, horzEdge->Bot);\n                    AddJoin(op1, op2, horzEdge->Top);\n                }\n            }\n            else\n                UpdateEdgeIntoAEL(horzEdge);\n        }\n        else\n        {\n            if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top);\n            DeleteFromAEL(horzEdge);\n        }\n    }\n//------------------------------------------------------------------------------\n\n    bool Clipper::ProcessIntersections(const cInt topY)\n    {\n        if( !m_ActiveEdges ) return true;\n        //try\n        {\n            BuildIntersectList(topY);\n            size_t IlSize = m_IntersectList.size();\n            if (IlSize == 0) return true;\n            if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList();\n            else return false;\n        }\n        //catch(...)\n        //{\n        //    m_SortedEdges = 0;\n        //    DisposeIntersectNodes();\n        //    throw clipperException(\"ProcessIntersections error\");\n        //}\n        m_SortedEdges = 0;\n        return true;\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::DisposeIntersectNodes()\n    {\n        for (size_t i = 0; i < m_IntersectList.size(); ++i )\n            delete m_IntersectList[i];\n        m_IntersectList.clear();\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::BuildIntersectList(const cInt topY)\n    {\n        if ( !m_ActiveEdges ) return;\n\n        //prepare for sorting ...\n        TEdge* e = m_ActiveEdges;\n        m_SortedEdges = e;\n        while( e )\n        {\n            e->PrevInSEL = e->PrevInAEL;\n            e->NextInSEL = e->NextInAEL;\n            e->Curr.X = TopX( *e, topY );\n            e = e->NextInAEL;\n        }\n\n        //bubblesort ...\n        bool isModified;\n        do\n        {\n            isModified = false;\n            e = m_SortedEdges;\n            while( e->NextInSEL )\n            {\n                TEdge *eNext = e->NextInSEL;\n                IntPoint Pt;\n                if(e->Curr.X > eNext->Curr.X)\n                {\n                    IntersectPoint(*e, *eNext, Pt);\n                    if (Pt.Y < topY) Pt = IntPoint(TopX(*e, topY), topY);\n                    IntersectNode * newNode = new IntersectNode;\n                    newNode->Edge1 = e;\n                    newNode->Edge2 = eNext;\n                    newNode->Pt = Pt;\n                    m_IntersectList.push_back(newNode);\n\n                    SwapPositionsInSEL(e, eNext);\n                    isModified = true;\n                }\n                else\n                    e = eNext;\n            }\n            if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0;\n            else break;\n        }\n        while ( isModified );\n        m_SortedEdges = 0; //important\n    }\n//------------------------------------------------------------------------------\n\n\n    void Clipper::ProcessIntersectList()\n    {\n        for (size_t i = 0; i < m_IntersectList.size(); ++i)\n        {\n            IntersectNode* iNode = m_IntersectList[i];\n            {\n                IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt);\n                SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 );\n            }\n            delete iNode;\n        }\n        m_IntersectList.clear();\n    }\n//------------------------------------------------------------------------------\n\n    bool IntersectListSort(IntersectNode* node1, IntersectNode* node2)\n    {\n        return node2->Pt.Y < node1->Pt.Y;\n    }\n//------------------------------------------------------------------------------\n\n    inline bool EdgesAdjacent(const IntersectNode &inode)\n    {\n        return (inode.Edge1->NextInSEL == inode.Edge2) ||\n               (inode.Edge1->PrevInSEL == inode.Edge2);\n    }\n//------------------------------------------------------------------------------\n\n    bool Clipper::FixupIntersectionOrder()\n    {\n        //pre-condition: intersections are sorted Bottom-most first.\n        //Now it's crucial that intersections are made only between adjacent edges,\n        //so to ensure this the order of intersections may need adjusting ...\n        CopyAELToSEL();\n        std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort);\n        size_t cnt = m_IntersectList.size();\n        for (size_t i = 0; i < cnt; ++i)\n        {\n            if (!EdgesAdjacent(*m_IntersectList[i]))\n            {\n                size_t j = i + 1;\n                while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++;\n                if (j == cnt)  return false;\n                std::swap(m_IntersectList[i], m_IntersectList[j]);\n            }\n            SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2);\n        }\n        return true;\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::DoMaxima(TEdge *e)\n    {\n        TEdge* eMaxPair = GetMaximaPairEx(e);\n        if (!eMaxPair)\n        {\n            if (e->OutIdx >= 0)\n                AddOutPt(e, e->Top);\n            DeleteFromAEL(e);\n            return;\n        }\n\n        TEdge* eNext = e->NextInAEL;\n        while(eNext && eNext != eMaxPair)\n        {\n            IntersectEdges(e, eNext, e->Top);\n            SwapPositionsInAEL(e, eNext);\n            eNext = e->NextInAEL;\n        }\n\n        if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned)\n        {\n            DeleteFromAEL(e);\n            DeleteFromAEL(eMaxPair);\n        }\n        else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 )\n        {\n            if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top);\n            DeleteFromAEL(e);\n            DeleteFromAEL(eMaxPair);\n        }\n#ifdef use_lines\n        else if (e->WindDelta == 0)\n        {\n            if (e->OutIdx >= 0)\n            {\n                AddOutPt(e, e->Top);\n                e->OutIdx = Unassigned;\n            }\n            DeleteFromAEL(e);\n\n            if (eMaxPair->OutIdx >= 0)\n            {\n                AddOutPt(eMaxPair, e->Top);\n                eMaxPair->OutIdx = Unassigned;\n            }\n            DeleteFromAEL(eMaxPair);\n        }\n#endif\n        else return;//throw clipperException(\"DoMaxima error\");\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)\n    {\n        TEdge* e = m_ActiveEdges;\n        while( e )\n        {\n            //1. process maxima, treating them as if they're 'bent' horizontal edges,\n            //   but exclude maxima with horizontal edges. nb: e can't be a horizontal.\n            bool IsMaximaEdge = IsMaxima(e, topY);\n\n            if(IsMaximaEdge)\n            {\n                TEdge* eMaxPair = GetMaximaPairEx(e);\n                IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair));\n            }\n\n            if(IsMaximaEdge)\n            {\n                if (m_StrictSimple) m_Maxima.push_back(e->Top.X);\n                TEdge* ePrev = e->PrevInAEL;\n                DoMaxima(e);\n                if( !ePrev ) e = m_ActiveEdges;\n                else e = ePrev->NextInAEL;\n            }\n            else\n            {\n                //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ...\n                if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML))\n                {\n                    UpdateEdgeIntoAEL(e);\n                    if (e->OutIdx >= 0)\n                        AddOutPt(e, e->Bot);\n                    AddEdgeToSEL(e);\n                }\n                else\n                {\n                    e->Curr.X = TopX( *e, topY );\n                    e->Curr.Y = topY;\n#ifdef use_xyz\n                    e->Curr.Z = topY == e->Top.Y ? e->Top.Z : (topY == e->Bot.Y ? e->Bot.Z : 0);\n#endif\n                }\n\n                //When StrictlySimple and 'e' is being touched by another edge, then\n                //make sure both edges have a vertex here ...\n                if (m_StrictSimple)\n                {\n                    TEdge* ePrev = e->PrevInAEL;\n                    if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) &&\n                        (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0))\n                    {\n                        IntPoint pt = e->Curr;\n#ifdef use_xyz\n                        SetZ(pt, *ePrev, *e);\n#endif\n                        OutPt* op = AddOutPt(ePrev, pt);\n                        OutPt* op2 = AddOutPt(e, pt);\n                        AddJoin(op, op2, pt); //StrictlySimple (type-3) join\n                    }\n                }\n\n                e = e->NextInAEL;\n            }\n        }\n\n        //3. Process horizontals at the Top of the scanbeam ...\n        m_Maxima.sort();\n        ProcessHorizontals();\n        m_Maxima.clear();\n\n        //4. Promote intermediate vertices ...\n        e = m_ActiveEdges;\n        while(e)\n        {\n            if(IsIntermediate(e, topY))\n            {\n                OutPt* op = 0;\n                if( e->OutIdx >= 0 )\n                    op = AddOutPt(e, e->Top);\n                UpdateEdgeIntoAEL(e);\n\n                //if output polygons share an edge, they'll need joining later ...\n                TEdge* ePrev = e->PrevInAEL;\n                TEdge* eNext = e->NextInAEL;\n                if (ePrev && ePrev->Curr.X == e->Bot.X &&\n                    ePrev->Curr.Y == e->Bot.Y && op &&\n                    ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y &&\n                    SlopesEqual(e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange) &&\n                    (e->WindDelta != 0) && (ePrev->WindDelta != 0))\n                {\n                    OutPt* op2 = AddOutPt(ePrev, e->Bot);\n                    AddJoin(op, op2, e->Top);\n                }\n                else if (eNext && eNext->Curr.X == e->Bot.X &&\n                         eNext->Curr.Y == e->Bot.Y && op &&\n                         eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y &&\n                         SlopesEqual(e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange) &&\n                         (e->WindDelta != 0) && (eNext->WindDelta != 0))\n                {\n                    OutPt* op2 = AddOutPt(eNext, e->Bot);\n                    AddJoin(op, op2, e->Top);\n                }\n            }\n            e = e->NextInAEL;\n        }\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::FixupOutPolyline(OutRec &outrec)\n    {\n        OutPt *pp = outrec.Pts;\n        OutPt *lastPP = pp->Prev;\n        while (pp != lastPP)\n        {\n            pp = pp->Next;\n            if (pp->Pt == pp->Prev->Pt)\n            {\n                if (pp == lastPP) lastPP = pp->Prev;\n                OutPt *tmpPP = pp->Prev;\n                tmpPP->Next = pp->Next;\n                pp->Next->Prev = tmpPP;\n                delete pp;\n                pp = tmpPP;\n            }\n        }\n\n        if (pp == pp->Prev)\n        {\n            DisposeOutPts(pp);\n            outrec.Pts = 0;\n            return;\n        }\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::FixupOutPolygon(OutRec &outrec)\n    {\n        //FixupOutPolygon() - removes duplicate points and simplifies consecutive\n        //parallel edges by removing the middle vertex.\n        OutPt *lastOK = 0;\n        outrec.BottomPt = 0;\n        OutPt *pp = outrec.Pts;\n        bool preserveCol = m_PreserveCollinear || m_StrictSimple;\n\n        for (;;)\n        {\n            if (pp->Prev == pp || pp->Prev == pp->Next)\n            {\n                DisposeOutPts(pp);\n                outrec.Pts = 0;\n                return;\n            }\n\n            //test for duplicate points and collinear edges ...\n            if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) ||\n                (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) &&\n                 (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt))))\n            {\n                lastOK = 0;\n                OutPt *tmp = pp;\n                pp->Prev->Next = pp->Next;\n                pp->Next->Prev = pp->Prev;\n                pp = pp->Prev;\n                delete tmp;\n            }\n            else if (pp == lastOK) break;\n            else\n            {\n                if (!lastOK) lastOK = pp;\n                pp = pp->Next;\n            }\n        }\n        outrec.Pts = pp;\n    }\n//------------------------------------------------------------------------------\n\n    int PointCount(OutPt *Pts)\n    {\n        if (!Pts) return 0;\n        int result = 0;\n        OutPt* p = Pts;\n        do\n        {\n            result++;\n            p = p->Next;\n        }\n        while (p != Pts);\n        return result;\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::BuildResult(Paths &polys)\n    {\n        polys.reserve(m_PolyOuts.size());\n        for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)\n        {\n            if (!m_PolyOuts[i]->Pts) continue;\n            Path pg;\n            OutPt* p = m_PolyOuts[i]->Pts->Prev;\n            int cnt = PointCount(p);\n            if (cnt < 2) continue;\n            pg.reserve(cnt);\n            for (int i = 0; i < cnt; ++i)\n            {\n                pg.push_back(p->Pt);\n                p = p->Prev;\n            }\n            polys.push_back(pg);\n        }\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::BuildResult2(PolyTree& polytree)\n    {\n        polytree.Clear();\n        polytree.AllNodes.reserve(m_PolyOuts.size());\n        //add each output polygon/contour to polytree ...\n        for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++)\n        {\n            OutRec* outRec = m_PolyOuts[i];\n            int cnt = PointCount(outRec->Pts);\n            if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue;\n            FixHoleLinkage(*outRec);\n            PolyNode* pn = new PolyNode();\n            //nb: polytree takes ownership of all the PolyNodes\n            polytree.AllNodes.push_back(pn);\n            outRec->PolyNd = pn;\n            pn->Parent = 0;\n            pn->Index = 0;\n            pn->Contour.reserve(cnt);\n            OutPt *op = outRec->Pts->Prev;\n            for (int j = 0; j < cnt; j++)\n            {\n                pn->Contour.push_back(op->Pt);\n                op = op->Prev;\n            }\n        }\n\n        //fixup PolyNode links etc ...\n        polytree.Childs.reserve(m_PolyOuts.size());\n        for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++)\n        {\n            OutRec* outRec = m_PolyOuts[i];\n            if (!outRec->PolyNd) continue;\n            if (outRec->IsOpen)\n            {\n                outRec->PolyNd->m_IsOpen = true;\n                polytree.AddChild(*outRec->PolyNd);\n            }\n            else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd)\n                outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd);\n            else\n                polytree.AddChild(*outRec->PolyNd);\n        }\n    }\n//------------------------------------------------------------------------------\n\n    void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2)\n    {\n        //just swap the contents (because fIntersectNodes is a single-linked-list)\n        IntersectNode inode = int1; //gets a copy of Int1\n        int1.Edge1 = int2.Edge1;\n        int1.Edge2 = int2.Edge2;\n        int1.Pt = int2.Pt;\n        int2.Edge1 = inode.Edge1;\n        int2.Edge2 = inode.Edge2;\n        int2.Pt = inode.Pt;\n    }\n//------------------------------------------------------------------------------\n\n    inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2)\n    {\n        if (e2.Curr.X == e1.Curr.X)\n        {\n            if (e2.Top.Y > e1.Top.Y)\n                return e2.Top.X < TopX(e1, e2.Top.Y);\n            else return e1.Top.X > TopX(e2, e1.Top.Y);\n        }\n        else return e2.Curr.X < e1.Curr.X;\n    }\n//------------------------------------------------------------------------------\n\n    bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2,\n                    cInt& Left, cInt& Right)\n    {\n        if (a1 < a2)\n        {\n            if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);}\n            else {Left = std::max(a1,b2); Right = std::min(a2,b1);}\n        }\n        else\n        {\n            if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);}\n            else {Left = std::max(a2,b2); Right = std::min(a1,b1);}\n        }\n        return Left < Right;\n    }\n//------------------------------------------------------------------------------\n\n    inline void UpdateOutPtIdxs(OutRec& outrec)\n    {\n        OutPt* op = outrec.Pts;\n        do\n        {\n            op->Idx = outrec.Idx;\n            op = op->Prev;\n        }\n        while(op != outrec.Pts);\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge)\n    {\n        if(!m_ActiveEdges)\n        {\n            edge->PrevInAEL = 0;\n            edge->NextInAEL = 0;\n            m_ActiveEdges = edge;\n        }\n        else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge))\n        {\n            edge->PrevInAEL = 0;\n            edge->NextInAEL = m_ActiveEdges;\n            m_ActiveEdges->PrevInAEL = edge;\n            m_ActiveEdges = edge;\n        }\n        else\n        {\n            if(!startEdge) startEdge = m_ActiveEdges;\n            while(startEdge->NextInAEL  &&\n                  !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge))\n                startEdge = startEdge->NextInAEL;\n            edge->NextInAEL = startEdge->NextInAEL;\n            if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge;\n            edge->PrevInAEL = startEdge;\n            startEdge->NextInAEL = edge;\n        }\n    }\n//----------------------------------------------------------------------\n\n    OutPt* DupOutPt(OutPt* outPt, bool InsertAfter)\n    {\n        OutPt* result = new OutPt;\n        result->Pt = outPt->Pt;\n        result->Idx = outPt->Idx;\n        if (InsertAfter)\n        {\n            result->Next = outPt->Next;\n            result->Prev = outPt;\n            outPt->Next->Prev = result;\n            outPt->Next = result;\n        }\n        else\n        {\n            result->Prev = outPt->Prev;\n            result->Next = outPt;\n            outPt->Prev->Next = result;\n            outPt->Prev = result;\n        }\n        return result;\n    }\n//------------------------------------------------------------------------------\n\n    bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b,\n                  const IntPoint Pt, bool DiscardLeft)\n    {\n        Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight);\n        Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight);\n        if (Dir1 == Dir2) return false;\n\n        //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we\n        //want Op1b to be on the Right. (And likewise with Op2 and Op2b.)\n        //So, to facilitate this while inserting Op1b and Op2b ...\n        //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b,\n        //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.)\n        if (Dir1 == dLeftToRight)\n        {\n            while (op1->Next->Pt.X <= Pt.X &&\n                   op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y)\n                op1 = op1->Next;\n            if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next;\n            op1b = DupOutPt(op1, !DiscardLeft);\n            if (op1b->Pt != Pt)\n            {\n                op1 = op1b;\n                op1->Pt = Pt;\n                op1b = DupOutPt(op1, !DiscardLeft);\n            }\n        }\n        else\n        {\n            while (op1->Next->Pt.X >= Pt.X &&\n                   op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y)\n                op1 = op1->Next;\n            if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next;\n            op1b = DupOutPt(op1, DiscardLeft);\n            if (op1b->Pt != Pt)\n            {\n                op1 = op1b;\n                op1->Pt = Pt;\n                op1b = DupOutPt(op1, DiscardLeft);\n            }\n        }\n\n        if (Dir2 == dLeftToRight)\n        {\n            while (op2->Next->Pt.X <= Pt.X &&\n                   op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y)\n                op2 = op2->Next;\n            if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next;\n            op2b = DupOutPt(op2, !DiscardLeft);\n            if (op2b->Pt != Pt)\n            {\n                op2 = op2b;\n                op2->Pt = Pt;\n                op2b = DupOutPt(op2, !DiscardLeft);\n            };\n        } else\n        {\n            while (op2->Next->Pt.X >= Pt.X &&\n                   op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y)\n                op2 = op2->Next;\n            if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next;\n            op2b = DupOutPt(op2, DiscardLeft);\n            if (op2b->Pt != Pt)\n            {\n                op2 = op2b;\n                op2->Pt = Pt;\n                op2b = DupOutPt(op2, DiscardLeft);\n            };\n        };\n\n        if ((Dir1 == dLeftToRight) == DiscardLeft)\n        {\n            op1->Prev = op2;\n            op2->Next = op1;\n            op1b->Next = op2b;\n            op2b->Prev = op1b;\n        }\n        else\n        {\n            op1->Next = op2;\n            op2->Prev = op1;\n            op1b->Prev = op2b;\n            op2b->Next = op1b;\n        }\n        return true;\n    }\n//------------------------------------------------------------------------------\n\n    bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)\n    {\n        OutPt *op1 = j->OutPt1, *op1b;\n        OutPt *op2 = j->OutPt2, *op2b;\n\n        //There are 3 kinds of joins for output polygons ...\n        //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere\n        //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal).\n        //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same\n        //location at the Bottom of the overlapping segment (& Join.OffPt is above).\n        //3. StrictSimple joins where edges touch but are not collinear and where\n        //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point.\n        bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y);\n\n        if (isHorizontal  && (j->OffPt == j->OutPt1->Pt) &&\n            (j->OffPt == j->OutPt2->Pt))\n        {\n            //Strictly Simple join ...\n            if (outRec1 != outRec2) return false;\n            op1b = j->OutPt1->Next;\n            while (op1b != op1 && (op1b->Pt == j->OffPt))\n                op1b = op1b->Next;\n            bool reverse1 = (op1b->Pt.Y > j->OffPt.Y);\n            op2b = j->OutPt2->Next;\n            while (op2b != op2 && (op2b->Pt == j->OffPt))\n                op2b = op2b->Next;\n            bool reverse2 = (op2b->Pt.Y > j->OffPt.Y);\n            if (reverse1 == reverse2) return false;\n            if (reverse1)\n            {\n                op1b = DupOutPt(op1, false);\n                op2b = DupOutPt(op2, true);\n                op1->Prev = op2;\n                op2->Next = op1;\n                op1b->Next = op2b;\n                op2b->Prev = op1b;\n                j->OutPt1 = op1;\n                j->OutPt2 = op1b;\n                return true;\n            } else\n            {\n                op1b = DupOutPt(op1, true);\n                op2b = DupOutPt(op2, false);\n                op1->Next = op2;\n                op2->Prev = op1;\n                op1b->Prev = op2b;\n                op2b->Next = op1b;\n                j->OutPt1 = op1;\n                j->OutPt2 = op1b;\n                return true;\n            }\n        }\n        else if (isHorizontal)\n        {\n            //treat horizontal joins differently to non-horizontal joins since with\n            //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt\n            //may be anywhere along the horizontal edge.\n            op1b = op1;\n            while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2)\n                op1 = op1->Prev;\n            while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2)\n                op1b = op1b->Next;\n            if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon'\n\n            op2b = op2;\n            while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b)\n                op2 = op2->Prev;\n            while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1)\n                op2b = op2b->Next;\n            if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon'\n\n            cInt Left, Right;\n            //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges\n            if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right))\n                return false;\n\n            //DiscardLeftSide: when overlapping edges are joined, a spike will created\n            //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up\n            //on the discard Side as either may still be needed for other joins ...\n            IntPoint Pt;\n            bool DiscardLeftSide;\n            if (op1->Pt.X >= Left && op1->Pt.X <= Right)\n            {\n                Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X);\n            }\n            else if (op2->Pt.X >= Left&& op2->Pt.X <= Right)\n            {\n                Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X);\n            }\n            else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right)\n            {\n                Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X;\n            }\n            else\n            {\n                Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X);\n            }\n            j->OutPt1 = op1; j->OutPt2 = op2;\n            return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide);\n        } else\n        {\n            //nb: For non-horizontal joins ...\n            //    1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y\n            //    2. Jr.OutPt1.Pt > Jr.OffPt.Y\n\n            //make sure the polygons are correctly oriented ...\n            op1b = op1->Next;\n            while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next;\n            bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) ||\n                             !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange));\n            if (Reverse1)\n            {\n                op1b = op1->Prev;\n                while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev;\n                if ((op1b->Pt.Y > op1->Pt.Y) ||\n                    !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false;\n            };\n            op2b = op2->Next;\n            while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next;\n            bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) ||\n                             !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange));\n            if (Reverse2)\n            {\n                op2b = op2->Prev;\n                while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev;\n                if ((op2b->Pt.Y > op2->Pt.Y) ||\n                    !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false;\n            }\n\n            if ((op1b == op1) || (op2b == op2) || (op1b == op2b) ||\n                ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false;\n\n            if (Reverse1)\n            {\n                op1b = DupOutPt(op1, false);\n                op2b = DupOutPt(op2, true);\n                op1->Prev = op2;\n                op2->Next = op1;\n                op1b->Next = op2b;\n                op2b->Prev = op1b;\n                j->OutPt1 = op1;\n                j->OutPt2 = op1b;\n                return true;\n            } else\n            {\n                op1b = DupOutPt(op1, true);\n                op2b = DupOutPt(op2, false);\n                op1->Next = op2;\n                op2->Prev = op1;\n                op1b->Prev = op2b;\n                op2b->Next = op1b;\n                j->OutPt1 = op1;\n                j->OutPt2 = op1b;\n                return true;\n            }\n        }\n    }\n//----------------------------------------------------------------------\n\n    static OutRec* ParseFirstLeft(OutRec* FirstLeft)\n    {\n        while (FirstLeft && !FirstLeft->Pts)\n            FirstLeft = FirstLeft->FirstLeft;\n        return FirstLeft;\n    }\n//------------------------------------------------------------------------------\n\n    void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec)\n    {\n        //tests if NewOutRec contains the polygon before reassigning FirstLeft\n        for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)\n        {\n            OutRec* outRec = m_PolyOuts[i];\n            OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft);\n            if (outRec->Pts  && firstLeft == OldOutRec)\n            {\n                if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts))\n                    outRec->FirstLeft = NewOutRec;\n            }\n        }\n    }\n//----------------------------------------------------------------------\n\n    void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec)\n    {\n        //A polygon has split into two such that one is now the inner of the other.\n        //It's possible that these polygons now wrap around other polygons, so check\n        //every polygon that's also contained by OuterOutRec's FirstLeft container\n        //(including 0) to see if they've become inner to the new inner polygon ...\n        OutRec* orfl = OuterOutRec->FirstLeft;\n        for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)\n        {\n            OutRec* outRec = m_PolyOuts[i];\n\n            if (!outRec->Pts || outRec == OuterOutRec || outRec == InnerOutRec)\n                continue;\n            OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft);\n            if (firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec)\n                continue;\n            if (Poly2ContainsPoly1(outRec->Pts, InnerOutRec->Pts))\n                outRec->FirstLeft = InnerOutRec;\n            else if (Poly2ContainsPoly1(outRec->Pts, OuterOutRec->Pts))\n                outRec->FirstLeft = OuterOutRec;\n            else if (outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec)\n                outRec->FirstLeft = orfl;\n        }\n    }\n//----------------------------------------------------------------------\n    void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec)\n    {\n        //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon\n        for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)\n        {\n            OutRec* outRec = m_PolyOuts[i];\n            OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft);\n            if (outRec->Pts && firstLeft == OldOutRec)\n                outRec->FirstLeft = NewOutRec;\n        }\n    }\n//----------------------------------------------------------------------\n\n    void Clipper::JoinCommonEdges()\n    {\n        for (JoinList::size_type i = 0; i < m_Joins.size(); i++)\n        {\n            Join* join = m_Joins[i];\n\n            OutRec *outRec1 = GetOutRec(join->OutPt1->Idx);\n            OutRec *outRec2 = GetOutRec(join->OutPt2->Idx);\n\n            if (!outRec1->Pts || !outRec2->Pts) continue;\n            if (outRec1->IsOpen || outRec2->IsOpen) continue;\n\n            //get the polygon fragment with the correct hole state (FirstLeft)\n            //before calling JoinPoints() ...\n            OutRec *holeStateRec;\n            if (outRec1 == outRec2) holeStateRec = outRec1;\n            else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2;\n            else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1;\n            else holeStateRec = GetLowermostRec(outRec1, outRec2);\n\n            if (!JoinPoints(join, outRec1, outRec2)) continue;\n\n            if (outRec1 == outRec2)\n            {\n                //instead of joining two polygons, we've just created a new one by\n                //splitting one polygon into two.\n                outRec1->Pts = join->OutPt1;\n                outRec1->BottomPt = 0;\n                outRec2 = CreateOutRec();\n                outRec2->Pts = join->OutPt2;\n\n                //update all OutRec2.Pts Idx's ...\n                UpdateOutPtIdxs(*outRec2);\n\n                if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts))\n                {\n                    //outRec1 contains outRec2 ...\n                    outRec2->IsHole = !outRec1->IsHole;\n                    outRec2->FirstLeft = outRec1;\n\n                    if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1);\n\n                    if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0))\n                        ReversePolyPtLinks(outRec2->Pts);\n\n                } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts))\n                {\n                    //outRec2 contains outRec1 ...\n                    outRec2->IsHole = outRec1->IsHole;\n                    outRec1->IsHole = !outRec2->IsHole;\n                    outRec2->FirstLeft = outRec1->FirstLeft;\n                    outRec1->FirstLeft = outRec2;\n\n                    if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2);\n\n                    if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0))\n                        ReversePolyPtLinks(outRec1->Pts);\n                }\n                else\n                {\n                    //the 2 polygons are completely separate ...\n                    outRec2->IsHole = outRec1->IsHole;\n                    outRec2->FirstLeft = outRec1->FirstLeft;\n\n                    //fixup FirstLeft pointers that may need reassigning to OutRec2\n                    if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2);\n                }\n\n            } else\n            {\n                //joined 2 polygons together ...\n\n                outRec2->Pts = 0;\n                outRec2->BottomPt = 0;\n                outRec2->Idx = outRec1->Idx;\n\n                outRec1->IsHole = holeStateRec->IsHole;\n                if (holeStateRec == outRec2)\n                    outRec1->FirstLeft = outRec2->FirstLeft;\n                outRec2->FirstLeft = outRec1;\n\n                if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1);\n            }\n        }\n    }\n\n//------------------------------------------------------------------------------\n// ClipperOffset support functions ...\n//------------------------------------------------------------------------------\n\n    DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2)\n    {\n        if(pt2.X == pt1.X && pt2.Y == pt1.Y)\n            return DoublePoint(0, 0);\n\n        double Dx = (double)(pt2.X - pt1.X);\n        double dy = (double)(pt2.Y - pt1.Y);\n        double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy );\n        Dx *= f;\n        dy *= f;\n        return DoublePoint(dy, -Dx);\n    }\n\n//------------------------------------------------------------------------------\n// ClipperOffset class\n//------------------------------------------------------------------------------\n\n    ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance)\n    {\n        this->MiterLimit = miterLimit;\n        this->ArcTolerance = arcTolerance;\n        m_lowest.X = -1;\n    }\n//------------------------------------------------------------------------------\n\n    ClipperOffset::~ClipperOffset()\n    {\n        Clear();\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperOffset::Clear()\n    {\n        for (int i = 0; i < m_polyNodes.ChildCount(); ++i)\n            delete m_polyNodes.Childs[i];\n        m_polyNodes.Childs.clear();\n        m_lowest.X = -1;\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType)\n    {\n        int highI = (int)path.size() - 1;\n        if (highI < 0) return;\n        PolyNode* newNode = new PolyNode();\n        newNode->m_jointype = joinType;\n        newNode->m_endtype = endType;\n\n        //strip duplicate points from path and also get index to the lowest point ...\n        if (endType == etClosedLine || endType == etClosedPolygon)\n            while (highI > 0 && path[0] == path[highI]) highI--;\n        newNode->Contour.reserve(highI + 1);\n        newNode->Contour.push_back(path[0]);\n        int j = 0, k = 0;\n        for (int i = 1; i <= highI; i++)\n            if (newNode->Contour[j] != path[i])\n            {\n                j++;\n                newNode->Contour.push_back(path[i]);\n                if (path[i].Y > newNode->Contour[k].Y ||\n                    (path[i].Y == newNode->Contour[k].Y &&\n                     path[i].X < newNode->Contour[k].X)) k = j;\n            }\n        if (endType == etClosedPolygon && j < 2)\n        {\n            delete newNode;\n            return;\n        }\n        m_polyNodes.AddChild(*newNode);\n\n        //if this path's lowest pt is lower than all the others then update m_lowest\n        if (endType != etClosedPolygon) return;\n        if (m_lowest.X < 0)\n            m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k);\n        else\n        {\n            IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y];\n            if (newNode->Contour[k].Y > ip.Y ||\n                (newNode->Contour[k].Y == ip.Y &&\n                 newNode->Contour[k].X < ip.X))\n                m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k);\n        }\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType)\n    {\n        for (Paths::size_type i = 0; i < paths.size(); ++i)\n            AddPath(paths[i], joinType, endType);\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperOffset::FixOrientations()\n    {\n        //fixup orientations of all closed paths if the orientation of the\n        //closed path with the lowermost vertex is wrong ...\n        if (m_lowest.X >= 0 &&\n            !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour))\n        {\n            for (int i = 0; i < m_polyNodes.ChildCount(); ++i)\n            {\n                PolyNode& node = *m_polyNodes.Childs[i];\n                if (node.m_endtype == etClosedPolygon ||\n                    (node.m_endtype == etClosedLine && Orientation(node.Contour)))\n                    ReversePath(node.Contour);\n            }\n        } else\n        {\n            for (int i = 0; i < m_polyNodes.ChildCount(); ++i)\n            {\n                PolyNode& node = *m_polyNodes.Childs[i];\n                if (node.m_endtype == etClosedLine && !Orientation(node.Contour))\n                    ReversePath(node.Contour);\n            }\n        }\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperOffset::Execute(Paths& solution, double delta)\n    {\n        solution.clear();\n        FixOrientations();\n        DoOffset(delta);\n\n        //now clean up 'corners' ...\n        Clipper clpr;\n        clpr.AddPaths(m_destPolys, ptSubject, true);\n        if (delta > 0)\n        {\n            clpr.Execute(ctUnion, solution, pftPositive, pftPositive);\n        }\n        else\n        {\n            IntRect r = clpr.GetBounds();\n            Path outer(4);\n            outer[0] = IntPoint(r.left - 10, r.bottom + 10);\n            outer[1] = IntPoint(r.right + 10, r.bottom + 10);\n            outer[2] = IntPoint(r.right + 10, r.top - 10);\n            outer[3] = IntPoint(r.left - 10, r.top - 10);\n\n            clpr.AddPath(outer, ptSubject, true);\n            clpr.ReverseSolution(true);\n            clpr.Execute(ctUnion, solution, pftNegative, pftNegative);\n            if (solution.size() > 0) solution.erase(solution.begin());\n        }\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperOffset::Execute(PolyTree& solution, double delta)\n    {\n        solution.Clear();\n        FixOrientations();\n        DoOffset(delta);\n\n        //now clean up 'corners' ...\n        Clipper clpr;\n        clpr.AddPaths(m_destPolys, ptSubject, true);\n        if (delta > 0)\n        {\n            clpr.Execute(ctUnion, solution, pftPositive, pftPositive);\n        }\n        else\n        {\n            IntRect r = clpr.GetBounds();\n            Path outer(4);\n            outer[0] = IntPoint(r.left - 10, r.bottom + 10);\n            outer[1] = IntPoint(r.right + 10, r.bottom + 10);\n            outer[2] = IntPoint(r.right + 10, r.top - 10);\n            outer[3] = IntPoint(r.left - 10, r.top - 10);\n\n            clpr.AddPath(outer, ptSubject, true);\n            clpr.ReverseSolution(true);\n            clpr.Execute(ctUnion, solution, pftNegative, pftNegative);\n            //remove the outer PolyNode rectangle ...\n            if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0)\n            {\n                PolyNode* outerNode = solution.Childs[0];\n                solution.Childs.reserve(outerNode->ChildCount());\n                solution.Childs[0] = outerNode->Childs[0];\n                solution.Childs[0]->Parent = outerNode->Parent;\n                for (int i = 1; i < outerNode->ChildCount(); ++i)\n                    solution.AddChild(*outerNode->Childs[i]);\n            }\n            else\n                solution.Clear();\n        }\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperOffset::DoOffset(double delta)\n    {\n        m_destPolys.clear();\n        m_delta = delta;\n\n        //if Zero offset, just copy any CLOSED polygons to m_p and return ...\n        if (NEAR_ZERO(delta))\n        {\n            m_destPolys.reserve(m_polyNodes.ChildCount());\n            for (int i = 0; i < m_polyNodes.ChildCount(); i++)\n            {\n                PolyNode& node = *m_polyNodes.Childs[i];\n                if (node.m_endtype == etClosedPolygon)\n                    m_destPolys.push_back(node.Contour);\n            }\n            return;\n        }\n\n        //see offset_triginometry3.svg in the documentation folder ...\n        if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit);\n        else m_miterLim = 0.5;\n\n        double y;\n        if (ArcTolerance <= 0.0) y = def_arc_tolerance;\n        else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance)\n            y = std::fabs(delta) * def_arc_tolerance;\n        else y = ArcTolerance;\n        //see offset_triginometry2.svg in the documentation folder ...\n        double steps = pi / std::acos(1 - y / std::fabs(delta));\n        if (steps > std::fabs(delta) * pi)\n            steps = std::fabs(delta) * pi;  //ie excessive precision check\n        m_sin = std::sin(two_pi / steps);\n        m_cos = std::cos(two_pi / steps);\n        m_StepsPerRad = steps / two_pi;\n        if (delta < 0.0) m_sin = -m_sin;\n\n        m_destPolys.reserve(m_polyNodes.ChildCount() * 2);\n        for (int i = 0; i < m_polyNodes.ChildCount(); i++)\n        {\n            PolyNode& node = *m_polyNodes.Childs[i];\n            m_srcPoly = node.Contour;\n\n            int len = (int)m_srcPoly.size();\n            if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon)))\n                continue;\n\n            m_destPoly.clear();\n            if (len == 1)\n            {\n                if (node.m_jointype == jtRound)\n                {\n                    double X = 1.0, Y = 0.0;\n                    for (cInt j = 1; j <= steps; j++)\n                    {\n                        m_destPoly.push_back(IntPoint(\n                                Round(m_srcPoly[0].X + X * delta),\n                                Round(m_srcPoly[0].Y + Y * delta)));\n                        double X2 = X;\n                        X = X * m_cos - m_sin * Y;\n                        Y = X2 * m_sin + Y * m_cos;\n                    }\n                }\n                else\n                {\n                    double X = -1.0, Y = -1.0;\n                    for (int j = 0; j < 4; ++j)\n                    {\n                        m_destPoly.push_back(IntPoint(\n                                Round(m_srcPoly[0].X + X * delta),\n                                Round(m_srcPoly[0].Y + Y * delta)));\n                        if (X < 0) X = 1;\n                        else if (Y < 0) Y = 1;\n                        else X = -1;\n                    }\n                }\n                m_destPolys.push_back(m_destPoly);\n                continue;\n            }\n            //build m_normals ...\n            m_normals.clear();\n            m_normals.reserve(len);\n            for (int j = 0; j < len - 1; ++j)\n                m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1]));\n            if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon)\n                m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0]));\n            else\n                m_normals.push_back(DoublePoint(m_normals[len - 2]));\n\n            if (node.m_endtype == etClosedPolygon)\n            {\n                int k = len - 1;\n                for (int j = 0; j < len; ++j)\n                    OffsetPoint(j, k, node.m_jointype);\n                m_destPolys.push_back(m_destPoly);\n            }\n            else if (node.m_endtype == etClosedLine)\n            {\n                int k = len - 1;\n                for (int j = 0; j < len; ++j)\n                    OffsetPoint(j, k, node.m_jointype);\n                m_destPolys.push_back(m_destPoly);\n                m_destPoly.clear();\n                //re-build m_normals ...\n                DoublePoint n = m_normals[len -1];\n                for (int j = len - 1; j > 0; j--)\n                    m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y);\n                m_normals[0] = DoublePoint(-n.X, -n.Y);\n                k = 0;\n                for (int j = len - 1; j >= 0; j--)\n                    OffsetPoint(j, k, node.m_jointype);\n                m_destPolys.push_back(m_destPoly);\n            }\n            else\n            {\n                int k = 0;\n                for (int j = 1; j < len - 1; ++j)\n                    OffsetPoint(j, k, node.m_jointype);\n\n                IntPoint pt1;\n                if (node.m_endtype == etOpenButt)\n                {\n                    int j = len - 1;\n                    pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X *\n                                                                delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta));\n                    m_destPoly.push_back(pt1);\n                    pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X *\n                                                                delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta));\n                    m_destPoly.push_back(pt1);\n                }\n                else\n                {\n                    int j = len - 1;\n                    k = len - 2;\n                    m_sinA = 0;\n                    m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y);\n                    if (node.m_endtype == etOpenSquare)\n                        DoSquare(j, k);\n                    else\n                        DoRound(j, k);\n                }\n\n                //re-build m_normals ...\n                for (int j = len - 1; j > 0; j--)\n                    m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y);\n                m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y);\n\n                k = len - 1;\n                for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype);\n\n                if (node.m_endtype == etOpenButt)\n                {\n                    pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta),\n                                   (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta));\n                    m_destPoly.push_back(pt1);\n                    pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta),\n                                   (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta));\n                    m_destPoly.push_back(pt1);\n                }\n                else\n                {\n                    k = 1;\n                    m_sinA = 0;\n                    if (node.m_endtype == etOpenSquare)\n                        DoSquare(0, 1);\n                    else\n                        DoRound(0, 1);\n                }\n                m_destPolys.push_back(m_destPoly);\n            }\n        }\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype)\n    {\n        //cross product ...\n        m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y);\n        if (std::fabs(m_sinA * m_delta) < 1.0)\n        {\n            //dot product ...\n            double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y );\n            if (cosA > 0) // angle => 0 degrees\n            {\n                m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta),\n                                              Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta)));\n                return;\n            }\n            //else angle => 180 degrees\n        }\n        else if (m_sinA > 1.0) m_sinA = 1.0;\n        else if (m_sinA < -1.0) m_sinA = -1.0;\n\n        if (m_sinA * m_delta < 0)\n        {\n            m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta),\n                                          Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta)));\n            m_destPoly.push_back(m_srcPoly[j]);\n            m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta),\n                                          Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta)));\n        }\n        else\n            switch (jointype)\n            {\n                case jtMiter:\n                {\n                    double r = 1 + (m_normals[j].X * m_normals[k].X +\n                                    m_normals[j].Y * m_normals[k].Y);\n                    if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k);\n                    break;\n                }\n                case jtSquare: DoSquare(j, k); break;\n                case jtRound: DoRound(j, k); break;\n            }\n        k = j;\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperOffset::DoSquare(int j, int k)\n    {\n        double dx = std::tan(std::atan2(m_sinA,\n                                        m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4);\n        m_destPoly.push_back(IntPoint(\n                Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)),\n                Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx))));\n        m_destPoly.push_back(IntPoint(\n                Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)),\n                Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx))));\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperOffset::DoMiter(int j, int k, double r)\n    {\n        double q = m_delta / r;\n        m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q),\n                                      Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q)));\n    }\n//------------------------------------------------------------------------------\n\n    void ClipperOffset::DoRound(int j, int k)\n    {\n        double a = std::atan2(m_sinA,\n                              m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y);\n        int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1);\n\n        double X = m_normals[k].X, Y = m_normals[k].Y, X2;\n        for (int i = 0; i < steps; ++i)\n        {\n            m_destPoly.push_back(IntPoint(\n                    Round(m_srcPoly[j].X + X * m_delta),\n                    Round(m_srcPoly[j].Y + Y * m_delta)));\n            X2 = X;\n            X = X * m_cos - m_sin * Y;\n            Y = X2 * m_sin + Y * m_cos;\n        }\n        m_destPoly.push_back(IntPoint(\n                Round(m_srcPoly[j].X + m_normals[j].X * m_delta),\n                Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta)));\n    }\n\n//------------------------------------------------------------------------------\n// Miscellaneous public functions\n//------------------------------------------------------------------------------\n\n    void Clipper::DoSimplePolygons()\n    {\n        PolyOutList::size_type i = 0;\n        while (i < m_PolyOuts.size())\n        {\n            OutRec* outrec = m_PolyOuts[i++];\n            OutPt* op = outrec->Pts;\n            if (!op || outrec->IsOpen) continue;\n            do //for each Pt in Polygon until duplicate found do ...\n            {\n                OutPt* op2 = op->Next;\n                while (op2 != outrec->Pts)\n                {\n                    if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op)\n                    {\n                        //split the polygon into two ...\n                        OutPt* op3 = op->Prev;\n                        OutPt* op4 = op2->Prev;\n                        op->Prev = op4;\n                        op4->Next = op;\n                        op2->Prev = op3;\n                        op3->Next = op2;\n\n                        outrec->Pts = op;\n                        OutRec* outrec2 = CreateOutRec();\n                        outrec2->Pts = op2;\n                        UpdateOutPtIdxs(*outrec2);\n                        if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts))\n                        {\n                            //OutRec2 is contained by OutRec1 ...\n                            outrec2->IsHole = !outrec->IsHole;\n                            outrec2->FirstLeft = outrec;\n                            if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec);\n                        }\n                        else\n                        if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts))\n                        {\n                            //OutRec1 is contained by OutRec2 ...\n                            outrec2->IsHole = outrec->IsHole;\n                            outrec->IsHole = !outrec2->IsHole;\n                            outrec2->FirstLeft = outrec->FirstLeft;\n                            outrec->FirstLeft = outrec2;\n                            if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2);\n                        }\n                        else\n                        {\n                            //the 2 polygons are separate ...\n                            outrec2->IsHole = outrec->IsHole;\n                            outrec2->FirstLeft = outrec->FirstLeft;\n                            if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2);\n                        }\n                        op2 = op; //ie get ready for the Next iteration\n                    }\n                    op2 = op2->Next;\n                }\n                op = op->Next;\n            }\n            while (op != outrec->Pts);\n        }\n    }\n//------------------------------------------------------------------------------\n\n    void ReversePath(Path& p)\n    {\n        std::reverse(p.begin(), p.end());\n    }\n//------------------------------------------------------------------------------\n\n    void ReversePaths(Paths& p)\n    {\n        for (Paths::size_type i = 0; i < p.size(); ++i)\n            ReversePath(p[i]);\n    }\n//------------------------------------------------------------------------------\n\n    void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType)\n    {\n        Clipper c;\n        c.StrictlySimple(true);\n        c.AddPath(in_poly, ptSubject, true);\n        c.Execute(ctUnion, out_polys, fillType, fillType);\n    }\n//------------------------------------------------------------------------------\n\n    void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType)\n    {\n        Clipper c;\n        c.StrictlySimple(true);\n        c.AddPaths(in_polys, ptSubject, true);\n        c.Execute(ctUnion, out_polys, fillType, fillType);\n    }\n//------------------------------------------------------------------------------\n\n    void SimplifyPolygons(Paths &polys, PolyFillType fillType)\n    {\n        SimplifyPolygons(polys, polys, fillType);\n    }\n//------------------------------------------------------------------------------\n\n    inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2)\n    {\n        double Dx = ((double)pt1.X - pt2.X);\n        double dy = ((double)pt1.Y - pt2.Y);\n        return (Dx*Dx + dy*dy);\n    }\n//------------------------------------------------------------------------------\n\n    double DistanceFromLineSqrd(\n            const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2)\n    {\n        //The equation of a line in general form (Ax + By + C = 0)\n        //given 2 points (x?y? & (x?y? is ...\n        //(y?- y?x + (x?- x?y + (y?- y?x?- (x?- x?y?= 0\n        //A = (y?- y?; B = (x?- x?; C = (y?- y?x?- (x?- x?y?\n        //perpendicular distance of point (x?y? = (Ax?+ By?+ C)/Sqrt(A?+ B?\n        //see http://en.wikipedia.org/wiki/Perpendicular_distance\n        double A = double(ln1.Y - ln2.Y);\n        double B = double(ln2.X - ln1.X);\n        double C = A * ln1.X  + B * ln1.Y;\n        C = A * pt.X + B * pt.Y - C;\n        return (C * C) / (A * A + B * B);\n    }\n//---------------------------------------------------------------------------\n\n    bool SlopesNearCollinear(const IntPoint& pt1,\n                             const IntPoint& pt2, const IntPoint& pt3, double distSqrd)\n    {\n        //this function is more accurate when the point that's geometrically\n        //between the other 2 points is the one that's tested for distance.\n        //ie makes it more likely to pick up 'spikes' ...\n        if (Abs(pt1.X - pt2.X) > Abs(pt1.Y - pt2.Y))\n        {\n            if ((pt1.X > pt2.X) == (pt1.X < pt3.X))\n                return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd;\n            else if ((pt2.X > pt1.X) == (pt2.X < pt3.X))\n                return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd;\n            else\n                return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd;\n        }\n        else\n        {\n            if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y))\n                return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd;\n            else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y))\n                return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd;\n            else\n                return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd;\n        }\n    }\n//------------------------------------------------------------------------------\n\n    bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd)\n    {\n        double Dx = (double)pt1.X - pt2.X;\n        double dy = (double)pt1.Y - pt2.Y;\n        return ((Dx * Dx) + (dy * dy) <= distSqrd);\n    }\n//------------------------------------------------------------------------------\n\n    OutPt* ExcludeOp(OutPt* op)\n    {\n        OutPt* result = op->Prev;\n        result->Next = op->Next;\n        op->Next->Prev = result;\n        result->Idx = 0;\n        return result;\n    }\n//------------------------------------------------------------------------------\n\n    void CleanPolygon(const Path& in_poly, Path& out_poly, double distance)\n    {\n        //distance = proximity in units/pixels below which vertices\n        //will be stripped. Default ~= sqrt(2).\n\n        size_t size = in_poly.size();\n\n        if (size == 0)\n        {\n            out_poly.clear();\n            return;\n        }\n\n        OutPt* outPts = new OutPt[size];\n        for (size_t i = 0; i < size; ++i)\n        {\n            outPts[i].Pt = in_poly[i];\n            outPts[i].Next = &outPts[(i + 1) % size];\n            outPts[i].Next->Prev = &outPts[i];\n            outPts[i].Idx = 0;\n        }\n\n        double distSqrd = distance * distance;\n        OutPt* op = &outPts[0];\n        while (op->Idx == 0 && op->Next != op->Prev)\n        {\n            if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd))\n            {\n                op = ExcludeOp(op);\n                size--;\n            }\n            else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd))\n            {\n                ExcludeOp(op->Next);\n                op = ExcludeOp(op);\n                size -= 2;\n            }\n            else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd))\n            {\n                op = ExcludeOp(op);\n                size--;\n            }\n            else\n            {\n                op->Idx = 1;\n                op = op->Next;\n            }\n        }\n\n        if (size < 3) size = 0;\n        out_poly.resize(size);\n        for (size_t i = 0; i < size; ++i)\n        {\n            out_poly[i] = op->Pt;\n            op = op->Next;\n        }\n        delete [] outPts;\n    }\n//------------------------------------------------------------------------------\n\n    void CleanPolygon(Path& poly, double distance)\n    {\n        CleanPolygon(poly, poly, distance);\n    }\n//------------------------------------------------------------------------------\n\n    void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance)\n    {\n        out_polys.resize(in_polys.size());\n        for (Paths::size_type i = 0; i < in_polys.size(); ++i)\n            CleanPolygon(in_polys[i], out_polys[i], distance);\n    }\n//------------------------------------------------------------------------------\n\n    void CleanPolygons(Paths& polys, double distance)\n    {\n        CleanPolygons(polys, polys, distance);\n    }\n//------------------------------------------------------------------------------\n\n    void Minkowski(const Path& poly, const Path& path,\n                   Paths& solution, bool isSum, bool isClosed)\n    {\n        int delta = (isClosed ? 1 : 0);\n        size_t polyCnt = poly.size();\n        size_t pathCnt = path.size();\n        Paths pp;\n        pp.reserve(pathCnt);\n        if (isSum)\n            for (size_t i = 0; i < pathCnt; ++i)\n            {\n                Path p;\n                p.reserve(polyCnt);\n                for (size_t j = 0; j < poly.size(); ++j)\n                    p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y));\n                pp.push_back(p);\n            }\n        else\n            for (size_t i = 0; i < pathCnt; ++i)\n            {\n                Path p;\n                p.reserve(polyCnt);\n                for (size_t j = 0; j < poly.size(); ++j)\n                    p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y));\n                pp.push_back(p);\n            }\n\n        solution.clear();\n        solution.reserve((pathCnt + delta) * (polyCnt + 1));\n        for (size_t i = 0; i < pathCnt - 1 + delta; ++i)\n            for (size_t j = 0; j < polyCnt; ++j)\n            {\n                Path quad;\n                quad.reserve(4);\n                quad.push_back(pp[i % pathCnt][j % polyCnt]);\n                quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]);\n                quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]);\n                quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]);\n                if (!Orientation(quad)) ReversePath(quad);\n                solution.push_back(quad);\n            }\n    }\n//------------------------------------------------------------------------------\n\n    void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed)\n    {\n        Minkowski(pattern, path, solution, true, pathIsClosed);\n        Clipper c;\n        c.AddPaths(solution, ptSubject, true);\n        c.Execute(ctUnion, solution, pftNonZero, pftNonZero);\n    }\n//------------------------------------------------------------------------------\n\n    void TranslatePath(const Path& input, Path& output, const IntPoint delta)\n    {\n        //precondition: input != output\n        output.resize(input.size());\n        for (size_t i = 0; i < input.size(); ++i)\n            output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y);\n    }\n//------------------------------------------------------------------------------\n\n    void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed)\n    {\n        Clipper c;\n        for (size_t i = 0; i < paths.size(); ++i)\n        {\n            Paths tmp;\n            Minkowski(pattern, paths[i], tmp, true, pathIsClosed);\n            c.AddPaths(tmp, ptSubject, true);\n            if (pathIsClosed)\n            {\n                Path tmp2;\n                TranslatePath(paths[i], tmp2, pattern[0]);\n                c.AddPath(tmp2, ptClip, true);\n            }\n        }\n        c.Execute(ctUnion, solution, pftNonZero, pftNonZero);\n    }\n//------------------------------------------------------------------------------\n\n    void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution)\n    {\n        Minkowski(poly1, poly2, solution, false, true);\n        Clipper c;\n        c.AddPaths(solution, ptSubject, true);\n        c.Execute(ctUnion, solution, pftNonZero, pftNonZero);\n    }\n//------------------------------------------------------------------------------\n\n    enum NodeType {ntAny, ntOpen, ntClosed};\n\n    void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths)\n    {\n        bool match = true;\n        if (nodetype == ntClosed) match = !polynode.IsOpen();\n        else if (nodetype == ntOpen) return;\n\n        if (!polynode.Contour.empty() && match)\n            paths.push_back(polynode.Contour);\n        for (int i = 0; i < polynode.ChildCount(); ++i)\n            AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths);\n    }\n//------------------------------------------------------------------------------\n\n    void PolyTreeToPaths(const PolyTree& polytree, Paths& paths)\n    {\n        paths.resize(0);\n        paths.reserve(polytree.Total());\n        AddPolyNodeToPaths(polytree, ntAny, paths);\n    }\n//------------------------------------------------------------------------------\n\n    void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths)\n    {\n        paths.resize(0);\n        paths.reserve(polytree.Total());\n        AddPolyNodeToPaths(polytree, ntClosed, paths);\n    }\n//------------------------------------------------------------------------------\n\n    void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths)\n    {\n        paths.resize(0);\n        paths.reserve(polytree.Total());\n        //Open paths are top level only, so ...\n        for (int i = 0; i < polytree.ChildCount(); ++i)\n            if (polytree.Childs[i]->IsOpen())\n                paths.push_back(polytree.Childs[i]->Contour);\n    }\n//------------------------------------------------------------------------------\n\n    std::ostream& operator <<(std::ostream &s, const IntPoint &p)\n    {\n        s << \"(\" << p.X << \",\" << p.Y << \")\";\n        return s;\n    }\n//------------------------------------------------------------------------------\n\n    std::ostream& operator <<(std::ostream &s, const Path &p)\n    {\n        if (p.empty()) return s;\n        Path::size_type last = p.size() -1;\n        for (Path::size_type i = 0; i < last; i++)\n            s << \"(\" << p[i].X << \",\" << p[i].Y << \"), \";\n        s << \"(\" << p[last].X << \",\" << p[last].Y << \")\\n\";\n        return s;\n    }\n//------------------------------------------------------------------------------\n\n    std::ostream& operator <<(std::ostream &s, const Paths &p)\n    {\n        for (Paths::size_type i = 0; i < p.size(); i++)\n            s << p[i];\n        s << \"\\n\";\n        return s;\n    }\n//------------------------------------------------------------------------------\n\n} //ClipperLib namespace\n"
  },
  {
    "path": "app/src/main/jni/clipper.hpp",
    "content": "/*******************************************************************************\n*                                                                              *\n* Author    :  Angus Johnson                                                   *\n* Version   :  6.4.2                                                           *\n* Date      :  27 February 2017                                                *\n* Website   :  http://www.angusj.com                                           *\n* Copyright :  Angus Johnson 2010-2017                                         *\n*                                                                              *\n* License:                                                                     *\n* Use, modification & distribution is subject to Boost Software License Ver 1. *\n* http://www.boost.org/LICENSE_1_0.txt                                         *\n*                                                                              *\n* Attributions:                                                                *\n* The code in this library is an extension of Bala Vatti's clipping algorithm: *\n* \"A generic solution to polygon clipping\"                                     *\n* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63.             *\n* http://portal.acm.org/citation.cfm?id=129906                                 *\n*                                                                              *\n* Computer graphics and geometric modeling: implementation and algorithms      *\n* By Max K. Agoston                                                            *\n* Springer; 1 edition (January 4, 2005)                                        *\n* http://books.google.com/books?q=vatti+clipping+agoston                       *\n*                                                                              *\n* See also:                                                                    *\n* \"Polygon Offsetting by Computing Winding Numbers\"                            *\n* Paper no. DETC2005-85513 pp. 565-575                                         *\n* ASME 2005 International Design Engineering Technical Conferences             *\n* and Computers and Information in Engineering Conference (IDETC/CIE2005)      *\n* September 24-28, 2005 , Long Beach, California, USA                          *\n* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf              *\n*                                                                              *\n*******************************************************************************/\n\n#ifndef clipper_hpp\n#define clipper_hpp\n\n#define CLIPPER_VERSION \"6.4.2\"\n\n//use_int32: When enabled 32bit ints are used instead of 64bit ints. This\n//improve performance but coordinate values are limited to the range +/- 46340\n//#define use_int32\n\n//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance.\n//#define use_xyz\n\n//use_lines: Enables line clipping. Adds a very minor cost to performance.\n#define use_lines\n\n//use_deprecated: Enables temporary support for the obsolete functions\n//#define use_deprecated  \n\n#include <vector>\n#include <list>\n#include <set>\n#include <stdexcept>\n#include <cstring>\n#include <cstdlib>\n#include <ostream>\n#include <functional>\n#include <queue>\n\nnamespace ClipperLib {\n\n    enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };\n    enum PolyType { ptSubject, ptClip };\n//By far the most widely used winding rules for polygon filling are\n//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)\n//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)\n//see http://glprogramming.com/red/chapter11.html\n    enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };\n\n#ifdef use_int32\n    typedef int cInt;\n  static cInt const loRange = 0x7FFF;\n  static cInt const hiRange = 0x7FFF;\n#else\n    typedef signed long long cInt;\n    static cInt const loRange = 0x3FFFFFFF;\n    static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;\n    typedef signed long long long64;     //used by Int128 class\n    typedef unsigned long long ulong64;\n\n#endif\n\n    struct IntPoint {\n        cInt X;\n        cInt Y;\n#ifdef use_xyz\n        cInt Z;\n  IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {};\n#else\n        IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {};\n#endif\n\n        friend inline bool operator== (const IntPoint& a, const IntPoint& b)\n        {\n            return a.X == b.X && a.Y == b.Y;\n        }\n        friend inline bool operator!= (const IntPoint& a, const IntPoint& b)\n        {\n            return a.X != b.X  || a.Y != b.Y;\n        }\n    };\n//------------------------------------------------------------------------------\n\n    typedef std::vector< IntPoint > Path;\n    typedef std::vector< Path > Paths;\n\n    inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}\n    inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}\n\n    std::ostream& operator <<(std::ostream &s, const IntPoint &p);\n    std::ostream& operator <<(std::ostream &s, const Path &p);\n    std::ostream& operator <<(std::ostream &s, const Paths &p);\n\n    struct DoublePoint\n    {\n        double X;\n        double Y;\n        DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {}\n        DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {}\n    };\n//------------------------------------------------------------------------------\n\n#ifdef use_xyz\n    typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt);\n#endif\n\n    enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};\n    enum JoinType {jtSquare, jtRound, jtMiter};\n    enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};\n\n    class PolyNode;\n    typedef std::vector< PolyNode* > PolyNodes;\n\n    class PolyNode\n    {\n    public:\n        PolyNode();\n        virtual ~PolyNode(){};\n        Path Contour;\n        PolyNodes Childs;\n        PolyNode* Parent;\n        PolyNode* GetNext() const;\n        bool IsHole() const;\n        bool IsOpen() const;\n        int ChildCount() const;\n    private:\n        //PolyNode& operator =(PolyNode& other);\n        unsigned Index; //node index in Parent.Childs\n        bool m_IsOpen;\n        JoinType m_jointype;\n        EndType m_endtype;\n        PolyNode* GetNextSiblingUp() const;\n        void AddChild(PolyNode& child);\n        friend class Clipper; //to access Index\n        friend class ClipperOffset;\n    };\n\n    class PolyTree: public PolyNode\n    {\n    public:\n        ~PolyTree(){ Clear(); };\n        PolyNode* GetFirst() const;\n        void Clear();\n        int Total() const;\n    private:\n        //PolyTree& operator =(PolyTree& other);\n        PolyNodes AllNodes;\n        friend class Clipper; //to access AllNodes\n    };\n\n    bool Orientation(const Path &poly);\n    double Area(const Path &poly);\n    int PointInPolygon(const IntPoint &pt, const Path &path);\n\n    void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd);\n    void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd);\n    void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd);\n\n    void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);\n    void CleanPolygon(Path& poly, double distance = 1.415);\n    void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415);\n    void CleanPolygons(Paths& polys, double distance = 1.415);\n\n    void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);\n    void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed);\n    void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);\n\n    void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);\n    void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths);\n    void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths);\n\n    void ReversePath(Path& p);\n    void ReversePaths(Paths& p);\n\n    struct IntRect { cInt left; cInt top; cInt right; cInt bottom; };\n\n//enums that are used internally ...\n    enum EdgeSide { esLeft = 1, esRight = 2};\n\n//forward declarations (for stuff used internally) ...\n    struct TEdge;\n    struct IntersectNode;\n    struct LocalMinimum;\n    struct OutPt;\n    struct OutRec;\n    struct Join;\n\n    typedef std::vector < OutRec* > PolyOutList;\n    typedef std::vector < TEdge* > EdgeList;\n    typedef std::vector < Join* > JoinList;\n    typedef std::vector < IntersectNode* > IntersectList;\n\n//------------------------------------------------------------------------------\n\n//ClipperBase is the ancestor to the Clipper class. It should not be\n//instantiated directly. This class simply abstracts the conversion of sets of\n//polygon coordinates into edge objects that are stored in a LocalMinima list.\n    class ClipperBase\n    {\n    public:\n        ClipperBase();\n        virtual ~ClipperBase();\n        virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);\n        bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed);\n        virtual void Clear();\n        IntRect GetBounds();\n        bool PreserveCollinear() {return m_PreserveCollinear;};\n        void PreserveCollinear(bool value) {m_PreserveCollinear = value;};\n    protected:\n        void DisposeLocalMinimaList();\n        TEdge* AddBoundsToLML(TEdge *e, bool IsClosed);\n        virtual void Reset();\n        TEdge* ProcessBound(TEdge* E, bool IsClockwise);\n        void InsertScanbeam(const cInt Y);\n        bool PopScanbeam(cInt &Y);\n        bool LocalMinimaPending();\n        bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin);\n        OutRec* CreateOutRec();\n        void DisposeAllOutRecs();\n        void DisposeOutRec(PolyOutList::size_type index);\n        void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);\n        void DeleteFromAEL(TEdge *e);\n        void UpdateEdgeIntoAEL(TEdge *&e);\n\n        typedef std::vector<LocalMinimum> MinimaList;\n        MinimaList::iterator m_CurrentLM;\n        MinimaList           m_MinimaList;\n\n        bool              m_UseFullRange;\n        EdgeList          m_edges;\n        bool              m_PreserveCollinear;\n        bool              m_HasOpenPaths;\n        PolyOutList       m_PolyOuts;\n        TEdge           *m_ActiveEdges;\n\n        typedef std::priority_queue<cInt> ScanbeamList;\n        ScanbeamList     m_Scanbeam;\n    };\n//------------------------------------------------------------------------------\n\n    class Clipper : public virtual ClipperBase\n    {\n    public:\n        Clipper(int initOptions = 0);\n        bool Execute(ClipType clipType,\n                     Paths &solution,\n                     PolyFillType fillType = pftEvenOdd);\n        bool Execute(ClipType clipType,\n                     Paths &solution,\n                     PolyFillType subjFillType,\n                     PolyFillType clipFillType);\n        bool Execute(ClipType clipType,\n                     PolyTree &polytree,\n                     PolyFillType fillType = pftEvenOdd);\n        bool Execute(ClipType clipType,\n                     PolyTree &polytree,\n                     PolyFillType subjFillType,\n                     PolyFillType clipFillType);\n        bool ReverseSolution() { return m_ReverseOutput; };\n        void ReverseSolution(bool value) {m_ReverseOutput = value;};\n        bool StrictlySimple() {return m_StrictSimple;};\n        void StrictlySimple(bool value) {m_StrictSimple = value;};\n        //set the callback function for z value filling on intersections (otherwise Z is 0)\n#ifdef use_xyz\n        void ZFillFunction(ZFillCallback zFillFunc);\n#endif\n    protected:\n        virtual bool ExecuteInternal();\n    private:\n        JoinList         m_Joins;\n        JoinList         m_GhostJoins;\n        IntersectList    m_IntersectList;\n        ClipType         m_ClipType;\n        typedef std::list<cInt> MaximaList;\n        MaximaList       m_Maxima;\n        TEdge           *m_SortedEdges;\n        bool             m_ExecuteLocked;\n        PolyFillType     m_ClipFillType;\n        PolyFillType     m_SubjFillType;\n        bool             m_ReverseOutput;\n        bool             m_UsingPolyTree;\n        bool             m_StrictSimple;\n#ifdef use_xyz\n        ZFillCallback   m_ZFill; //custom callback\n#endif\n        void SetWindingCount(TEdge& edge);\n        bool IsEvenOddFillType(const TEdge& edge) const;\n        bool IsEvenOddAltFillType(const TEdge& edge) const;\n        void InsertLocalMinimaIntoAEL(const cInt botY);\n        void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge);\n        void AddEdgeToSEL(TEdge *edge);\n        bool PopEdgeFromSEL(TEdge *&edge);\n        void CopyAELToSEL();\n        void DeleteFromSEL(TEdge *e);\n        void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);\n        bool IsContributing(const TEdge& edge) const;\n        bool IsTopHorz(const cInt XPos);\n        void DoMaxima(TEdge *e);\n        void ProcessHorizontals();\n        void ProcessHorizontal(TEdge *horzEdge);\n        void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);\n        OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);\n        OutRec* GetOutRec(int idx);\n        void AppendPolygon(TEdge *e1, TEdge *e2);\n        void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);\n        OutPt* AddOutPt(TEdge *e, const IntPoint &pt);\n        OutPt* GetLastOutPt(TEdge *e);\n        bool ProcessIntersections(const cInt topY);\n        void BuildIntersectList(const cInt topY);\n        void ProcessIntersectList();\n        void ProcessEdgesAtTopOfScanbeam(const cInt topY);\n        void BuildResult(Paths& polys);\n        void BuildResult2(PolyTree& polytree);\n        void SetHoleState(TEdge *e, OutRec *outrec);\n        void DisposeIntersectNodes();\n        bool FixupIntersectionOrder();\n        void FixupOutPolygon(OutRec &outrec);\n        void FixupOutPolyline(OutRec &outrec);\n        bool IsHole(TEdge *e);\n        bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);\n        void FixHoleLinkage(OutRec &outrec);\n        void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt);\n        void ClearJoins();\n        void ClearGhostJoins();\n        void AddGhostJoin(OutPt *op, const IntPoint offPt);\n        bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2);\n        void JoinCommonEdges();\n        void DoSimplePolygons();\n        void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);\n        void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec);\n        void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec);\n#ifdef use_xyz\n        void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);\n#endif\n    };\n//------------------------------------------------------------------------------\n\n    class ClipperOffset\n    {\n    public:\n        ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25);\n        ~ClipperOffset();\n        void AddPath(const Path& path, JoinType joinType, EndType endType);\n        void AddPaths(const Paths& paths, JoinType joinType, EndType endType);\n        void Execute(Paths& solution, double delta);\n        void Execute(PolyTree& solution, double delta);\n        void Clear();\n        double MiterLimit;\n        double ArcTolerance;\n    private:\n        Paths m_destPolys;\n        Path m_srcPoly;\n        Path m_destPoly;\n        std::vector<DoublePoint> m_normals;\n        double m_delta, m_sinA, m_sin, m_cos;\n        double m_miterLim, m_StepsPerRad;\n        IntPoint m_lowest;\n        PolyNode m_polyNodes;\n\n        void FixOrientations();\n        void DoOffset(double delta);\n        void OffsetPoint(int j, int& k, JoinType jointype);\n        void DoSquare(int j, int k);\n        void DoMiter(int j, int k, double r);\n        void DoRound(int j, int k);\n    };\n//------------------------------------------------------------------------------\n\n    class clipperException : public std::exception\n    {\n    public:\n        clipperException(const char* description): m_descr(description) {}\n        virtual ~clipperException() throw() {}\n        virtual const char* what() const throw() {return m_descr.c_str();}\n    private:\n        std::string m_descr;\n    };\n//------------------------------------------------------------------------------\n\n} //ClipperLib namespace\n\n#endif //clipper_hpp\n\n\n"
  },
  {
    "path": "app/src/main/jni/common.cpp",
    "content": "#include \"common.h\"\n#include <jni.h>\n#include <android/log.h>\nbool cvPointCompare(const cv::Point& a, const cv::Point& b) {\n    return a.x < b.x;\n}\nbool compareBoxWidth(const TextBox &a, const TextBox& b)\n{\n    return abs(a.boxPoint[0].x-a.boxPoint[1].x)>abs(b.boxPoint[0].x-b.boxPoint[1].x);\n}\n\nstd::vector<cv::Point> getMinBoxes(const std::vector<cv::Point>& inVec, float& minSideLen, float& allEdgeSize) {\n    std::vector<cv::Point> minBoxVec;\n    cv::RotatedRect textRect = cv::minAreaRect(inVec);\n    cv::Mat boxPoints2f;\n    cv::boxPoints(textRect, boxPoints2f);\n\n    float* p1 = (float*)boxPoints2f.data;\n    std::vector<cv::Point> tmpVec;\n    for (int i = 0; i < 4; ++i, p1 += 2) {\n        tmpVec.emplace_back(int(p1[0]), int(p1[1]));\n    }\n\n    std::sort(tmpVec.begin(), tmpVec.end(), cvPointCompare);\n\n    minBoxVec.clear();\n\n    int index1, index2, index3, index4;\n    if (tmpVec[1].y > tmpVec[0].y) {\n        index1 = 0;\n        index4 = 1;\n    }\n    else {\n        index1 = 1;\n        index4 = 0;\n    }\n\n    if (tmpVec[3].y > tmpVec[2].y) {\n        index2 = 2;\n        index3 = 3;\n    }\n    else {\n        index2 = 3;\n        index3 = 2;\n    }\n\n    minBoxVec.clear();\n\n    minBoxVec.push_back(tmpVec[index1]);\n    minBoxVec.push_back(tmpVec[index2]);\n    minBoxVec.push_back(tmpVec[index3]);\n    minBoxVec.push_back(tmpVec[index4]);\n\n    minSideLen = (std::min)(textRect.size.width, textRect.size.height);\n    allEdgeSize = 2.f * (textRect.size.width + textRect.size.height);\n\n    return minBoxVec;\n}\n\nfloat boxScoreFast(const cv::Mat & inMat, const std::vector<cv::Point> & inBox) {\n    std::vector<cv::Point> box = inBox;\n    int width = inMat.cols;\n    int height = inMat.rows;\n    int maxX = -1, minX = 1000000, maxY = -1, minY = 1000000;\n    for (int i = 0; i < box.size(); ++i) {\n        if (maxX < box[i].x)\n            maxX = box[i].x;\n        if (minX > box[i].x)\n            minX = box[i].x;\n        if (maxY < box[i].y)\n            maxY = box[i].y;\n        if (minY > box[i].y)\n            minY = box[i].y;\n    }\n    maxX = (std::min)((std::max)(maxX, 0), width - 1);\n    minX = (std::max)((std::min)(minX, width - 1), 0);\n    maxY = (std::min)((std::max)(maxY, 0), height - 1);\n    minY = (std::max)((std::min)(minY, height - 1), 0);\n\n    for (int i = 0; i < box.size(); ++i) {\n        box[i].x = box[i].x - minX;\n        box[i].y = box[i].y - minY;\n    }\n\n    std::vector<std::vector<cv::Point>> maskBox;\n    maskBox.push_back(box);\n    cv::Mat maskMat(maxY - minY + 1, maxX - minX + 1, CV_8UC1, cv::Scalar(0, 0, 0));\n    cv::fillPoly(maskMat, maskBox, cv::Scalar(1, 1, 1), 1);\n    return cv::mean(inMat(cv::Rect(cv::Point(minX, minY), cv::Point(maxX + 1, maxY + 1))).clone(),\n        maskMat).val[0];\n}\n\nstd::vector<cv::Point> unClip(const std::vector<cv::Point> & inBox, float perimeter, float unClipRatio) {\n    std::vector<cv::Point> outBox;\n    ClipperLib::Path poly;\n\n    for (int i = 0; i < inBox.size(); ++i) {\n        poly.push_back(ClipperLib::IntPoint(inBox[i].x, inBox[i].y));\n    }\n\n    double distance = unClipRatio * ClipperLib::Area(poly) / (double)perimeter;\n\n    ClipperLib::ClipperOffset clipperOffset;\n    clipperOffset.AddPath(poly, ClipperLib::JoinType::jtRound, ClipperLib::EndType::etClosedPolygon);\n    ClipperLib::Paths polys;\n    polys.push_back(poly);\n    clipperOffset.Execute(polys, distance);\n\n    outBox.clear();\n    std::vector<cv::Point> rsVec;\n    for (int i = 0; i < polys.size(); ++i) {\n        ClipperLib::Path tmpPoly = polys[i];\n        for (int j = 0; j < tmpPoly.size(); ++j) {\n            outBox.emplace_back(tmpPoly[j].X, tmpPoly[j].Y);\n        }\n    }\n    return outBox;\n}\n\ncv::Mat getRotateCropImage(const cv::Mat& src, std::vector<cv::Point> box) {\n    cv::Mat image;\n    src.copyTo(image);\n    std::vector<cv::Point> points = box;\n\n    int collectX[4] = { box[0].x, box[1].x, box[2].x, box[3].x };\n    int collectY[4] = { box[0].y, box[1].y, box[2].y, box[3].y };\n    int left = int(*std::min_element(collectX, collectX + 4));\n    int right = int(*std::max_element(collectX, collectX + 4));\n    int top = int(*std::min_element(collectY, collectY + 4));\n    int bottom = int(*std::max_element(collectY, collectY + 4));\n\n    cv::Mat imgCrop;\n    image(cv::Rect(left, top, right - left, bottom - top)).copyTo(imgCrop);\n\n    for (int i = 0; i < points.size(); i++) {\n        points[i].x -= left;\n        points[i].y -= top;\n    }\n\n\n    int imgCropWidth = int(sqrt(pow(points[0].x - points[1].x, 2) +\n        pow(points[0].y - points[1].y, 2)));\n    int imgCropHeight = int(sqrt(pow(points[0].x - points[3].x, 2) +\n        pow(points[0].y - points[3].y, 2)));\n\n    cv::Point2f ptsDst[4];\n    ptsDst[0] = cv::Point2f(0., 0.);\n    ptsDst[1] = cv::Point2f(imgCropWidth, 0.);\n    ptsDst[2] = cv::Point2f(imgCropWidth, imgCropHeight);\n    ptsDst[3] = cv::Point2f(0.f, imgCropHeight);\n\n    cv::Point2f ptsSrc[4];\n    ptsSrc[0] = cv::Point2f(points[0].x, points[0].y);\n    ptsSrc[1] = cv::Point2f(points[1].x, points[1].y);\n    ptsSrc[2] = cv::Point2f(points[2].x, points[2].y);\n    ptsSrc[3] = cv::Point2f(points[3].x, points[3].y);\n\n    cv::Mat M = cv::getPerspectiveTransform(ptsSrc, ptsDst);\n\n    cv::Mat partImg;\n    cv::warpPerspective(imgCrop, partImg, M,\n        cv::Size(imgCropWidth, imgCropHeight),\n        cv::BORDER_REPLICATE);\n\n    if (float(partImg.rows) >= float(partImg.cols) * 1.5) {\n        cv::Mat srcCopy = cv::Mat(partImg.rows, partImg.cols, partImg.depth());\n        cv::transpose(partImg, srcCopy);\n        cv::flip(srcCopy, srcCopy, 0);\n        return srcCopy;\n    }\n    else {\n        return partImg;\n    }\n}\nstd::vector<cv::Mat> getPartImages(const cv::Mat& src, std::vector<TextBox>& textBoxes)\n{\n    std::sort(textBoxes.begin(),textBoxes.end(),compareBoxWidth);\n    std::vector<cv::Mat> partImages;\n    if(textBoxes.size() > 0)\n    {\n        for (int i = 0; i < textBoxes.size(); ++i)\n        {\n            cv::Mat partImg = getRotateCropImage(src, textBoxes[i].boxPoint);\n            partImages.emplace_back(partImg);\n        }\n    }\n\n    return partImages;\n}\ncv::Mat matRotateClockWise180(cv::Mat src) {\n    flip(src, src, 0);\n    flip(src, src, 1);\n    return src;\n}\n\ncv::Mat makePadding(cv::Mat& src, const int padding) {\n    if (padding <= 0) return src;\n    cv::Scalar paddingScalar = { 255, 255, 255 };\n    cv::Mat paddingSrc;\n    cv::copyMakeBorder(src, paddingSrc, padding, padding, padding, padding, cv::BORDER_ISOLATED, paddingScalar);\n    return paddingSrc;\n}"
  },
  {
    "path": "app/src/main/jni/common.h",
    "content": "#ifndef __COMMON_H_\n#define __COMMON_H_\n#include <opencv2/opencv.hpp>\n#include <vector>\n#include \"clipper.hpp\"\n#include <jni.h>\nstruct TextBox {\n    std::vector<cv::Point> boxPoint;\n    float score;\n    std::string text;\n};\nstruct TextLine {\n    std::string text;\n    std::vector<float> charScores;\n};\n\nstruct Angle {\n    int index;\n    float score;\n};\nstd::vector<cv::Point> getMinBoxes(const std::vector<cv::Point>& inVec, float& minSideLen, float& allEdgeSize);\nfloat boxScoreFast(const cv::Mat& inMat, const std::vector<cv::Point>& inBox);\nstd::vector<cv::Point> unClip(const std::vector<cv::Point>& inBox, float perimeter, float unClipRatio);\ncv::Mat getRotateCropImage(const cv::Mat& src, std::vector<cv::Point> box);\nstd::vector<cv::Mat> getPartImages(const cv::Mat& src, std::vector<TextBox>& textBoxes);\ncv::Mat matRotateClockWise180(cv::Mat src);\ncv::Mat makePadding(cv::Mat& src, const int padding);\n\n#endif"
  },
  {
    "path": "app/src/main/jni/paddleocr_ncnn.cpp",
    "content": "// Tencent is pleased to support the open source community by making ncnn available.\n//\n// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.\n//\n// Licensed under the BSD 3-Clause License (the \"License\"); you may not use this file except\n// in compliance with the License. You may obtain a copy of the License at\n//\n// https://opensource.org/licenses/BSD-3-Clause\n//\n// Unless required by applicable law or agreed to in writing, software distributed\n// under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR\n// CONDITIONS OF ANY KIND, either express or implied. See the License for the\n// specific language governing permissions and limitations under the License.\n\n#include <android/asset_manager_jni.h>\n#include <android/bitmap.h>\n#include <android/log.h>\n\n#include <jni.h>\n\n#include <string>\n#include <vector>\n#include <opencv2/core/core.hpp>\n// ncnn\n#include \"layer.h\"\n#include \"net.h\"\n#include \"benchmark.h\"\n#include \"common.h\"\nstatic ncnn::UnlockedPoolAllocator g_blob_pool_allocator;\nstatic ncnn::PoolAllocator g_workspace_pool_allocator;\nconst int dstHeight = 32;//when use PP-OCRv3 it should be 48\nncnn::Net dbNet;\nncnn::Net crnnNet;\n\nstd::vector<std::string> keys;\nchar *readKeysFromAssets(AAssetManager *mgr)\n{\n    if (mgr == NULL) {\n        return NULL;\n    }\n    char *buffer;\n\n    AAsset *asset = AAssetManager_open(mgr, \"paddleocr_keys.txt\", AASSET_MODE_UNKNOWN);\n    if (asset == NULL) {\n        return NULL;\n    }\n\n    off_t bufferSize = AAsset_getLength(asset);\n    buffer = (char *) malloc(bufferSize + 1);\n    buffer[bufferSize] = 0;\n    int numBytesRead = AAsset_read(asset, buffer, bufferSize);\n    AAsset_close(asset);\n\n    return buffer;\n}\n\n\nstd::vector<TextBox> findRsBoxes(const cv::Mat& fMapMat, const cv::Mat& norfMapMat,\n    const float boxScoreThresh, const float unClipRatio) \n{\n    float minArea = 3;\n    std::vector<TextBox> rsBoxes;\n    rsBoxes.clear();\n    std::vector<std::vector<cv::Point>> contours;\n    cv::findContours(norfMapMat, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);\n    for (int i = 0; i < contours.size(); ++i) \n    {\n        float minSideLen, perimeter;\n        std::vector<cv::Point> minBox = getMinBoxes(contours[i], minSideLen, perimeter);\n        if (minSideLen < minArea)\n            continue;\n        float score = boxScoreFast(fMapMat, contours[i]);\n        if (score < boxScoreThresh)\n            continue;\n        //---use clipper start---\n        std::vector<cv::Point> clipBox = unClip(minBox, perimeter, unClipRatio);\n        std::vector<cv::Point> clipMinBox = getMinBoxes(clipBox, minSideLen, perimeter);\n        //---use clipper end---\n\n        if (minSideLen < minArea + 2)\n            continue;\n\n        for (int j = 0; j < clipMinBox.size(); ++j) \n        {\n            clipMinBox[j].x = (clipMinBox[j].x / 1.0);\n            clipMinBox[j].x = (std::min)((std::max)(clipMinBox[j].x, 0), norfMapMat.cols);\n\n            clipMinBox[j].y = (clipMinBox[j].y / 1.0);\n            clipMinBox[j].y = (std::min)((std::max)(clipMinBox[j].y, 0), norfMapMat.rows);\n        }\n        \n        rsBoxes.emplace_back(TextBox{ clipMinBox, score });\n    }\n    reverse(rsBoxes.begin(), rsBoxes.end());\n\n    return rsBoxes;\n}\n\nstd::vector<TextBox> getTextBoxes(const cv::Mat & src, float boxScoreThresh, float boxThresh, float unClipRatio)\n{\n    int width = src.cols;\n    int height = src.rows;\n    int target_size = 640;\n    // pad to multiple of 32\n    int w = width;\n    int h = height;\n    float scale = 1.f;\n    if (w > h)\n    {\n        scale = (float)target_size / w;\n        w = target_size;\n        h = h * scale;\n    }\n    else\n    {\n        scale = (float)target_size / h;\n        h = target_size;\n        w = w * scale;\n    }\n\n    ncnn::Mat input = ncnn::Mat::from_pixels_resize(src.data, ncnn::Mat::PIXEL_RGB, width, height, w, h);\n\n    // pad to target_size rectangle\n    int wpad = (w + 31) / 32 * 32 - w;\n    int hpad = (h + 31) / 32 * 32 - h;\n    ncnn::Mat in_pad;\n    ncnn::copy_make_border(input, in_pad, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, ncnn::BORDER_CONSTANT, 0.f);\n\n    const float meanValues[3] = { 0.485 * 255, 0.456 * 255, 0.406 * 255 };\n    const float normValues[3] = { 1.0 / 0.229 / 255.0, 1.0 / 0.224 / 255.0, 1.0 / 0.225 / 255.0 };\n\n    in_pad.substract_mean_normalize(meanValues, normValues);\n    ncnn::Extractor extractor = dbNet.create_extractor();\n\n    extractor.input(\"input0\", in_pad);\n    ncnn::Mat out;\n    extractor.extract(\"out1\", out);\n\n    cv::Mat fMapMat(in_pad.h, in_pad.w, CV_32FC1, (float*)out.data);\n    cv::Mat norfMapMat;\n    norfMapMat = fMapMat > boxThresh;\n\n    cv::dilate(norfMapMat, norfMapMat, cv::Mat(), cv::Point(-1, -1), 1);\n\n    std::vector<TextBox> result = findRsBoxes(fMapMat, norfMapMat, boxScoreThresh, 2.0f);\n    for(int i = 0; i < result.size(); i++)\n    {\n        for(int j = 0; j < result[i].boxPoint.size(); j++)\n        {\n            float x = (result[i].boxPoint[j].x-(wpad/2))/scale;\n            float y = (result[i].boxPoint[j].y-(hpad/2))/scale;\n            x = std::max(std::min(x,(float)(width-1)),0.f);\n            y = std::max(std::min(y,(float)(height-1)),0.f);\n            result[i].boxPoint[j].x = x;\n            result[i].boxPoint[j].y = y;\n        }\n    }\n\n    return result;\n}\n\ntemplate<class ForwardIterator>\ninline static size_t argmax(ForwardIterator first, ForwardIterator last) {\n    return std::distance(first, std::max_element(first, last));\n}\n\nTextLine scoreToTextLine(const std::vector<float>& outputData, int h, int w)\n{\n    int keySize = keys.size();\n    std::string strRes;\n    std::vector<float> scores;\n    int lastIndex = 0;\n    int maxIndex;\n    float maxValue;\n\n    for (int i = 0; i < h; i++)\n    {\n        maxIndex = 0;\n        maxValue = -1000.f;\n\n        maxIndex = int(argmax(outputData.begin()+i*w, outputData.begin()+i*w+w));\n        maxValue = float(*std::max_element(outputData.begin()+i*w, outputData.begin()+i*w+w));// / partition;\n        if (maxIndex > 0 && maxIndex < keySize && (!(i > 0 && maxIndex == lastIndex))) {\n            scores.emplace_back(maxValue);\n            strRes.append(keys[maxIndex - 1]);\n        }\n        lastIndex = maxIndex;\n    }\n    return { strRes, scores };\n}\n\nTextLine getTextLine(const cv::Mat & src)\n{\n    float scale = (float)dstHeight / (float)src.rows;\n    int dstWidth = int((float)src.cols * scale);\n\n    cv::Mat srcResize;\n    cv::resize(src, srcResize, cv::Size(dstWidth, dstHeight));\n    //if you use PP-OCRv3 you should change PIXEL_RGB to PIXEL_RGB2BGR\n    ncnn::Mat input = ncnn::Mat::from_pixels(srcResize.data, ncnn::Mat::PIXEL_RGB,srcResize.cols, srcResize.rows);\n    const float mean_vals[3] = { 127.5, 127.5, 127.5 };\n    const float norm_vals[3] = { 1.0 / 127.5, 1.0 / 127.5, 1.0 / 127.5 };\n    input.substract_mean_normalize(mean_vals, norm_vals);\n\n    ncnn::Extractor extractor = crnnNet.create_extractor();\n    //extractor.set_num_threads(2);\n    extractor.input(\"input\", input);\n\n    ncnn::Mat out;\n    extractor.extract(\"out\", out);\n    float* floatArray = (float*)out.data;\n    std::vector<float> outputData(floatArray, floatArray + out.h * out.w);\n    TextLine res = scoreToTextLine(outputData, out.h, out.w);\n    return res;\n}\n\nstd::vector<TextLine> getTextLines(std::vector<cv::Mat> & partImg) {\n    int size = partImg.size();\n    std::vector<TextLine> textLines(size);\n    for (int i = 0; i < size; ++i)\n    {\n        TextLine textLine = getTextLine(partImg[i]);\n        textLines[i] = textLine;\n    }\n    return textLines;\n}\n\nextern \"C\" {\n\n// FIXME DeleteGlobalRef is missing for objCls\nstatic jclass objCls = NULL;\nstatic jmethodID constructortorId;\nstatic jfieldID x0Id;\nstatic jfieldID y0Id;\nstatic jfieldID x1Id;\nstatic jfieldID y1Id;\nstatic jfieldID x2Id;\nstatic jfieldID y2Id;\nstatic jfieldID x3Id;\nstatic jfieldID y3Id;\nstatic jfieldID labelId;\nstatic jfieldID probId;\n\nJNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)\n{\n    __android_log_print(ANDROID_LOG_DEBUG, \"PaddleOCRNcnn\", \"JNI_OnLoad\");\n\n    ncnn::create_gpu_instance();\n\n    return JNI_VERSION_1_4;\n}\n\nJNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved)\n{\n    __android_log_print(ANDROID_LOG_DEBUG, \"PaddleOCRNcnn\", \"JNI_OnUnload\");\n\n    ncnn::destroy_gpu_instance();\n}\n\n// public native boolean Init(AssetManager mgr);\nJNIEXPORT jboolean JNICALL Java_com_tencent_paddleocrncnn_PaddleOCRNcnn_Init(JNIEnv* env, jobject thiz, jobject assetManager)\n{\n    ncnn::Option opt;\n    opt.lightmode = true;\n    opt.num_threads = 4;\n    opt.blob_allocator = &g_blob_pool_allocator;\n    opt.workspace_allocator = &g_workspace_pool_allocator;\n    opt.use_packing_layout = true;\n\n    // use vulkan compute\n    if (ncnn::get_gpu_count() != 0)\n        opt.use_vulkan_compute = true;\n\n    AAssetManager* mgr = AAssetManager_fromJava(env, assetManager);\n\n    dbNet.opt = opt;\n    crnnNet.opt = opt;\n\n    // init param\n    {\n        int ret = dbNet.load_param(mgr, \"pdocrv2.0_det-op.param\");\n        if (ret != 0)\n        {\n            __android_log_print(ANDROID_LOG_WARN, \"PaddleocrNcnn\", \"load_dbNet_param failed\");\n            return JNI_FALSE;\n        }\n        \n        ret = crnnNet.load_param(mgr, \"pdocrv2.0_rec-op.param\");\n        \n        if (ret != 0)\n        {\n            __android_log_print(ANDROID_LOG_WARN, \"PaddleocrNcnn\", \"load_crnnNet_param failed\");\n            return JNI_FALSE;\n        }\n    }\n\n    // init bin\n    {\n        int ret = dbNet.load_model(mgr, \"pdocrv2.0_det-op.bin\");\n        if (ret != 0)\n        {\n            __android_log_print(ANDROID_LOG_WARN, \"PaddleocrNcnn\", \"load_dbNet_model failed\");\n            return JNI_FALSE;\n        }\n        \n        ret = crnnNet.load_model(mgr, \"pdocrv2.0_rec-op.bin\");\n        \n        if (ret != 0)\n        {\n            __android_log_print(ANDROID_LOG_WARN, \"PaddleocrNcnn\", \"load_crnnNet_model failed\");\n            return JNI_FALSE;\n        }\n    }\n    \n    //load keys\n    char *buffer = readKeysFromAssets(mgr);\n    if (buffer != NULL) {\n        std::istringstream inStr(buffer);\n        std::string line;\n        int size = 0;\n        while (getline(inStr, line)) {\n            keys.emplace_back(line);\n            size++;\n        }\n        free(buffer);\n    } else {\n        return false;\n    }\n    \n    \n    // init jni glue\n    jclass localObjCls = env->FindClass(\"com/tencent/paddleocrncnn/PaddleOCRNcnn$Obj\");\n    objCls = reinterpret_cast<jclass>(env->NewGlobalRef(localObjCls));\n\n    constructortorId = env->GetMethodID(objCls, \"<init>\", \"(Lcom/tencent/paddleocrncnn/PaddleOCRNcnn;)V\");\n\n    x0Id = env->GetFieldID(objCls, \"x0\", \"F\");\n    y0Id = env->GetFieldID(objCls, \"y0\", \"F\");\n    x1Id = env->GetFieldID(objCls, \"x1\", \"F\");\n    y1Id = env->GetFieldID(objCls, \"y1\", \"F\");\n    x2Id = env->GetFieldID(objCls, \"x2\", \"F\");\n    y2Id = env->GetFieldID(objCls, \"y2\", \"F\");\n    x3Id = env->GetFieldID(objCls, \"x3\", \"F\");\n    y3Id = env->GetFieldID(objCls, \"y3\", \"F\");\n    labelId = env->GetFieldID(objCls, \"label\", \"Ljava/lang/String;\");\n    probId = env->GetFieldID(objCls, \"prob\", \"F\");\n\n    return JNI_TRUE;\n}\n\n// public native Obj[] Detect(Bitmap bitmap, boolean use_gpu);\nJNIEXPORT jobjectArray JNICALL Java_com_tencent_paddleocrncnn_PaddleOCRNcnn_Detect(JNIEnv* env, jobject thiz, jobject bitmap, jboolean use_gpu)\n{\n    if (use_gpu == JNI_TRUE && ncnn::get_gpu_count() == 0)\n    {\n        return NULL;\n        //return env->NewStringUTF(\"no vulkan capable gpu\");\n    }\n\n    AndroidBitmapInfo info;\n    AndroidBitmap_getInfo(env, bitmap, &info);\n    const int width = info.width;\n    const int height = info.height;\n    if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888)\n        return NULL;\n\n    ncnn::Mat in = ncnn::Mat::from_android_bitmap(env, bitmap, ncnn::Mat::PIXEL_RGB);\n\n    cv::Mat rgb = cv::Mat::zeros(in.h,in.w,CV_8UC3);\n    in.to_pixels(rgb.data, ncnn::Mat::PIXEL_RGB);\n\n    std::vector<TextBox> objects; \n    objects = getTextBoxes(rgb, 0.4, 0.3, 2.0);\n\n    std::vector<cv::Mat> partImages = getPartImages(rgb, objects);\n    std::vector<TextLine> textLines = getTextLines(partImages);\n\n    if(textLines.size() > 0)\n    {\n        for(int i = 0; i < textLines.size(); i++)\n            objects[i].text = textLines[i].text;\n    }\n    // objects to Obj[]\n    jobjectArray jObjArray = env->NewObjectArray(objects.size(), objCls, NULL);\n\n    for (size_t i=0; i<objects.size(); i++)\n    {\n        jobject jObj = env->NewObject(objCls, constructortorId, thiz);\n\n        float x0 = objects[i].boxPoint[0].x;\n        float y0 = objects[i].boxPoint[0].y;\n        float x1 = objects[i].boxPoint[1].x;\n        float y1 = objects[i].boxPoint[1].y;\n        float x2 = objects[i].boxPoint[2].x;\n        float y2 = objects[i].boxPoint[2].y;\n        float x3 = objects[i].boxPoint[3].x;\n        float y3 = objects[i].boxPoint[3].y;\n\n        env->SetFloatField(jObj, x0Id, x0);\n        env->SetFloatField(jObj, y0Id, y0);\n        env->SetFloatField(jObj, x1Id, x1);\n        env->SetFloatField(jObj, y1Id, y1);\n        env->SetFloatField(jObj, x2Id, x2);\n        env->SetFloatField(jObj, y2Id, y2);\n        env->SetFloatField(jObj, x3Id, x3);\n        env->SetFloatField(jObj, y3Id, y3);\n        env->SetObjectField(jObj, labelId, env->NewStringUTF(objects[i].text.c_str()));\n        env->SetFloatField(jObj, probId, objects[i].score);\n\n        env->SetObjectArrayElement(jObjArray, i, jObj);\n    }\n\n    return jObjArray;\n}\n\n}\n"
  },
  {
    "path": "app/src/main/res/layout/main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:orientation=\"vertical\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\">\n\n    <LinearLayout\n        android:orientation=\"horizontal\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\">\n\n    <Button\n        android:id=\"@+id/buttonImage\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"选图\" />\n    <Button\n        android:id=\"@+id/buttonCamera\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"拍照\" />\n    <Button\n        android:id=\"@+id/buttonDetect\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"识别-cpu\" />\n    <Button\n        android:id=\"@+id/buttonDetectGPU\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"识别-gpu\" />\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/imageView\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:layout_weight=\"1\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">paddleocr_ncnn</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/xml/file_paths.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<paths xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <external-path\n        name=\"my_images\"\n        path=\".\"/>\n</paths>"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    repositories {\n        jcenter()\n        google()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:3.5.0'\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n        google()\n    }\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Sun Aug 25 10:34:48 CST 2019\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-5.4.1-all.zip\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "local.properties",
    "content": "## This file must *NOT* be checked into Version Control Systems,\n# as it contains information specific to your local configuration.\n#\n# Location of the SDK. This is only used by Gradle.\n# For customization when using a Version Control System, please read the\n# header note.\n#Wed Sep 15 14:52:33 CST 2021\nsdk.dir=D\\:\\\\Android\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app'\n"
  }
]