[
  {
    "path": ".gitignore",
    "content": "# built application files\n*.apk\n*.ap_\n*.com\n*.class\n*.dll\n*.exe\n*.o\n\n# files for the dex VM\n*.dex\n\n# Java class files\n*.class\n\n# generated files\nbin/\ngen/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Intellij project files\n*.iml\n*.ipr\n*.iws\n.idea/\n.DS_Store\n.idea/\n.gradle\nbuild/\n*.iml\nout/"
  },
  {
    "path": ".gitmodules",
    "content": "\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: android\nandroid:\n  components:\n    - platform-tools\n    - tools\n    - build-tools-26.0.2\n    - android-25\n    - extra-android-support\n    - extra-android-m2repository\n\nscript:\n    - ./gradlew check -i\n    - ./gradlew assemble\n\nafter_failure:\n  - cat /home/travis/build/Bilibili/DanmakuFlameMaster/DanmakuFlameMaster/build/outputs/lint-results.html\n  - cat /home/travis/build/Bilibili/DanmakuFlameMaster/DanmakuFlameMaster/build/outputs/lint-results.xml\n"
  },
  {
    "path": "DanmakuFlameMaster/build.gradle",
    "content": "/*\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\napply plugin: 'com.android.library'\n\ndef SourcePath = 'src/main/'\n\nandroid {\n    compileSdkVersion 25\n    buildToolsVersion \"26.0.2\"\n\n    defaultConfig {\n        minSdkVersion 9\n        targetSdkVersion 25\n        versionName VERSION_NAME\n        versionCode Integer.parseInt(VERSION_CODE)\n    }\n\n    sourceSets {\n        main {\n            manifest.srcFile \"${SourcePath}AndroidManifest.xml\"\n            java.srcDirs = [\"${SourcePath}java\"]\n            jniLibs.srcDirs = [\"${SourcePath}libs\"]\n        }\n        androidTest.setRoot(\"${SourcePath}../androidTest\")\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_7\n        targetCompatibility JavaVersion.VERSION_1_7\n    }\n}\nif (rootProject.file('gradle/gradle-mvn-push.gradle').exists()) {\n    apply from: rootProject.file('gradle/gradle-mvn-push.gradle')\n}\nif (rootProject.file('gradle/gradle-bintray-upload.gradle').exists()) {\n    apply from: rootProject.file('gradle/gradle-bintray-upload.gradle')\n}"
  },
  {
    "path": "DanmakuFlameMaster/gradle.properties",
    "content": "POM_NAME=DanmakuFlameMaster\nBINTRAY_POM_NAME=dfm\nPOM_ARTIFACT_ID=dfm\nPOM_PACKAGING=aar"
  },
  {
    "path": "DanmakuFlameMaster/src/androidTest/java/tv/cjump/jni/NativeBitmapFactoryTest.java",
    "content": "package tv.cjump.jni;\n\nimport android.app.ActivityManager;\nimport android.app.Instrumentation;\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.os.Debug;\nimport master.flame.danmaku.danmaku.util.SystemClock;\nimport android.test.InstrumentationTestCase;\nimport android.util.Log;\n\nimport junit.framework.Assert;\nimport junit.framework.TestCase;\n\nimport java.util.ArrayList;\nimport java.util.Locale;\n\n/**\n * Created by ch on 15-6-12.\n */\npublic class NativeBitmapFactoryTest extends InstrumentationTestCase {\n\n    private static final int DEFAULT_MESSAGE_SIZE = 1024;\n    private static final int BYTES_IN_MEGABYTE = 1024 * 1024;\n\n    private static final int BITMAP_WIDTH = 200;\n    private static final int BITMAP_HEIGHT = 200;\n    private static final String TAG = NativeBitmapFactoryTest.class.getSimpleName();\n\n    public void testLoadLibs() {\n        NativeBitmapFactory.loadLibs();\n        boolean isInNativeAlloc = NativeBitmapFactory.isInNativeAlloc();\n        Assert.assertTrue(\"NativeBitmapFactory is not supported on your OS\", isInNativeAlloc);\n    }\n\n    public void testNativeBitmap() {\n        Bitmap bitmap = NativeBitmapFactory.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);\n        accessBitmap(bitmap);\n        bitmap.recycle();\n        gcAndWait();\n    }\n\n    public void testDalvikBitmap() {\n        Bitmap bitmap = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);\n        accessBitmap(bitmap);\n        bitmap.recycle();\n        gcAndWait();\n    }\n\n    public void testNativeBitmaps() {\n        StringBuilder sb = new StringBuilder(DEFAULT_MESSAGE_SIZE);\n        appendValue(sb, \"\\n\\n\", \"===== before create 50 NativeBitmap\", \"\\n\\n\");\n        updateHeapValue(sb);\n        final String message = sb.toString();\n        Log.i(TAG, message);\n        sb = new StringBuilder();\n        ArrayList<Bitmap> bitmaps = new ArrayList<>();\n        for (int i = 0; i < 150; i++) {\n            Bitmap bitmap = NativeBitmapFactory.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);\n            accessBitmap(bitmap);\n            bitmaps.add(bitmap);\n        }\n        updateHeapValue(sb);\n        Log.d(TAG, sb.toString());\n        for (Bitmap bitmap : bitmaps) {\n            bitmap.recycle();\n        }\n        gcAndWait();\n    }\n\n    public Context getContext(){\n        return getInstrumentation().getTargetContext();\n    }\n\n    private void updateHeapValue(StringBuilder sb) {\n\n        ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();\n        ActivityManager activityManager = (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);\n        activityManager.getMemoryInfo(mi);\n        long availableMegs = mi.availMem;\n        final Runtime runtime = Runtime.getRuntime();\n        final long heapMemory = runtime.totalMemory() - runtime.freeMemory();\n        // When changing format of output below, make sure to sync \"scripts/test_runner.py\" as well.\n        appendSize(sb, \"System availMem:          \", availableMegs, \"\\n\");\n        appendSize(sb, \"Java heap size:          \", heapMemory, \"\\n\");\n        appendSize(sb, \"Native heap size:        \", Debug.getNativeHeapSize(), \"\\n\");\n    }\n\n    public void testDalvikBitmaps() {\n        StringBuilder sb = new StringBuilder(DEFAULT_MESSAGE_SIZE);\n        appendValue(sb, \"\\n\\n\", \"===== before create 50 DalvikBitmap\", \"\\n\\n\");\n        updateHeapValue(sb);\n        final String message = sb.toString();\n        Log.i(TAG, message);\n        sb = new StringBuilder();\n        ArrayList<Bitmap> bitmaps = new ArrayList<>();\n        for (int i = 0; i < 150; i++) {\n            Bitmap bitmap = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);\n            accessBitmap(bitmap);\n            bitmaps.add(bitmap);\n        }\n        updateHeapValue(sb);\n        Log.d(TAG, sb.toString());\n        for (Bitmap bitmap : bitmaps) {\n            bitmap.recycle();\n        }\n        gcAndWait();\n    }\n\n    public void testReleaseLibs() {\n        NativeBitmapFactory.releaseLibs();\n    }\n\n\n    private void accessBitmap(Bitmap bitmap) {\n        boolean result = (bitmap != null && bitmap.getWidth() == BITMAP_WIDTH && bitmap.getHeight() == BITMAP_HEIGHT);\n        if (result) {\n            if (android.os.Build.VERSION.SDK_INT >= 17 && !bitmap.isPremultiplied()) {\n                bitmap.setPremultiplied(true);\n            }\n            Canvas canvas = new Canvas(bitmap);\n            Paint paint = new Paint();\n            paint.setColor(Color.RED);\n            paint.setTextSize(20f);\n            canvas.drawRect(0f, 0f, (float) bitmap.getWidth(), (float) bitmap.getHeight(),\n                    paint);\n            canvas.drawText(\"TestLib\", 0, 0, paint);\n        }\n    }\n\n    private void gcAndWait() {\n        System.gc();\n        SystemClock.sleep(10000);\n//        try {\n//            Debug.dumpHprofData(\"/sdcard/nbf_test_dump.hprof\");\n//        } catch (IOException e) {\n//            e.printStackTrace();\n//        }\n    }\n\n    private static void appendSize(StringBuilder sb, String prefix, long bytes, String suffix) {\n        String value = String.format(Locale.getDefault(), \"%.2f\", (float) bytes / BYTES_IN_MEGABYTE);\n        appendValue(sb, prefix, value + \" MB\", suffix);\n    }\n\n    private static void appendTime(StringBuilder sb, String prefix, long timeMs, String suffix) {\n        appendValue(sb, prefix, timeMs + \" ms\", suffix);\n    }\n\n    private static void appendNumber(StringBuilder sb, String prefix, long number, String suffix) {\n        appendValue(sb, prefix, number + \"\", suffix);\n    }\n\n    private static void appendValue(StringBuilder sb, String prefix, String value, String suffix) {\n        sb.append(prefix).append(value).append(suffix);\n    }\n\n}"
  },
  {
    "path": "DanmakuFlameMaster/src/main/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"src\" path=\"java\"/>\n\t<classpathentry kind=\"con\" path=\"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK\"/>\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.LIBRARIES\"/>\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.DEPENDENCIES\"/>\n\t<classpathentry kind=\"src\" path=\"gen\"/>\n\t<classpathentry kind=\"output\" path=\"bin/classes\"/>\n</classpath>\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<projectDescription>\r\n\t<name>DanmakuFlameMaster</name>\r\n\t<comment></comment>\r\n\t<projects>\r\n\t</projects>\r\n\t<buildSpec>\r\n\t\t<buildCommand>\r\n\t\t\t<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>\r\n\t\t\t<arguments>\r\n\t\t\t</arguments>\r\n\t\t</buildCommand>\r\n\t\t<buildCommand>\r\n\t\t\t<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>\r\n\t\t\t<arguments>\r\n\t\t\t</arguments>\r\n\t\t</buildCommand>\r\n\t\t<buildCommand>\r\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\r\n\t\t\t<arguments>\r\n\t\t\t</arguments>\r\n\t\t</buildCommand>\r\n\t\t<buildCommand>\r\n\t\t\t<name>com.android.ide.eclipse.adt.ApkBuilder</name>\r\n\t\t\t<arguments>\r\n\t\t\t</arguments>\r\n\t\t</buildCommand>\r\n\t</buildSpec>\r\n\t<natures>\r\n\t\t<nature>com.android.ide.eclipse.adt.AndroidNature</nature>\r\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\r\n\t</natures>\r\n</projectDescription>\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6\norg.eclipse.jdt.core.compiler.compliance=1.6\norg.eclipse.jdt.core.compiler.source=1.6\norg.eclipse.jdt.core.formatter.align_type_members_on_columns=false\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16\norg.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16\norg.eclipse.jdt.core.formatter.alignment_for_assignment=0\norg.eclipse.jdt.core.formatter.alignment_for_binary_expression=16\norg.eclipse.jdt.core.formatter.alignment_for_compact_if=16\norg.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80\norg.eclipse.jdt.core.formatter.alignment_for_enum_constants=0\norg.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16\norg.eclipse.jdt.core.formatter.alignment_for_method_declaration=0\norg.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16\norg.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80\norg.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16\norg.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16\norg.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16\norg.eclipse.jdt.core.formatter.blank_lines_after_imports=1\norg.eclipse.jdt.core.formatter.blank_lines_after_package=1\norg.eclipse.jdt.core.formatter.blank_lines_before_field=0\norg.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0\norg.eclipse.jdt.core.formatter.blank_lines_before_imports=1\norg.eclipse.jdt.core.formatter.blank_lines_before_member_type=1\norg.eclipse.jdt.core.formatter.blank_lines_before_method=1\norg.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1\norg.eclipse.jdt.core.formatter.blank_lines_before_package=1\norg.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1\norg.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1\norg.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line\norg.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line\norg.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=true\norg.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=true\norg.eclipse.jdt.core.formatter.comment.format_block_comments=true\norg.eclipse.jdt.core.formatter.comment.format_header=false\norg.eclipse.jdt.core.formatter.comment.format_html=true\norg.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true\norg.eclipse.jdt.core.formatter.comment.format_line_comments=true\norg.eclipse.jdt.core.formatter.comment.format_source_code=true\norg.eclipse.jdt.core.formatter.comment.indent_parameter_description=true\norg.eclipse.jdt.core.formatter.comment.indent_root_tags=true\norg.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert\norg.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert\norg.eclipse.jdt.core.formatter.comment.line_length=80\norg.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true\norg.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true\norg.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false\norg.eclipse.jdt.core.formatter.compact_else_if=true\norg.eclipse.jdt.core.formatter.continuation_indentation=2\norg.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2\norg.eclipse.jdt.core.formatter.disabling_tag=@formatter\\:off\norg.eclipse.jdt.core.formatter.enabling_tag=@formatter\\:on\norg.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false\norg.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true\norg.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true\norg.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true\norg.eclipse.jdt.core.formatter.indent_empty_lines=false\norg.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true\norg.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true\norg.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true\norg.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true\norg.eclipse.jdt.core.formatter.indentation.size=4\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert\norg.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert\norg.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert\norg.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert\norg.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert\norg.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert\norg.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert\norg.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert\norg.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert\norg.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert\norg.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert\norg.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert\norg.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert\norg.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert\norg.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert\norg.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert\norg.eclipse.jdt.core.formatter.join_lines_in_comments=true\norg.eclipse.jdt.core.formatter.join_wrapped_lines=true\norg.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false\norg.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false\norg.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false\norg.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false\norg.eclipse.jdt.core.formatter.lineSplit=300\norg.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false\norg.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false\norg.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0\norg.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1\norg.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true\norg.eclipse.jdt.core.formatter.tabulation.char=space\norg.eclipse.jdt.core.formatter.tabulation.size=4\norg.eclipse.jdt.core.formatter.use_on_off_tags=false\norg.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false\norg.eclipse.jdt.core.formatter.wrap_before_binary_operator=true\norg.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true\norg.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/.settings/org.eclipse.jdt.ui.prefs",
    "content": "eclipse.preferences.version=1\nformatter_profile=_AndroidCodeStyle\nformatter_settings_version=12\norg.eclipse.jdt.ui.ignorelowercasenames=true\norg.eclipse.jdt.ui.importorder=android;com;dalvik;gov;junit;libcore;net;org;java;javax;\norg.eclipse.jdt.ui.ondemandthreshold=99\norg.eclipse.jdt.ui.staticondemandthreshold=99\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n-->\n\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"master.flame.danmaku\"\n    android:versionCode=\"9025\"\n    android:versionName=\"0.9.25\" >\n\n    <uses-sdk\n        android:minSdkVersion=\"9\"\n        android:targetSdkVersion=\"22\" />\n\n</manifest>"
  },
  {
    "path": "DanmakuFlameMaster/src/main/build.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project name=\"DanmakuFlameMaster\" default=\"help\">\n\n    <!-- The local.properties file is created and updated by the 'android' tool.\n         It contains the path to the SDK. It should *NOT* be checked into\n         Version Control Systems. -->\n    <property file=\"local.properties\" />\n\n    <!-- The ant.properties file can be created by you. It is only edited by the\n         'android' tool to add properties to it.\n         This is the place to change some Ant specific build properties.\n         Here are some properties you may want to change/update:\n\n         source.dir\n             The name of the source directory. Default is 'src'.\n         out.dir\n             The name of the output directory. Default is 'bin'.\n\n         For other overridable properties, look at the beginning of the rules\n         files in the SDK, at tools/ant/build.xml\n\n         Properties related to the SDK location or the project target should\n         be updated using the 'android' tool with the 'update' action.\n\n         This file is an integral part of the build system for your\n         application and should be checked into Version Control Systems.\n\n         -->\n    <property file=\"ant.properties\" />\n\n    <!-- if sdk.dir was not set from one of the property file, then\n         get it from the ANDROID_HOME env var.\n         This must be done before we load project.properties since\n         the proguard config can use sdk.dir -->\n    <property environment=\"env\" />\n    <condition property=\"sdk.dir\" value=\"${env.ANDROID_HOME}\">\n        <isset property=\"env.ANDROID_HOME\" />\n    </condition>\n\n    <!-- The project.properties file is created and updated by the 'android'\n         tool, as well as ADT.\n\n         This contains project specific properties such as project target, and library\n         dependencies. Lower level build properties are stored in ant.properties\n         (or in .classpath for Eclipse projects).\n\n         This file is an integral part of the build system for your\n         application and should be checked into Version Control Systems. -->\n    <loadproperties srcFile=\"project.properties\" />\n\n    <!-- quick check on sdk.dir -->\n    <fail\n            message=\"sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable.\"\n            unless=\"sdk.dir\"\n    />\n\n    <!--\n        Import per project custom build rules if present at the root of the project.\n        This is the place to put custom intermediary targets such as:\n            -pre-build\n            -pre-compile\n            -post-compile (This is typically used for code obfuscation.\n                           Compiled code location: ${out.classes.absolute.dir}\n                           If this is not done in place, override ${out.dex.input.absolute.dir})\n            -post-package\n            -post-build\n            -pre-clean\n    -->\n    <import file=\"custom_rules.xml\" optional=\"true\" />\n\n    <!-- Import the actual build file.\n\n         To customize existing targets, there are two options:\n         - Customize only one target:\n             - copy/paste the target into this file, *before* the\n               <import> task.\n             - customize it to your needs.\n         - Customize the whole content of build.xml\n             - copy/paste the content of the rules files (minus the top node)\n               into this file, replacing the <import> task.\n             - customize to your needs.\n\n         ***********************\n         ****** IMPORTANT ******\n         ***********************\n         In all cases you must update the value of version-tag below to read 'custom' instead of an integer,\n         in order to avoid having your file be overridden by tools such as \"android update project\"\n    -->\n    <!-- version-tag: 1 -->\n    <import file=\"${sdk.dir}/tools/ant/build.xml\" />\n\n</project>\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/controller/CacheManagingDrawTask.java",
    "content": "/*\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage master.flame.danmaku.controller;\n\nimport android.os.Handler;\nimport android.os.HandlerThread;\nimport android.os.Message;\n\nimport master.flame.danmaku.danmaku.model.AbsDisplayer;\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\nimport master.flame.danmaku.danmaku.model.DanmakuTimer;\nimport master.flame.danmaku.danmaku.model.ICacheManager;\nimport master.flame.danmaku.danmaku.model.IDanmakus;\nimport master.flame.danmaku.danmaku.model.IDrawingCache;\nimport master.flame.danmaku.danmaku.model.android.CachingPolicy;\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext;\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext.DanmakuConfigTag;\nimport master.flame.danmaku.danmaku.model.android.Danmakus;\nimport master.flame.danmaku.danmaku.model.android.DrawingCache;\nimport master.flame.danmaku.danmaku.model.android.DrawingCachePoolManager;\nimport master.flame.danmaku.danmaku.model.objectpool.Pool;\nimport master.flame.danmaku.danmaku.model.objectpool.Pools;\nimport master.flame.danmaku.danmaku.renderer.IRenderer.RenderingState;\nimport master.flame.danmaku.danmaku.util.DanmakuUtils;\nimport master.flame.danmaku.danmaku.util.SystemClock;\nimport tv.cjump.jni.NativeBitmapFactory;\n\npublic class CacheManagingDrawTask extends DrawTask {\n\n    private static final int MAX_CACHE_SCREEN_SIZE = 3;\n\n    private int mMaxCacheSize = 2;\n\n    private CacheManager mCacheManager;\n\n    private DanmakuTimer mCacheTimer;\n\n    private final Object mDrawingNotify = new Object();\n    private int mRemaininCacheCount;\n\n    public CacheManagingDrawTask(DanmakuTimer timer, DanmakuContext config, TaskListener taskListener) {\n        super(timer, config, taskListener);\n        NativeBitmapFactory.loadLibs();\n        mMaxCacheSize = (int) Math.max(1024 * 1024 * 4, Runtime.getRuntime().maxMemory() * config.cachingPolicy.maxCachePoolSizeFactorPercentage);\n        mCacheManager = new CacheManager(mMaxCacheSize, MAX_CACHE_SCREEN_SIZE);\n        mRenderer.setCacheManager(mCacheManager);\n    }\n\n    @Override\n    protected void initTimer(DanmakuTimer timer) {\n        mTimer = timer;\n        mCacheTimer = new DanmakuTimer();\n        mCacheTimer.update(timer.currMillisecond);\n    }\n\n    @Override\n    public void addDanmaku(BaseDanmaku danmaku) {\n        super.addDanmaku(danmaku);\n        if (mCacheManager == null)\n            return;\n        mCacheManager.addDanmaku(danmaku);\n    }\n\n    @Override\n    public void invalidateDanmaku(BaseDanmaku item, boolean remeasure) {\n        super.invalidateDanmaku(item, remeasure);\n        if (mCacheManager == null) {\n            return;\n        }\n        mCacheManager.invalidateDanmaku(item, remeasure);\n    }\n\n    @Override\n    public void removeAllDanmakus(boolean isClearDanmakusOnScreen) {\n        super.removeAllDanmakus(isClearDanmakusOnScreen);\n        if (mCacheManager != null) {\n            mCacheManager.requestClearAll();\n        }\n    }\n\n    @Override\n    protected void onDanmakuRemoved(BaseDanmaku danmaku) {\n        super.onDanmakuRemoved(danmaku);\n        if (mCacheManager != null) {\n            if (++mRemaininCacheCount > 5) {  // control frequency (it does not require very precise\n                mCacheManager.requestClearTimeout();\n                mRemaininCacheCount = 0;\n            }\n        } else {\n            IDrawingCache<?> cache = danmaku.getDrawingCache();\n            if (cache != null) {\n                if (cache.hasReferences()) {\n                    cache.decreaseReference();\n                } else {\n                    cache.destroy();\n                }\n                danmaku.cache = null;\n            }\n        }\n    }\n\n    @Override\n    public RenderingState draw(AbsDisplayer displayer) {\n        RenderingState result = super.draw(displayer);\n        synchronized (mDrawingNotify) {\n            mDrawingNotify.notify();\n        }\n        if (result != null && mCacheManager != null) {\n            if (result.totalDanmakuCount - result.lastTotalDanmakuCount < -20) {\n                mCacheManager.requestClearTimeout();\n                mCacheManager.requestBuild(-mContext.mDanmakuFactory.MAX_DANMAKU_DURATION);\n            }\n        }\n        return result;\n    }\n\n    @Override\n    public void seek(long mills) {\n        super.seek(mills);\n        if (mCacheManager == null) {\n            start();\n        }\n        mCacheManager.seek(mills);\n    }\n\n    @Override\n    public void start() {\n        super.start();\n        NativeBitmapFactory.loadLibs();\n        if (mCacheManager == null) {\n            mCacheManager = new CacheManager(mMaxCacheSize, MAX_CACHE_SCREEN_SIZE);\n            mCacheManager.begin();\n            mRenderer.setCacheManager(mCacheManager);\n        } else {\n            mCacheManager.resume();\n        }\n    }\n\n    @Override\n    public void quit() {\n        super.quit();\n        reset();\n        mRenderer.setCacheManager(null);\n        if (mCacheManager != null) {\n            mCacheManager.end();\n            mCacheManager = null;\n        }\n        NativeBitmapFactory.releaseLibs();\n    }\n\n    @Override\n    public void prepare() {\n        if (mParser == null) {\n            return;\n        }\n        loadDanmakus(mParser);\n        mCacheManager.begin();\n    }\n\n    @Override\n    public void onPlayStateChanged(int state) {\n        super.onPlayStateChanged(state);\n        if (mCacheManager != null) {\n            mCacheManager.onPlayStateChanged(state);\n        }\n    }\n\n    @Override\n    public void requestSync(long fromTimeMills, long toTimeMills, long offsetMills) {\n        super.requestSync(fromTimeMills, toTimeMills, offsetMills);\n        if (mCacheManager != null) {\n            mCacheManager.seek(toTimeMills);\n        }\n    }\n\n    public class CacheManager implements ICacheManager {\n\n        @SuppressWarnings(\"unused\")\n        private static final String TAG = \"CacheManager\";\n        public static final byte RESULT_SUCCESS = 0;\n        public static final byte RESULT_FAILED = 1;\n        public static final byte RESULT_FAILED_OVERSIZE = 2;\n\n        public HandlerThread mThread;\n\n        Danmakus mCaches = new Danmakus();\n\n        DrawingCachePoolManager mCachePoolManager = new DrawingCachePoolManager();\n\n        Pool<DrawingCache> mCachePool = Pools.finitePool(mCachePoolManager, 800);\n\n        private int mMaxSize;\n\n        private int mRealSize;\n\n        private int mScreenSize = 3;\n\n        private CacheHandler mHandler;\n\n        private boolean mEndFlag;\n\n        public CacheManager(int maxSize, int screenSize) {\n            mEndFlag = false;\n            mRealSize = 0;\n            mMaxSize = maxSize;\n            mScreenSize = screenSize;\n        }\n\n        public void seek(long mills) {\n            if (mHandler == null)\n                return;\n            mHandler.requestCancelCaching();\n            mHandler.removeMessages(CacheHandler.BUILD_CACHES);\n            mHandler.obtainMessage(CacheHandler.SEEK, mills).sendToTarget();\n        }\n\n        @Override\n        public void addDanmaku(BaseDanmaku danmaku) {\n            if (mHandler != null) {\n                if (danmaku.isLive && danmaku.forceBuildCacheInSameThread) {\n                    if (!danmaku.isTimeOut()) {\n                        mHandler.createCache(danmaku);\n                    }\n                    return;\n                }\n                mHandler.obtainMessage(CacheHandler.ADD_DANMAKU, danmaku).sendToTarget();\n            }\n        }\n\n        public void invalidateDanmaku(BaseDanmaku danmaku, boolean remeasure) {\n            if (mHandler != null) {\n                mHandler.requestCancelCaching();\n                mHandler.obtainMessage(CacheHandler.REBUILD_CACHE, danmaku).sendToTarget();\n                mHandler.sendEmptyMessage(CacheHandler.DISABLE_CANCEL_FLAG);\n                requestBuild(0);\n            }\n        }\n\n        public void begin() {\n            mEndFlag = false;\n            if (mThread == null) {\n                mThread = new HandlerThread(\"DFM Cache-Building Thread\");\n                mThread.start();\n            }\n            if (mHandler == null)\n                mHandler = new CacheHandler(mThread.getLooper());\n            mHandler.begin();\n        }\n\n        public void end() {\n            mEndFlag = true;\n            synchronized (mDrawingNotify) {\n                mDrawingNotify.notifyAll();\n            }\n            if (mHandler != null) {\n                mHandler.removeCallbacksAndMessages(null);\n                mHandler.pause();\n                mHandler = null;\n            }\n            if (mThread != null) {\n                try {\n                    mThread.join();\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n                mThread.quit();\n                mThread = null;\n            }\n        }\n\n        public void resume() {\n            if (mHandler != null) {\n                mHandler.resume();\n            } else {\n                begin();\n            }\n        }\n\n        public void onPlayStateChanged(int state) {\n            if (mHandler != null) {\n                mHandler.onPlayStateChanged(state == IDrawTask.PLAY_STATE_PLAYING);\n            }\n        }\n\n        public float getPoolPercent() {\n            if (mMaxSize == 0) {\n                return 0;\n            }\n            return mRealSize / (float) mMaxSize;\n        }\n\n        public boolean isPoolFull() {\n            return mRealSize + 5120 >= mMaxSize;\n        }\n\n        private void evictAll() {\n            if (mCaches != null) {\n                mCaches.forEach(new IDanmakus.DefaultConsumer<BaseDanmaku>() {\n                    @Override\n                    public int accept(BaseDanmaku danmaku) {\n                        entryRemoved(true, danmaku, null);\n                        return ACTION_CONTINUE;\n                    }\n                });\n                mCaches.clear();\n            }\n            mRealSize = 0;\n        }\n\n        private void evictAllNotInScreen() {\n            if (mCaches != null) {\n                mCaches.forEach(new IDanmakus.DefaultConsumer<BaseDanmaku>() {\n                    @Override\n                    public int accept(BaseDanmaku danmaku) {\n                        if (danmaku.isOutside()) {\n                            entryRemoved(true, danmaku, null);\n                            return ACTION_REMOVE;\n                        }\n                        return ACTION_CONTINUE;\n                    }\n                });\n            }\n        }\n\n        protected void entryRemoved(boolean evicted, BaseDanmaku oldValue, BaseDanmaku newValue) {\n            IDrawingCache<?> cache = oldValue.getDrawingCache();\n            if (cache != null) {\n                long releasedSize = clearCache(oldValue);\n                if (oldValue.isTimeOut()) {\n                    mContext.getDisplayer().getCacheStuffer().releaseResource(oldValue);\n                }\n                if (releasedSize <= 0) return;\n                mRealSize -= releasedSize;\n                mCachePool.release((DrawingCache) cache);\n            }\n        }\n\n        private long clearCache(BaseDanmaku oldValue) {\n            IDrawingCache cache = oldValue.cache;\n            if (cache == null) {\n                return 0;\n            }\n            if (cache.hasReferences()) {\n                cache.decreaseReference();\n                oldValue.cache = null;\n                return 0;\n            }\n            long size = sizeOf(oldValue);\n            cache.destroy();\n            oldValue.cache = null;\n            return size;\n        }\n\n        protected int sizeOf(BaseDanmaku value) {\n            if (value.cache != null && !value.cache.hasReferences()) {\n                return value.cache.size();\n            }\n            return 0;\n        }\n\n        private void clearCachePool() {\n            DrawingCache item;\n            while ((item = mCachePool.acquire()) != null) {\n                item.destroy();\n            }\n        }\n\n        private boolean push(BaseDanmaku item, int itemSize, boolean forcePush) {\n            int size = itemSize; //sizeOf(item);\n            if (size > 0) {\n                clearTimeOutAndFilteredCaches(size, forcePush);\n                // may be a risk of OOM if (mRealSize + size) is still larger than mMaxSize\n            }\n            this.mCaches.addItem(item);\n            mRealSize += size;\n//Log.i(\"DFM CACHE\", \"realsize:\"+mRealSize + \",size\" + size);\n            return true;\n        }\n\n        private void clearTimeOutCaches() {\n            mCaches.forEach(new IDanmakus.DefaultConsumer<BaseDanmaku>() {\n                @Override\n                public int accept(BaseDanmaku val) {\n                    if (val.isTimeOut()) {\n                        IDrawingCache<?> cache = val.cache;\n                        if (mContext.cachingPolicy.periodOfRecycle == CachingPolicy.CACHE_PERIOD_NOT_RECYCLE && cache != null && !cache.hasReferences()) {\n                            if (cache.size() / (float) mMaxCacheSize < mContext.cachingPolicy.forceRecyleThreshold) {\n                                return ACTION_CONTINUE;\n                            }\n                            //else 回收尺寸过大的cache\n                        }\n                        if (!mEndFlag) {\n                            synchronized (mDrawingNotify) {\n                                try {\n                                    mDrawingNotify.wait(30);\n                                } catch (InterruptedException e) {\n                                    e.printStackTrace();\n                                    return ACTION_BREAK;\n                                }\n                            }\n                        }\n                        entryRemoved(false, val, null);\n                        return ACTION_REMOVE;\n                    } else {\n                        return ACTION_BREAK;\n                    }\n                }\n            });\n        }\n\n        private BaseDanmaku findReusableCache(final BaseDanmaku refDanmaku,\n                                              final boolean strictMode,\n                                              final int maximumTimes) {\n            int slopPixel = 0;\n            if (!strictMode) {\n                slopPixel = mDisp.getSlopPixel() * 2;\n            }\n            final int finalSlopPixel = slopPixel + mContext.cachingPolicy.reusableOffsetPixel;\n            IDanmakus.Consumer<BaseDanmaku, BaseDanmaku> consumer = new IDanmakus.Consumer<BaseDanmaku, BaseDanmaku>() {\n                int count = 0;\n                BaseDanmaku mResult;\n\n                @Override\n                public BaseDanmaku result() {\n                    return mResult;\n                }\n\n                @Override\n                public int accept(BaseDanmaku danmaku) {\n                    if (count++ >= maximumTimes) {\n                        return ACTION_BREAK;\n                    }\n                    IDrawingCache<?> cache = danmaku.getDrawingCache();\n                    if (cache == null || cache.get() == null) {\n                        return ACTION_CONTINUE;\n                    }\n                    if (danmaku.paintWidth == refDanmaku.paintWidth\n                            && danmaku.paintHeight == refDanmaku.paintHeight\n                            && danmaku.underlineColor == refDanmaku.underlineColor\n                            && danmaku.borderColor == refDanmaku.borderColor\n                            && danmaku.textColor == refDanmaku.textColor\n                            && danmaku.text.equals(refDanmaku.text)\n                            && danmaku.tag == refDanmaku.tag) {\n                        mResult = danmaku;\n                        return ACTION_BREAK;\n                    }\n                    if (strictMode) {\n                        return ACTION_CONTINUE;\n                    }\n                    if (!danmaku.isTimeOut()) {\n                        return ACTION_BREAK;\n                    }\n                    if (cache.hasReferences()) {\n                        return ACTION_CONTINUE;\n                    }\n                    float widthGap = cache.width() - refDanmaku.paintWidth;\n                    float heightGap = cache.height() - refDanmaku.paintHeight;\n                    if (widthGap >= 0 && widthGap <= finalSlopPixel &&\n                            heightGap >= 0 && heightGap <= finalSlopPixel) {\n                        mResult = danmaku;\n                        return ACTION_BREAK;\n                    }\n                    return ACTION_CONTINUE;\n                }\n            };\n            mCaches.forEach(consumer);\n            return consumer.result();\n        }\n\n        public class CacheHandler extends Handler {\n\n            private static final int PREPARE = 0x1;\n\n            public static final int ADD_DANMAKU = 0x2;\n\n            public static final int BUILD_CACHES = 0x3;\n\n            public static final int CLEAR_TIMEOUT_CACHES = 0x4;\n\n            public static final int SEEK = 0x5;\n\n            public static final int QUIT = 0x6;\n\n            public static final int CLEAR_ALL_CACHES = 0x7;\n\n            public static final int CLEAR_OUTSIDE_CACHES = 0x8;\n\n            public static final int CLEAR_OUTSIDE_CACHES_AND_RESET = 0x9;\n\n            public static final int DISPATCH_ACTIONS = 0x10;\n\n            public static final int REBUILD_CACHE = 0x11;\n\n            public static final int DISABLE_CANCEL_FLAG = 0x12;\n\n            private boolean mPause;\n\n            private boolean mIsPlayerPause;\n\n            private boolean mSeekedFlag;\n\n            private boolean mCancelFlag;\n\n            public CacheHandler(android.os.Looper looper) {\n                super(looper);\n            }\n\n            public void requestCancelCaching() {\n                mCancelFlag = true;\n            }\n\n            @Override\n            public void handleMessage(Message msg) {\n                int what = msg.what;\n                switch (what) {\n                    case PREPARE:\n                        evictAllNotInScreen();\n                        for (int i = 0; i < 300; i++) {\n                            mCachePool.release(new DrawingCache());\n                        }\n                    case DISPATCH_ACTIONS:\n//Log.e(TAG,\"dispatch_actions:\"+mCacheTimer.currMillisecond+\":\"+mTimer.currMillisecond);\n                        long delayed = dispatchAction();\n                        if (delayed <= 0) {\n                            delayed = mContext.mDanmakuFactory.MAX_DANMAKU_DURATION / 2;\n                        }\n                        sendEmptyMessageDelayed(DISPATCH_ACTIONS, delayed);\n                        break;\n                    case BUILD_CACHES:\n                        removeMessages(BUILD_CACHES);\n                        boolean repositioned = ((mTaskListener != null && mReadyState == false) || mSeekedFlag);\n                        prepareCaches(repositioned);\n                        if (repositioned)\n                            mSeekedFlag = false;\n                        if (mTaskListener != null && mReadyState == false) {\n                            mTaskListener.ready();\n                            mReadyState = true;\n                        }\n//                        Log.i(TAG,\"BUILD_CACHES:\"+mCacheTimer.currMillisecond+\":\"+mTimer.currMillisecond);\n                        break;\n                    case ADD_DANMAKU:\n                        BaseDanmaku item = (BaseDanmaku) msg.obj;\n                        addDanmakuAndBuildCache(item);\n                        break;\n                    case REBUILD_CACHE:\n                        BaseDanmaku cacheitem = (BaseDanmaku) msg.obj;\n                        if (cacheitem != null) {\n                            IDrawingCache<?> cache = cacheitem.getDrawingCache();\n                            boolean requestRemeasure = 0 != (cacheitem.requestFlags & BaseDanmaku.FLAG_REQUEST_REMEASURE);\n                            if (!requestRemeasure && cache != null && cache.get() !=null && !cache.hasReferences()) {\n                                cache = DanmakuUtils.buildDanmakuDrawingCache(cacheitem, mDisp, (DrawingCache) cacheitem.cache, mContext.cachingPolicy.bitsPerPixelOfCache);\n                                cacheitem.cache = cache;\n                                push(cacheitem, 0, true);\n                                return;\n                            }\n                            if (cacheitem.isLive) {\n                                clearCache(cacheitem);\n                                createCache(cacheitem);\n                            } else {\n                                if (cache != null && cache.hasReferences()) {\n                                    cache.destroy();\n                                }\n                                entryRemoved(true, cacheitem, null);\n                                addDanmakuAndBuildCache(cacheitem);\n                            }\n                        }\n                        break;\n                    case CLEAR_TIMEOUT_CACHES:\n                        clearTimeOutCaches();\n                        break;\n                    case SEEK:\n                        Long seekMills = (Long) msg.obj;\n                        if (seekMills != null) {\n                            long seekCacheTime = seekMills.longValue();\n                            long oldCacheTime = mCacheTimer.currMillisecond;\n                            mCacheTimer.update(seekCacheTime);\n                            mSeekedFlag = true;\n                            long firstCacheTime = getFirstCacheTime();\n                            if (seekCacheTime > oldCacheTime || firstCacheTime - seekCacheTime > mContext.mDanmakuFactory.MAX_DANMAKU_DURATION) {\n                                evictAllNotInScreen();\n                            } else {\n                                clearTimeOutCaches();\n                            }\n                            prepareCaches(true);\n                            resume();\n                        }\n                        break;\n                    case QUIT:\n                        removeCallbacksAndMessages(null);\n                        mPause = true;\n                        evictAll();\n                        clearCachePool();\n                        this.getLooper().quit();\n                        break;\n                    case CLEAR_ALL_CACHES:\n                        evictAll();\n                        mCacheTimer.update(mTimer.currMillisecond - mContext.mDanmakuFactory.MAX_DANMAKU_DURATION);\n                        mSeekedFlag = true;\n                        break;\n                    case CLEAR_OUTSIDE_CACHES:\n                        evictAllNotInScreen();\n                        mCacheTimer.update(mTimer.currMillisecond);\n                        break;\n                    case CLEAR_OUTSIDE_CACHES_AND_RESET:\n                        evictAllNotInScreen();\n                        mCacheTimer.update(mTimer.currMillisecond);\n                        requestClear();\n                        break;\n                    case DISABLE_CANCEL_FLAG:\n                        mCancelFlag = false;\n                        break;\n                }\n            }\n\n            private long dispatchAction() {\n                if (mCacheTimer.currMillisecond <= mTimer.currMillisecond - mContext.mDanmakuFactory.MAX_DANMAKU_DURATION) {\n                    if (mContext.cachingPolicy.periodOfRecycle != CachingPolicy.CACHE_PERIOD_NOT_RECYCLE) {\n                        evictAllNotInScreen();\n                    }\n                    mCacheTimer.update(mTimer.currMillisecond);\n                    sendEmptyMessage(BUILD_CACHES);\n                    return 0;\n                }\n                float level = getPoolPercent();\n                BaseDanmaku firstCache = mCaches.first();\n                //TODO 如果firstcache大于当前时间超过半屏并且水位在0.5f以下,\n                long gapTime = firstCache != null ? firstCache.getActualTime() - mTimer.currMillisecond : 0;\n                long doubleScreenDuration = mContext.mDanmakuFactory.MAX_DANMAKU_DURATION * 2;\n                if (level < 0.6f && gapTime > mContext.mDanmakuFactory.MAX_DANMAKU_DURATION) {\n                    mCacheTimer.update(mTimer.currMillisecond);\n                    removeMessages(BUILD_CACHES);\n                    sendEmptyMessage(BUILD_CACHES);\n                    return 0;\n                } else if (level > 0.4f && gapTime < -doubleScreenDuration) {\n                    // clear timeout caches\n                    removeMessages(CLEAR_TIMEOUT_CACHES);\n                    sendEmptyMessage(CLEAR_TIMEOUT_CACHES);\n                    return 0;\n                }\n\n                if (level >= 0.9f) {\n                    return 0;\n                }\n                // check cache time\n                long deltaTime = mCacheTimer.currMillisecond - mTimer.currMillisecond;\n                if (firstCache != null && firstCache.isTimeOut() && deltaTime < -mContext.mDanmakuFactory.MAX_DANMAKU_DURATION) {\n                    mCacheTimer.update(mTimer.currMillisecond);\n                    sendEmptyMessage(CLEAR_OUTSIDE_CACHES);\n                    sendEmptyMessage(BUILD_CACHES);\n                    return 0;\n                } else if (deltaTime > doubleScreenDuration) {\n                    return 0;\n                }\n\n                removeMessages(BUILD_CACHES);\n                sendEmptyMessage(BUILD_CACHES);\n                return 0;\n            }\n\n            private void releaseDanmakuCache(BaseDanmaku item, DrawingCache cache) {\n                if (cache == null) {\n                    cache = (DrawingCache) item.cache;\n                }\n                item.cache = null;\n                if (cache == null) {\n                    return;\n                }\n                cache.destroy(); //fixme: consider hasReferences?\n                mCachePool.release(cache);\n            }\n\n            private void preMeasure() {\n                // pre measure\n                IDanmakus danmakus = null;\n                try {\n                    long begin = mTimer.currMillisecond;\n                    long end = begin + mContext.mDanmakuFactory.MAX_DANMAKU_DURATION * 2;\n                    danmakus = danmakuList.subnew(begin - mContext.mDanmakuFactory.MAX_DANMAKU_DURATION, end);\n                } catch (Exception e) {\n\n                }\n                if (danmakus == null || danmakus.isEmpty()) {\n                    return;\n                }\n                danmakus.forEach(new IDanmakus.DefaultConsumer<BaseDanmaku>() {\n                    @Override\n                    public int accept(BaseDanmaku item) {\n                        if (mPause || mCancelFlag) {\n                            return ACTION_BREAK;\n                        }\n                        if (!item.hasPassedFilter()) {\n                            mContext.mDanmakuFilters.filter(item, 0, 0, null, true, mContext);\n                        }\n                        if (item.isFiltered()) {\n                            return ACTION_CONTINUE;\n                        }\n                        if (!item.isMeasured()) {\n                            item.measure(mDisp, true);\n                        }\n                        if (!item.isPrepared()) {\n                            item.prepare(mDisp, true);\n                        }\n                        return ACTION_CONTINUE;\n                    }\n                });\n            }\n\n            private long prepareCaches(final boolean repositioned) {\n                preMeasure();\n                final long curr = mCacheTimer.currMillisecond - 30;\n                final long end = curr + mContext.mDanmakuFactory.MAX_DANMAKU_DURATION * mScreenSize;\n                if (end < mTimer.currMillisecond) {\n                    return 0;\n                }\n                final long startTime = SystemClock.uptimeMillis();\n                IDanmakus danmakus = null;\n                int tryCount = 0;\n                boolean hasException = false;\n                do {\n                    try {\n                        danmakus = danmakuList.subnew(curr, end);\n                    } catch (Exception e) {\n                        hasException = true;\n                        SystemClock.sleep(10);\n                    }\n                } while (++tryCount < 3 && danmakus == null && hasException);\n                if (danmakus == null) {\n                    mCacheTimer.update(end);\n                    return 0;\n                }\n                final BaseDanmaku first = danmakus.first();\n                final BaseDanmaku last = danmakus.last();\n                if (first == null || last == null) {\n                    mCacheTimer.update(end);\n                    return 0;\n                }\n                long deltaTime = first.getActualTime() - mTimer.currMillisecond;\n                long sleepTime = (deltaTime < 0 ? 30 : 30 + 10 * deltaTime / mContext.mDanmakuFactory.MAX_DANMAKU_DURATION);\n                sleepTime = Math.min(100, sleepTime);\n                if (repositioned) {\n                    sleepTime = 0;\n                }\n                final long finalSleepTime = sleepTime;\n\n                BaseDanmaku item = null;\n                long consumingTime = 0;\n                int orderInScreen = 0;\n                int currScreenIndex = 0;\n                final int sizeInScreen = danmakus.size();\n//                String message = \"\";\n\n                danmakus.forEach(new IDanmakus.DefaultConsumer<BaseDanmaku>() {\n                    int orderInScreen = 0;\n                    int currScreenIndex = 0;\n                    @Override\n                    public int accept(BaseDanmaku item) {\n                        if (mPause || mCancelFlag) {\n                            return ACTION_BREAK;\n                        }\n                        if (last.getActualTime() < mTimer.currMillisecond) {\n                            return ACTION_BREAK;\n                        }\n\n                        IDrawingCache<?> cache = item.getDrawingCache();\n                        if (cache != null && cache.get() != null) {\n                            return ACTION_CONTINUE;\n                        }\n\n                        if (repositioned == false && (item.isTimeOut() || !item.isOutside())) {\n                            return ACTION_CONTINUE;\n                        }\n\n                        if (!item.hasPassedFilter()) {\n                            mContext.mDanmakuFilters.filter(item, orderInScreen, sizeInScreen, null, true, mContext);\n                        }\n\n//Log.e(\"prepareCache\", currScreenIndex+\",\"+indexInScreen+\",\" + item.time+\"skip:\"+skip);\n                        if (item.priority == 0 && item.isFiltered()) {\n                            return ACTION_CONTINUE;\n                        }\n\n                        if (item.getType() == BaseDanmaku.TYPE_SCROLL_RL) {\n                            // 同屏弹幕密度只对滚动弹幕有效\n                            int screenIndex = (int) ((item.getActualTime() - curr) / mContext.mDanmakuFactory.MAX_DANMAKU_DURATION);\n                            if (currScreenIndex == screenIndex)\n                                orderInScreen++;\n                            else {\n                                orderInScreen = 0;\n                                currScreenIndex = screenIndex;\n                            }\n                        }\n\n                        if (!repositioned && !mIsPlayerPause) {\n                            try {\n                                synchronized (mDrawingNotify) {\n                                    mDrawingNotify.wait(finalSleepTime);\n                                }\n                            } catch (InterruptedException e) {\n                                e.printStackTrace();\n                                return ACTION_BREAK;\n                            }\n                        }\n\n                        // build cache\n                        buildCache(item, false);\n                        if (!repositioned) {\n                            long consumingTime = SystemClock.uptimeMillis() - startTime;\n                            if (consumingTime >= mContext.mDanmakuFactory.COMMON_DANMAKU_DURATION * mScreenSize) {\n//                            message = \"break at consumingTime out:\" + consumingTime;\n                                return ACTION_BREAK;\n                            }\n                        }\n                        return ACTION_CONTINUE;\n                    }\n                });\n                consumingTime = SystemClock.uptimeMillis() - startTime;\n                if (item != null) {\n                    mCacheTimer.update(item.getTime());\n//Log.i(\"cache\",\"stop at :\"+item.time+\",\"+count+\",size:\"+danmakus.size()+\",\"+message);\n                } else {\n                    mCacheTimer.update(end);\n                }\n                return consumingTime;\n            }\n\n            public boolean createCache(BaseDanmaku item) {\n                // measure\n                if (!item.isMeasured()) {\n                    item.measure(mDisp, true);\n                }\n                DrawingCache cache = null;\n                try {\n                    cache = mCachePool.acquire();\n                    cache = DanmakuUtils.buildDanmakuDrawingCache(item, mDisp, cache, mContext.cachingPolicy.bitsPerPixelOfCache);\n                    item.cache = cache;\n                } catch (OutOfMemoryError e) {\n//Log.e(\"cache\", \"break at error: oom\");\n                    if (cache != null) {\n                        mCachePool.release(cache);\n                    }\n                    item.cache = null;\n                    return false;\n                } catch (Exception e) {\n//Log.e(\"cache\", \"break at exception:\" + e.getMessage());\n                    if (cache != null) {\n                        mCachePool.release(cache);\n                    }\n                    item.cache = null;\n                    return false;\n                }\n                return true;\n            }\n\n            private byte buildCache(BaseDanmaku item, boolean forceInsert) {\n\n                // measure\n                if (!item.isMeasured()) {\n                    item.measure(mDisp, true);\n                }\n\n                DrawingCache cache = null;\n                try {\n                    // try to find reuseable cache\n                    BaseDanmaku danmaku = findReusableCache(item, true, mContext.cachingPolicy.maxTimesOfStrictReusableFinds);\n                    if (danmaku != null) {\n                        cache = (DrawingCache) danmaku.cache;\n                    }\n                    if (cache != null) {\n                        cache.increaseReference();\n                        item.cache = cache;\n//Log.w(\"cache\", danmaku.text + \"DrawingCache hit!!:\" + item.paintWidth + \",\" + danmaku.paintWidth);\n                        mCacheManager.push(item, 0, forceInsert);\n                        return RESULT_SUCCESS;\n                    }\n\n                    // try to find reuseable cache from timeout || no-refrerence caches\n                    danmaku = findReusableCache(item, false, mContext.cachingPolicy.maxTimesOfReusableFinds);\n                    if (danmaku != null) {\n                        cache = (DrawingCache) danmaku.cache;\n                    }\n                    if (cache != null) {\n                        danmaku.cache = null;\n//Log.e(\"cache\", danmaku.text + \"DrawingCache hit!!:\" + item.paintWidth + \",\" + danmaku.paintWidth);\n                        cache = DanmakuUtils.buildDanmakuDrawingCache(item, mDisp, cache, mContext.cachingPolicy.bitsPerPixelOfCache);  //redraw\n                        item.cache = cache;\n                        mCacheManager.push(item, 0, forceInsert);\n                        return RESULT_SUCCESS;\n                    }\n\n                    // guess cache size\n                    int cacheSize = DanmakuUtils.getCacheSize((int) item.paintWidth, (int) item.paintHeight, mContext.cachingPolicy.bitsPerPixelOfCache / 8);\n                    if (cacheSize * 2 > mMaxCacheSize) {  // block large-size cache\n//                        Log.d(\"cache\", \"cache is too large:\"+cacheSize);\n                        return RESULT_FAILED;\n                    }\n                    if (!forceInsert && (mRealSize + cacheSize > mMaxSize)) {\n//                        Log.d(\"cache\", \"break at MaxSize:\"+mMaxSize);\n                        mCacheManager.clearTimeOutAndFilteredCaches(cacheSize, false);\n                        return RESULT_FAILED;\n                    }\n\n                    cache = mCachePool.acquire();\n                    cache = DanmakuUtils.buildDanmakuDrawingCache(item, mDisp, cache, mContext.cachingPolicy.bitsPerPixelOfCache);\n                    item.cache = cache;\n                    boolean pushed = mCacheManager.push(item, sizeOf(item), forceInsert);\n                    if (!pushed) {\n                        releaseDanmakuCache(item, cache);\n//Log.e(\"cache\", \"break at push failed:\" + mMaxSize);\n                    }\n                    return pushed ? RESULT_SUCCESS : RESULT_FAILED;\n\n                } catch (OutOfMemoryError e) {\n//Log.e(\"cache\", \"break at error: oom\");\n                    releaseDanmakuCache(item, cache);\n                    return RESULT_FAILED;\n                } catch (Exception e) {\n//Log.e(\"cache\", \"break at exception:\" + e.getMessage());\n                    releaseDanmakuCache(item, cache);\n                    return RESULT_FAILED;\n                }\n            }\n\n            private final void addDanmakuAndBuildCache(BaseDanmaku danmaku) {\n                if (danmaku.isTimeOut() || (danmaku.getActualTime() > mCacheTimer.currMillisecond + mContext.mDanmakuFactory.MAX_DANMAKU_DURATION && !danmaku.isLive)) {\n                    return;\n                }\n                if (danmaku.priority == 0 && danmaku.isFiltered()) {\n                    return;\n                }\n                IDrawingCache<?> cache = danmaku.getDrawingCache();\n                if (cache == null || cache.get() == null) {\n                    buildCache(danmaku, true);\n                }\n            }\n\n            public void begin() {\n                sendEmptyMessage(PREPARE);\n                sendEmptyMessageDelayed(CLEAR_TIMEOUT_CACHES, mContext.mDanmakuFactory.MAX_DANMAKU_DURATION);\n            }\n\n            public void pause() {\n                mPause = true;\n                sendEmptyMessage(QUIT);\n            }\n\n            public void resume() {\n                sendEmptyMessage(DISABLE_CANCEL_FLAG);\n                mPause = false;\n                removeMessages(DISPATCH_ACTIONS);\n                sendEmptyMessage(DISPATCH_ACTIONS);\n                sendEmptyMessageDelayed(CLEAR_TIMEOUT_CACHES, mContext.mDanmakuFactory.MAX_DANMAKU_DURATION);\n            }\n\n            public boolean isPause() {\n                return mPause;\n            }\n\n            public void requestBuildCacheAndDraw(long correctionTime) {\n                removeMessages(CacheHandler.BUILD_CACHES);\n                mSeekedFlag = true;\n                sendEmptyMessage(DISABLE_CANCEL_FLAG);\n                mCacheTimer.update(mTimer.currMillisecond + correctionTime);\n                sendEmptyMessage(CacheHandler.BUILD_CACHES);\n            }\n\n            public void onPlayStateChanged(boolean isPlaying) {\n                mIsPlayerPause = !isPlaying;\n            }\n        }\n\n        private void clearTimeOutAndFilteredCaches(int expectedFreeSize, final boolean forcePush) {\n            final int fexpectedFreeSize = expectedFreeSize;\n            mCaches.forEach(new IDanmakus.DefaultConsumer<BaseDanmaku>() {\n                @Override\n                public int accept(BaseDanmaku oldValue) {\n                    if (mEndFlag) {\n                        return IDanmakus.Consumer.ACTION_BREAK;\n                    }\n                    if (mRealSize + fexpectedFreeSize > mMaxSize) {\n                        if (oldValue.isTimeOut() || oldValue.isFiltered()) {\n                            entryRemoved(false, oldValue, null);\n                            return IDanmakus.Consumer.ACTION_REMOVE;\n                        } else if (forcePush) {\n                            return IDanmakus.Consumer.ACTION_BREAK;\n                        }\n                    } else {\n                        return IDanmakus.Consumer.ACTION_BREAK;\n                    }\n                    return IDanmakus.Consumer.ACTION_CONTINUE;\n                }\n            });\n        }\n\n        public long getFirstCacheTime() {\n            if (mCaches != null && mCaches.size() > 0) {\n                BaseDanmaku firstItem = mCaches.first();\n                if (firstItem == null)\n                    return 0;\n                return firstItem.getActualTime();\n            }\n            return 0;\n        }\n\n        public void requestBuild(long correctionTime) {\n            if (mHandler != null) {\n                mHandler.requestBuildCacheAndDraw(correctionTime);\n            }\n        }\n\n        public void requestClearAll() {\n            if (mHandler == null) {\n                return;\n            }\n            mHandler.removeMessages(CacheHandler.BUILD_CACHES);\n            mHandler.removeMessages(CacheHandler.DISABLE_CANCEL_FLAG);\n            mHandler.requestCancelCaching();\n            mHandler.removeMessages(CacheHandler.CLEAR_ALL_CACHES);\n            mHandler.sendEmptyMessage(CacheHandler.CLEAR_ALL_CACHES);\n        }\n\n        public void requestClearUnused() {\n            if (mHandler == null) {\n                return;\n            }\n            mHandler.removeMessages(CacheHandler.CLEAR_OUTSIDE_CACHES_AND_RESET);\n            mHandler.sendEmptyMessage(CacheHandler.CLEAR_OUTSIDE_CACHES_AND_RESET);\n        }\n\n        public void requestClearTimeout() {\n            if (mHandler == null) {\n                return;\n            }\n            mHandler.removeMessages(CacheHandler.CLEAR_TIMEOUT_CACHES);\n            mHandler.sendEmptyMessage(CacheHandler.CLEAR_TIMEOUT_CACHES);\n        }\n\n        public void post(Runnable runnable) {\n            if (mHandler == null) {\n                return;\n            }\n            mHandler.post(runnable);\n        }\n\n    }\n\n    @Override\n    public boolean onDanmakuConfigChanged(DanmakuContext config, DanmakuConfigTag tag,\n                                          Object... values) {\n        if (super.handleOnDanmakuConfigChanged(config, tag, values)) {\n            // do nothing\n        } else if (DanmakuConfigTag.SCROLL_SPEED_FACTOR.equals(tag)) {\n            mDisp.resetSlopPixel(mContext.scaleTextSize);\n            requestClear();\n        } else if (tag.isVisibilityRelatedTag()) {\n            if (values != null && values.length > 0) {\n                if (values[0] != null && ((values[0] instanceof Boolean) == false || ((Boolean) values[0]).booleanValue())) {\n                    if (mCacheManager != null) {\n                        mCacheManager.requestBuild(0l);\n                    }\n                }\n            }\n            requestClear();\n        } else if (DanmakuConfigTag.TRANSPARENCY.equals(tag) || DanmakuConfigTag.SCALE_TEXTSIZE.equals(tag) || DanmakuConfigTag.DANMAKU_STYLE.equals(tag)) {\n            if (DanmakuConfigTag.SCALE_TEXTSIZE.equals(tag)) {\n                mDisp.resetSlopPixel(mContext.scaleTextSize);\n            }\n            if (mCacheManager != null) {\n                mCacheManager.requestClearAll();\n                mCacheManager.requestBuild(-mContext.mDanmakuFactory.MAX_DANMAKU_DURATION);\n            }\n        } else {\n            if (mCacheManager != null) {\n                mCacheManager.requestClearUnused();\n                mCacheManager.requestBuild(0l);\n            }\n        }\n\n        if (mTaskListener != null && mCacheManager != null) {\n            mCacheManager.post(new Runnable() {\n\n                @Override\n                public void run() {\n                    mTaskListener.onDanmakuConfigChanged();\n                }\n            });\n        }\n        return true;\n    }\n}"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/controller/DanmakuFilters.java",
    "content": "\npackage master.flame.danmaku.controller;\n\nimport master.flame.danmaku.danmaku.model.Duration;\nimport master.flame.danmaku.danmaku.util.SystemClock;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.TreeMap;\n\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\nimport master.flame.danmaku.danmaku.model.DanmakuTimer;\nimport master.flame.danmaku.danmaku.model.IDanmakus;\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext;\nimport master.flame.danmaku.danmaku.model.android.Danmakus;\n\npublic class DanmakuFilters {\n\n    public static final int FILTER_TYPE_TYPE = 1;\n    public static final int FILYER_TYPE_QUANTITY = 2;\n    public static final int FILTER_TYPE_ELAPSED_TIME = 4;\n    public static final int FILTER_TYPE_TEXTCOLOR = 8;\n    public static final int FILTER_TYPE_USER_ID = 16;\n    public static final int FILTER_TYPE_USER_HASH = 32;\n    public static final int FILTER_TYPE_USER_GUEST = 64;\n    public static final int FILTER_TYPE_DUPLICATE_MERGE = 128;\n    public static final int FILTER_TYPE_MAXIMUM_LINES = 256;\n    public static final int FILTER_TYPE_OVERLAPPING = 512;\n\n\n    public interface IDanmakuFilter<T> {\n        /*\n         * 是否过滤\n         */\n        boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,\n                       DanmakuTimer timer, boolean fromCachingTask, DanmakuContext config);\n\n        void setData(T data);\n\n        void reset();\n\n        void clear();\n\n    }\n\n    public static abstract class BaseDanmakuFilter<T> implements IDanmakuFilter<T> {\n\n        @Override\n        public void clear() {\n\n        }\n\n    }\n\n    /**\n     * 根据弹幕类型过滤\n     * \n     * @author ch\n     */\n    public static class TypeDanmakuFilter extends BaseDanmakuFilter<List<Integer>> {\n\n        final List<Integer> mFilterTypes = Collections.synchronizedList(new ArrayList<Integer>());\n\n        public void enableType(Integer type) {\n            if (!mFilterTypes.contains(type))\n                mFilterTypes.add(type);\n        }\n\n        public void disableType(Integer type) {\n            if (mFilterTypes.contains(type))\n                mFilterTypes.remove(type);\n        }\n\n        @Override\n        public boolean filter(BaseDanmaku danmaku, int orderInScreen, int totalsizeInScreen,\n                              DanmakuTimer timer, boolean fromCachingTask, DanmakuContext config) {\n            boolean filtered = danmaku != null && mFilterTypes.contains(danmaku.getType());\n            if (filtered) {\n                danmaku.mFilterParam |= FILTER_TYPE_TYPE;\n            }\n            return filtered;\n        }\n\n        @Override\n        public void setData(List<Integer> data) {\n            reset();\n            if (data != null) {\n                for (Integer i : data) {\n                    enableType(i);\n                }\n            }\n        }\n\n        @Override\n        public void reset() {\n            mFilterTypes.clear();\n        }\n\n    }\n\n    /**\n     * 根据同屏数量过滤弹幕\n     * \n     * @author ch\n     */\n    public static class QuantityDanmakuFilter extends BaseDanmakuFilter<Integer> {\n\n        protected int mMaximumSize = -1;\n\n        protected BaseDanmaku mLastSkipped = null;\n        private float mFilterFactor = 1f;\n\n        private boolean needFilter(BaseDanmaku danmaku, int orderInScreen,\n                                 int totalSizeInScreen, DanmakuTimer timer, boolean fromCachingTask, DanmakuContext context) {\n\n            if (mMaximumSize <= 0 || danmaku.getType() != BaseDanmaku.TYPE_SCROLL_RL) {\n                return false;\n            }\n\n            if (mLastSkipped == null || mLastSkipped.isTimeOut()) {\n                mLastSkipped = danmaku;\n                return false;\n            }\n\n            long gapTime = danmaku.getActualTime() - mLastSkipped.getActualTime();\n            Duration maximumScrollDuration = context.mDanmakuFactory.MAX_Duration_Scroll_Danmaku;\n            if (gapTime >= 0 && maximumScrollDuration != null && gapTime < (maximumScrollDuration.value * mFilterFactor)) {\n                return true;\n            }\n\n            if (orderInScreen > mMaximumSize) {\n                return true;\n            }\n            mLastSkipped = danmaku;\n            return false;\n        }\n\n        @Override\n        public synchronized boolean filter(BaseDanmaku danmaku, int orderInScreen,\n                                           int totalsizeInScreen, DanmakuTimer timer, boolean fromCachingTask, DanmakuContext config) {\n            boolean filtered = needFilter(danmaku, orderInScreen, totalsizeInScreen, timer, fromCachingTask, config);\n            if (filtered) {\n                danmaku.mFilterParam |= FILYER_TYPE_QUANTITY;\n            }\n            return filtered;\n        }\n\n        @Override\n        public void setData(Integer data) {\n            reset();\n            if (data == null)\n                return;\n            if (data != mMaximumSize) {\n                mMaximumSize = data + data / 5;\n                mFilterFactor = 1f / (float) mMaximumSize;\n            }\n        }\n\n        @Override\n        public synchronized void reset() {\n            mLastSkipped = null;\n        }\n\n        @Override\n        public void clear() {\n            reset();\n        }\n    }\n\n    /**\n     * 根据绘制耗时过滤弹幕\n     * \n     * @author ch\n     */\n    public static class ElapsedTimeFilter extends BaseDanmakuFilter<Object> {\n\n        long mMaxTime = 20; // 绘制超过20ms就跳过 ，默认保持接近50fps\n\n        private synchronized boolean needFilter(BaseDanmaku danmaku, int orderInScreen,\n                                   int totalsizeInScreen, DanmakuTimer timer, boolean fromCachingTask) {\n            if (timer == null || !danmaku.isOutside()) {\n                return false;\n            }\n\n            long elapsedTime = SystemClock.uptimeMillis() - timer.currMillisecond;\n            if (elapsedTime >= mMaxTime) {\n                return true;\n            }\n            return false;\n        }\n\n        @Override\n        public boolean filter(BaseDanmaku danmaku, int orderInScreen,\n                              int totalsizeInScreen, DanmakuTimer timer, boolean fromCachingTask, DanmakuContext config) {\n            boolean filtered = needFilter(danmaku, orderInScreen, totalsizeInScreen, timer, fromCachingTask);\n            if (filtered) {\n                danmaku.mFilterParam |= FILTER_TYPE_ELAPSED_TIME;\n            }\n            return filtered;\n        }\n\n        @Override\n        public void setData(Object data) {\n            reset();\n        }\n\n        @Override\n        public synchronized void reset() {\n\n        }\n\n        @Override\n        public void clear() {\n            reset();\n        }\n\n    }\n\n    /**\n     * 根据文本颜色白名单过滤\n     * \n     * @author ch\n     */\n    public static class TextColorFilter extends BaseDanmakuFilter<List<Integer>> {\n\n        public List<Integer> mWhiteList = new ArrayList<Integer>();\n\n        private void addToWhiteList(Integer color) {\n            if (!mWhiteList.contains(color)) {\n                mWhiteList.add(color);\n            }\n        }\n\n        @Override\n        public boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,\n                              DanmakuTimer timer, boolean fromCachingTask, DanmakuContext config) {\n            boolean filtered = danmaku != null && !mWhiteList.contains(danmaku.textColor);\n            if (filtered) {\n                danmaku.mFilterParam |= FILTER_TYPE_TEXTCOLOR;\n            }\n            return filtered;\n        }\n\n        @Override\n        public void setData(List<Integer> data) {\n            reset();\n            if (data != null) {\n                for (Integer i : data) {\n                    addToWhiteList(i);\n                }\n            }\n        }\n\n        @Override\n        public void reset() {\n            mWhiteList.clear();\n        }\n\n    }\n\n    /**\n     * 根据用户标识黑名单过滤\n     * \n     * @author ch\n     */\n    public static abstract class UserFilter<T> extends BaseDanmakuFilter<List<T>> {\n\n        public List<T> mBlackList = new ArrayList<T>();\n\n        private void addToBlackList(T id) {\n            if (!mBlackList.contains(id)) {\n                mBlackList.add(id);\n            }\n        }\n\n        @Override\n        public abstract boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,\n                                       DanmakuTimer timer, boolean fromCachingTask, DanmakuContext config);\n\n        @Override\n        public void setData(List<T> data) {\n            reset();\n            if (data != null) {\n                for (T i : data) {\n                    addToBlackList(i);\n                }\n            }\n        }\n\n        @Override\n        public void reset() {\n            mBlackList.clear();\n        }\n\n    }\n\n    /**\n     * 根据用户Id黑名单过滤\n     * \n     * @author ch\n     */\n    public static class UserIdFilter extends UserFilter<Integer> {\n\n        @Override\n        public boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,\n                              DanmakuTimer timer, boolean fromCachingTask, DanmakuContext config) {\n            boolean filtered = danmaku != null && mBlackList.contains(danmaku.userId);\n            if (filtered) {\n                danmaku.mFilterParam |= FILTER_TYPE_USER_ID;\n            }\n            return filtered;\n        }\n\n    }\n\n    /**\n     * 根据用户hash黑名单过滤\n     * \n     * @author ch\n     */\n    public static class UserHashFilter extends UserFilter<String> {\n\n        @Override\n        public boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,\n                              DanmakuTimer timer, boolean fromCachingTask, DanmakuContext config) {\n            boolean filtered = danmaku != null && mBlackList.contains(danmaku.userHash);\n            if (filtered) {\n                danmaku.mFilterParam |= FILTER_TYPE_USER_HASH;\n            }\n            return filtered;\n        }\n\n    }\n\n    /**\n     * 屏蔽游客弹幕\n     * \n     * @author ch\n     */\n    public static class GuestFilter extends BaseDanmakuFilter<Boolean> {\n\n        private Boolean mBlock = false;\n\n        @Override\n        public boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,\n                              DanmakuTimer timer, boolean fromCachingTask, DanmakuContext config) {\n            boolean filtered = mBlock && danmaku.isGuest;\n            if (filtered) {\n                danmaku.mFilterParam |= FILTER_TYPE_USER_GUEST;\n            }\n            return filtered;\n        }\n\n        @Override\n        public void setData(Boolean data) {\n            mBlock = data;\n        }\n\n        @Override\n        public void reset() {\n            mBlock = false;\n        }\n\n    }\n\n    public static class DuplicateMergingFilter extends BaseDanmakuFilter<Void> {\n\n        protected final IDanmakus blockedDanmakus = new Danmakus(Danmakus.ST_BY_LIST);\n        protected final LinkedHashMap<String, BaseDanmaku> currentDanmakus = new LinkedHashMap<String, BaseDanmaku>();\n        private final IDanmakus passedDanmakus = new Danmakus(Danmakus.ST_BY_LIST);\n\n        private final void removeTimeoutDanmakus(final IDanmakus danmakus, final long limitTime) {\n            danmakus.forEachSync(new IDanmakus.DefaultConsumer<BaseDanmaku>() {\n                long startTime = SystemClock.uptimeMillis();\n                @Override\n                public int accept(BaseDanmaku item) {\n                    try {\n                        if (SystemClock.uptimeMillis() - startTime > limitTime) {\n                            return ACTION_BREAK;\n                        }\n                        if (item.isTimeOut()) {\n                            return ACTION_REMOVE;\n                        } else {\n                            return ACTION_BREAK;\n                        }\n                    } catch (Exception e) {\n                        return ACTION_BREAK;\n                    }\n                }\n            });\n        }\n\n        private void removeTimeoutDanmakus(LinkedHashMap<String, BaseDanmaku> danmakus,\n                int limitTime) {\n            Iterator<Entry<String, BaseDanmaku>> it = danmakus.entrySet().iterator();\n            long startTime = SystemClock.uptimeMillis();\n            while (it.hasNext()) {\n                try {\n                    Entry<String, BaseDanmaku> entry = it.next();\n                    BaseDanmaku item = entry.getValue();\n                    if (item.isTimeOut()) {\n                        it.remove();\n                    } else {\n                        break;\n                    }\n                } catch (Exception e) {\n                    break;\n                }\n                if (SystemClock.uptimeMillis() - startTime > limitTime) {\n                    break;\n                }\n            }\n        }\n\n        public synchronized boolean needFilter(BaseDanmaku danmaku, int index, int totalsizeInScreen,\n                DanmakuTimer timer, boolean fromCachingTask) {\n            removeTimeoutDanmakus(blockedDanmakus, 2);\n            removeTimeoutDanmakus(passedDanmakus, 2);\n            removeTimeoutDanmakus(currentDanmakus, 3);\n            if (blockedDanmakus.contains(danmaku) && !danmaku.isOutside()) {\n                return true;\n            }\n            if (passedDanmakus.contains(danmaku)) {\n                return false;\n            }\n            if (currentDanmakus.containsKey(danmaku.text)) {\n                currentDanmakus.put(String.valueOf(danmaku.text), danmaku);\n                blockedDanmakus.removeItem(danmaku);\n                blockedDanmakus.addItem(danmaku);\n                return true;\n            } else {\n                currentDanmakus.put(String.valueOf(danmaku.text), danmaku);\n                passedDanmakus.addItem(danmaku);\n                return false;\n            }\n\n        }\n\n        @Override\n        public boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,\n                              DanmakuTimer timer, boolean fromCachingTask, DanmakuContext config) {\n            boolean filtered = needFilter(danmaku, index, totalsizeInScreen, timer, fromCachingTask);\n            if (filtered) {\n                danmaku.mFilterParam |= FILTER_TYPE_DUPLICATE_MERGE;\n            }\n            return filtered;\n        }\n\n        @Override\n        public void setData(Void data) {\n\n        }\n\n        @Override\n        public synchronized void reset() {\n            passedDanmakus.clear();\n            blockedDanmakus.clear();\n            currentDanmakus.clear();\n        }\n\n        @Override\n        public void clear() {\n            reset();\n        }\n\n    }\n\n    public static class MaximumLinesFilter extends BaseDanmakuFilter<Map<Integer, Integer>> {\n\n        private Map<Integer, Integer> mMaximumLinesPairs;\n\n        @Override\n        public boolean filter(BaseDanmaku danmaku, int lines, int totalsizeInScreen, DanmakuTimer timer, boolean willHit, DanmakuContext config) {\n            boolean filtered = false;\n            if (mMaximumLinesPairs != null) {\n                Integer maxLines = mMaximumLinesPairs.get(danmaku.getType());\n                filtered = (maxLines != null && lines >= maxLines);\n                if (filtered) {\n                    danmaku.mFilterParam |= FILTER_TYPE_MAXIMUM_LINES;\n                }\n            }\n            return filtered;\n        }\n\n        @Override\n        public void setData(Map<Integer, Integer> data) {\n            mMaximumLinesPairs = data;\n        }\n\n        @Override\n        public void reset() {\n            mMaximumLinesPairs = null;\n        }\n    }\n\n    public static class OverlappingFilter extends BaseDanmakuFilter<Map<Integer, Boolean>> {\n\n        private Map<Integer, Boolean> mEnabledPairs;\n\n        @Override\n        public boolean filter(BaseDanmaku danmaku, int index, int totalsizeInScreen, DanmakuTimer timer, boolean willHit, DanmakuContext config) {\n            boolean filtered = false;\n            if (mEnabledPairs != null) {\n                Boolean enabledValue = mEnabledPairs.get(danmaku.getType());\n                filtered = enabledValue != null && enabledValue && willHit;\n                if (filtered) {\n                    danmaku.mFilterParam |= FILTER_TYPE_OVERLAPPING;\n                }\n            }\n            return filtered;\n        }\n\n        @Override\n        public void setData(Map<Integer, Boolean> data) {\n            mEnabledPairs = data;\n        }\n\n        @Override\n        public void reset() {\n            mEnabledPairs = null;\n        }\n    }\n\n    public final static String TAG_TYPE_DANMAKU_FILTER = \"1010_Filter\";\n\n    public final static String TAG_QUANTITY_DANMAKU_FILTER = \"1011_Filter\";\n\n    public final static String TAG_ELAPSED_TIME_FILTER = \"1012_Filter\";\n\n    public final static String TAG_TEXT_COLOR_DANMAKU_FILTER = \"1013_Filter\";\n\n    public final static String TAG_USER_ID_FILTER = \"1014_Filter\";\n\n    public final static String TAG_USER_HASH_FILTER = \"1015_Filter\";\n\n    public final static String TAG_GUEST_FILTER = \"1016_Filter\";\n\n    public final static String TAG_DUPLICATE_FILTER = \"1017_Filter\";\n\n    public final static String TAG_MAXIMUN_LINES_FILTER = \"1018_Filter\";\n\n    public final static String TAG_OVERLAPPING_FILTER = \"1019_Filter\";\n\n    public final static String TAG_PRIMARY_CUSTOM_FILTER = \"2000_Primary_Custom_Filter\";\n\n    public final Exception filterException = new Exception(\"not suuport this filter tag\");\n\n    public void filter(BaseDanmaku danmaku, int index, int totalsizeInScreen,\n                       DanmakuTimer timer, boolean fromCachingTask, DanmakuContext context) {\n        for (IDanmakuFilter<?> f : mFilterArray) {\n            if (f != null) {\n                boolean filtered = f.filter(danmaku, index, totalsizeInScreen, timer, fromCachingTask, context);\n                danmaku.filterResetFlag = context.mGlobalFlagValues.FILTER_RESET_FLAG;\n                if (filtered) {\n                    break;\n                }\n            }\n        }\n    }\n\n    public boolean filterSecondary(BaseDanmaku danmaku, int lines, int totalsizeInScreen,\n                                   DanmakuTimer timer, boolean willHit, DanmakuContext context) {\n        for (IDanmakuFilter<?> f : mFilterArraySecondary) {\n            if (f != null) {\n                boolean filtered = f.filter(danmaku, lines, totalsizeInScreen, timer, willHit, context);\n                danmaku.filterResetFlag = context.mGlobalFlagValues.FILTER_RESET_FLAG;\n                if (filtered) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    private final Map<String, IDanmakuFilter<?>> filters = Collections\n            .synchronizedSortedMap(new TreeMap<String, IDanmakuFilter<?>>());\n    private final Map<String, IDanmakuFilter<?>> filtersSecondary = Collections\n            .synchronizedSortedMap(new TreeMap<String, IDanmakuFilter<?>>());\n    IDanmakuFilter<?>[] mFilterArray = new IDanmakuFilter[0];\n    IDanmakuFilter<?>[] mFilterArraySecondary = new IDanmakuFilter[0];\n\n    public IDanmakuFilter<?> get(String tag) {\n        return get(tag, true);\n    }\n\n    public IDanmakuFilter<?> get(String tag, boolean primary) {\n        IDanmakuFilter<?> f = primary ? filters.get(tag) : filtersSecondary.get(tag);\n        if (f == null) {\n            f = registerFilter(tag, primary);\n        }\n        return f;\n    }\n\n    public IDanmakuFilter<?> registerFilter(String tag) {\n        return registerFilter(tag, true);\n    }\n\n    public IDanmakuFilter<?> registerFilter(String tag, boolean primary) {\n        if (tag == null) {\n            throwFilterException();\n            return null;\n        }\n        IDanmakuFilter<?> filter = filters.get(tag);\n        if (filter == null) {\n            if (TAG_TYPE_DANMAKU_FILTER.equals(tag)) {\n                filter = new TypeDanmakuFilter();\n            } else if (TAG_QUANTITY_DANMAKU_FILTER.equals(tag)) {\n                filter = new QuantityDanmakuFilter();\n            } else if (TAG_ELAPSED_TIME_FILTER.equals(tag)) {\n                filter = new ElapsedTimeFilter();\n            } else if (TAG_TEXT_COLOR_DANMAKU_FILTER.equals(tag)) {\n                filter = new TextColorFilter();\n            } else if (TAG_USER_ID_FILTER.equals(tag)) {\n                filter = new UserIdFilter();\n            } else if (TAG_USER_HASH_FILTER.equals(tag)) {\n                filter = new UserHashFilter();\n            } else if (TAG_GUEST_FILTER.equals(tag)) {\n                filter = new GuestFilter();\n            } else if (TAG_DUPLICATE_FILTER.equals(tag)) {\n                filter = new DuplicateMergingFilter();\n            } else if (TAG_MAXIMUN_LINES_FILTER.equals(tag)) {\n                filter = new MaximumLinesFilter();\n            } else if (TAG_OVERLAPPING_FILTER.equals(tag)) {\n                filter = new OverlappingFilter();\n            }\n        }\n        if (filter == null) {\n            throwFilterException();\n            return null;\n        }\n        filter.setData(null);\n        if (primary) {\n            filters.put(tag, filter);\n            mFilterArray = filters.values().toArray(mFilterArray);\n        } else {\n            filtersSecondary.put(tag, filter);\n            mFilterArraySecondary = filtersSecondary.values().toArray(mFilterArraySecondary);\n        }\n        return filter;\n    }\n\n    public void registerFilter(BaseDanmakuFilter filter) {\n        filters.put(TAG_PRIMARY_CUSTOM_FILTER + \"_\" + filter.hashCode(), filter);\n        mFilterArray = filters.values().toArray(mFilterArray);\n    }\n\n    public void unregisterFilter(String tag) {\n        unregisterFilter(tag, true);\n    }\n\n    public void unregisterFilter(String tag, boolean primary) {\n        IDanmakuFilter<?> f = primary ? filters.remove(tag) : filtersSecondary.remove(tag);\n        if (f != null) {\n            f.clear();\n            if (primary) {\n                mFilterArray = filters.values().toArray(mFilterArray);\n            } else {\n                mFilterArraySecondary = filtersSecondary.values().toArray(mFilterArraySecondary);\n            }\n        }\n    }\n\n    public void unregisterFilter(BaseDanmakuFilter filter) {\n        filters.remove(TAG_PRIMARY_CUSTOM_FILTER + \"_\" + filter.hashCode());\n        mFilterArray = filters.values().toArray(mFilterArray);\n    }\n\n    public void clear() {\n        for (IDanmakuFilter<?> f : mFilterArray) {\n            if (f != null)\n                f.clear();\n        }\n        for (IDanmakuFilter<?> f : mFilterArraySecondary) {\n            if (f != null)\n                f.clear();\n        }\n    }\n\n    public void reset() {\n        for (IDanmakuFilter<?> f : mFilterArray) {\n            if (f != null)\n                f.reset();\n        }\n        for (IDanmakuFilter<?> f : mFilterArraySecondary) {\n            if (f != null)\n                f.reset();\n        }\n    }\n\n    public void release() {\n        clear();\n        filters.clear();\n        mFilterArray = new IDanmakuFilter[0];\n        filtersSecondary.clear();\n        mFilterArraySecondary = new IDanmakuFilter[0];\n    }\n\n    private void throwFilterException() {\n        try {\n            throw filterException;\n        } catch (Exception e) {\n        }\n    }\n\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/controller/DrawHandler.java",
    "content": "/*\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage master.flame.danmaku.controller;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.os.Build;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Message;\nimport android.util.DisplayMetrics;\nimport android.view.Choreographer;\n\nimport java.util.LinkedList;\n\nimport master.flame.danmaku.danmaku.model.AbsDanmakuSync;\nimport master.flame.danmaku.danmaku.model.AbsDisplayer;\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\nimport master.flame.danmaku.danmaku.model.DanmakuTimer;\nimport master.flame.danmaku.danmaku.model.IDanmakus;\nimport master.flame.danmaku.danmaku.model.IDisplayer;\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext;\nimport master.flame.danmaku.danmaku.parser.BaseDanmakuParser;\nimport master.flame.danmaku.danmaku.renderer.IRenderer.RenderingState;\nimport master.flame.danmaku.danmaku.util.SystemClock;\nimport tv.cjump.jni.DeviceUtils;\n\npublic class DrawHandler extends Handler {\n\n    private DanmakuContext mContext;\n    private FrameCallback mFrameCallback;\n\n    public interface Callback {\n        public void prepared();\n\n        public void updateTimer(DanmakuTimer timer);\n\n        public void danmakuShown(BaseDanmaku danmaku);\n\n        public void drawingFinished();\n\n    }\n\n    public static final int START = 1;\n\n    public static final int UPDATE = 2;\n\n    public static final int RESUME = 3;\n\n    public static final int SEEK_POS = 4;\n\n    public static final int PREPARE = 5;\n\n    private static final int QUIT = 6;\n\n    private static final int PAUSE = 7;\n\n    private static final int SHOW_DANMAKUS = 8;\n\n    private static final int HIDE_DANMAKUS = 9;\n\n    private static final int NOTIFY_DISP_SIZE_CHANGED = 10;\n\n    private static final int NOTIFY_RENDERING = 11;\n\n    private static final int UPDATE_WHEN_PAUSED = 12;\n\n    private static final int CLEAR_DANMAKUS_ON_SCREEN = 13;\n\n    private static final int FORCE_RENDER = 14;\n\n    private static final long INDEFINITE_TIME = 10000000;\n\n    private long pausedPosition = 0;\n\n    private boolean quitFlag = true;\n\n    private long mTimeBase;\n\n    private boolean mReady;\n\n    private Callback mCallback;\n\n    private DanmakuTimer timer = new DanmakuTimer();\n\n    private BaseDanmakuParser mParser;\n\n    public IDrawTask drawTask;\n\n    private IDanmakuViewController mDanmakuView;\n\n    private boolean mDanmakusVisible = true;\n\n    private AbsDisplayer mDisp;\n\n    private final RenderingState mRenderingState = new RenderingState();\n\n    private static final int MAX_RECORD_SIZE = 500;\n\n    private LinkedList<Long> mDrawTimes = new LinkedList<>();\n\n    private UpdateThread mThread;\n\n    private boolean mUpdateInSeparateThread;\n\n    private long mCordonTime = 30;\n\n    private long mCordonTime2 = 60;\n\n    private long mFrameUpdateRate = 16;\n\n    @SuppressWarnings(\"unused\")\n    private long mThresholdTime;\n\n    private long mLastDeltaTime;\n\n    private boolean mInSeekingAction;\n\n    private long mDesireSeekingTime;\n\n    private long mRemainingTime;\n\n    private boolean mInSyncAction;\n\n    private boolean mInWaitingState;\n\n    private boolean mIdleSleep;\n\n    private boolean mNonBlockModeEnable;\n\n    public DrawHandler(Looper looper, IDanmakuViewController view, boolean danmakuVisibile) {\n        super(looper);\n        mIdleSleep = !DeviceUtils.isProblemBoxDevice();\n        bindView(view);\n        if (danmakuVisibile) {\n            showDanmakus(null);\n        } else {\n            hideDanmakus(false);\n        }\n        mDanmakusVisible = danmakuVisibile;\n    }\n\n    private void bindView(IDanmakuViewController view) {\n        this.mDanmakuView = view;\n    }\n\n    public void setIdleSleep(boolean enable) {\n        mIdleSleep = enable;\n    }\n\n    public void enableNonBlockMode(boolean enable) {\n        mNonBlockModeEnable = enable;\n    }\n\n    public void setConfig(DanmakuContext config) {\n        mContext = config;\n    }\n\n    public void setParser(BaseDanmakuParser parser) {\n        mParser = parser;\n        DanmakuTimer timer = parser.getTimer();\n        if (timer != null) {\n            this.timer = timer;\n        }\n    }\n\n    public void setCallback(Callback cb) {\n        mCallback = cb;\n    }\n\n    public void quit() {\n        quitFlag = true;\n        sendEmptyMessage(QUIT);\n    }\n\n    public boolean isStop() {\n        return quitFlag;\n    }\n\n    @Override\n    public void handleMessage(Message msg) {\n        int what = msg.what;\n        switch (what) {\n            case PREPARE:\n                mTimeBase = SystemClock.uptimeMillis();\n                if (mParser == null || !mDanmakuView.isViewReady()) {\n                    sendEmptyMessageDelayed(PREPARE, 100);\n                } else {\n                    prepare(new Runnable() {\n                        @Override\n                        public void run() {\n                            pausedPosition = 0;\n                            mReady = true;\n                            if (mCallback != null) {\n                                mCallback.prepared();\n                            }\n                        }\n                    });\n                }\n                break;\n            case SHOW_DANMAKUS:\n                mDanmakusVisible = true;\n                Long start = (Long) msg.obj;\n                boolean resume = false;\n                if (drawTask != null) {\n                    if (start == null) {\n                        timer.update(getCurrentTime());\n                        drawTask.requestClear();\n                    } else {\n                        drawTask.start();\n                        drawTask.seek(start);\n                        drawTask.requestClear();\n                        resume = true;\n                    }\n                }\n                if (quitFlag && mDanmakuView != null) {\n                    mDanmakuView.drawDanmakus();\n                }\n                notifyRendering();\n                if (!resume) {\n                    break;\n                }\n            case START:\n                Long startTime = (Long) msg.obj;\n                if (startTime != null) {\n                    pausedPosition = startTime;\n                } else {\n                    pausedPosition = 0;\n                }\n            case SEEK_POS:\n                if (what == SEEK_POS) {\n                    quitFlag = true;\n                    quitUpdateThread();\n                    Long position = (Long) msg.obj;\n                    long deltaMs = position - timer.currMillisecond;\n                    mTimeBase -= deltaMs;\n                    timer.update(position);\n                    mContext.mGlobalFlagValues.updateMeasureFlag();\n                    if (drawTask != null)\n                        drawTask.seek(position);\n                    pausedPosition = position;\n                }\n            case RESUME:\n                removeMessages(DrawHandler.PAUSE);\n                quitFlag = false;\n                if (mReady) {\n                    mRenderingState.reset();\n                    mDrawTimes.clear();\n                    mTimeBase = SystemClock.uptimeMillis() - pausedPosition;\n                    timer.update(pausedPosition);\n                    removeMessages(RESUME);\n                    sendEmptyMessage(UPDATE);\n                    drawTask.start();\n                    notifyRendering();\n                    mInSeekingAction = false;\n                    if (drawTask != null) {\n                        drawTask.onPlayStateChanged(IDrawTask.PLAY_STATE_PLAYING);\n                    }\n                } else {\n                    sendEmptyMessageDelayed(RESUME, 100);\n                }\n                break;\n            case UPDATE:\n                if (mContext.updateMethod == 0) {\n                    updateInChoreographer();\n                } else if (mContext.updateMethod == 1) {\n                    updateInNewThread();\n                } else if (mContext.updateMethod == 2) {\n                    updateInCurrentThread();\n                }\n                break;\n            case NOTIFY_DISP_SIZE_CHANGED:\n                mContext.mDanmakuFactory.notifyDispSizeChanged(mContext);\n                Boolean updateFlag = (Boolean) msg.obj;\n                if (updateFlag != null && updateFlag) {\n                    mContext.mGlobalFlagValues.updateMeasureFlag();\n                    mContext.mGlobalFlagValues.updateVisibleFlag();\n                    drawTask.requestClearRetainer();\n                }\n                break;\n            case HIDE_DANMAKUS:\n                mDanmakusVisible = false;\n                if (mDanmakuView != null) {\n                    mDanmakuView.clear();\n                }\n                if(this.drawTask != null) {\n                    this.drawTask.requestClear();\n                    this.drawTask.requestHide();\n                }\n                Boolean quitDrawTask = (Boolean) msg.obj;\n                if (quitDrawTask && this.drawTask != null) {\n                    this.drawTask.quit();\n                }\n                if (!quitDrawTask) {\n                    break;\n                }\n            case PAUSE:\n                removeMessages(DrawHandler.RESUME);\n                removeMessages(UPDATE);\n                if (drawTask != null) {\n                    drawTask.onPlayStateChanged(IDrawTask.PLAY_STATE_PAUSE);\n                }\n            case QUIT:\n                if (what == QUIT) {\n                    removeCallbacksAndMessages(null);\n                }\n                quitFlag = true;\n                syncTimerIfNeeded();\n                pausedPosition = timer.currMillisecond;\n                if (mUpdateInSeparateThread) {\n                    notifyRendering();\n                    quitUpdateThread();\n                }\n                if (mFrameCallback != null) {\n                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n                        Choreographer.getInstance().removeFrameCallback(mFrameCallback);\n                    }\n                }\n                if (what == QUIT){\n                    if (this.drawTask != null){\n                        this.drawTask.quit();\n                    }\n                    if (mParser != null) {\n                        mParser.release();\n                    }\n                    if (this.getLooper() != Looper.getMainLooper())\n                        this.getLooper().quit();\n                }\n                break;\n            case NOTIFY_RENDERING:\n                notifyRendering();\n                break;\n            case UPDATE_WHEN_PAUSED:\n                if (quitFlag && mDanmakuView != null) {\n                    drawTask.requestClear();\n                    mDanmakuView.drawDanmakus();\n                    notifyRendering();\n                }\n                break;\n            case CLEAR_DANMAKUS_ON_SCREEN:\n                if (drawTask != null) {\n                    drawTask.clearDanmakusOnScreen(getCurrentTime());\n                }\n                break;\n            case FORCE_RENDER:\n                if (drawTask != null) {\n                    drawTask.requestRender();\n                }\n                break;\n        }\n    }\n\n    private synchronized void quitUpdateThread() {\n        UpdateThread thread = mThread;\n        mThread = null;\n        if (thread != null) {\n            synchronized (drawTask) {\n                drawTask.notifyAll();\n            }\n            thread.quit();\n            try {\n                thread.join(2000);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    private void updateInCurrentThread() {\n        if (quitFlag) {\n            return;\n        }\n        long startMS = SystemClock.uptimeMillis();\n        long d = syncTimer(startMS);\n        if (d < 0 && !mNonBlockModeEnable) {\n            removeMessages(UPDATE);\n            sendEmptyMessageDelayed(UPDATE, 60 - d);\n            return;\n        }\n        d = mDanmakuView.drawDanmakus();\n        removeMessages(UPDATE);\n        if (d > mCordonTime2) {  // this situation may be cuased by ui-thread waiting of DanmakuView, so we sync-timer at once\n            timer.add(d);\n            mDrawTimes.clear();\n        }\n        if (!mDanmakusVisible) {\n            waitRendering(INDEFINITE_TIME);\n            return;\n        } else if (mRenderingState.nothingRendered && mIdleSleep) {\n            long dTime = mRenderingState.endTime - timer.currMillisecond;\n            if (dTime > 500) {\n                waitRendering(dTime - 10);\n                return;\n            }\n        }\n\n        if (d < mFrameUpdateRate) {\n            sendEmptyMessageDelayed(UPDATE, mFrameUpdateRate - d);\n            return;\n        }\n        sendEmptyMessage(UPDATE);\n    }\n\n    private void updateInNewThread() {\n        if (mThread != null) {\n            return;\n        }\n        mThread = new UpdateThread(\"DFM Update\") {\n            @Override\n            public void run() {\n                long lastTime = SystemClock.uptimeMillis();\n                long dTime = 0;\n                while (!isQuited() && !quitFlag) {\n                    long startMS = SystemClock.uptimeMillis();\n                    dTime = SystemClock.uptimeMillis() - lastTime;\n                    long diffTime = mFrameUpdateRate - dTime;\n                    if (diffTime > 1 && !mNonBlockModeEnable) {\n                        SystemClock.sleep(1);\n                        continue;\n                    }\n                    lastTime = startMS;\n                    long d = syncTimer(startMS);\n                    if (d < 0 && !mNonBlockModeEnable) {\n                        SystemClock.sleep(60 - d);\n                        continue;\n                    }\n                    d = mDanmakuView.drawDanmakus();\n                    if (d > mCordonTime2) {  // this situation may be cuased by ui-thread waiting of DanmakuView, so we sync-timer at once\n                        timer.add(d);\n                        mDrawTimes.clear();\n                    }\n                    if (!mDanmakusVisible) {\n                        waitRendering(INDEFINITE_TIME);\n                    } else if (mRenderingState.nothingRendered && mIdleSleep) {\n                        dTime = mRenderingState.endTime - timer.currMillisecond;\n                        if (dTime > 500) {\n                            notifyRendering();\n                            waitRendering(dTime - 10);\n                        }\n                    }\n                }\n            }\n        };\n        mThread.start();\n    }\n\n    @TargetApi(16)\n    private class FrameCallback implements Choreographer.FrameCallback {\n        @Override\n        public void doFrame(long frameTimeNanos) {\n            sendEmptyMessage(UPDATE);\n        }\n    };\n\n    @TargetApi(16)\n    private void updateInChoreographer() {\n        if (quitFlag) {\n            return;\n        }\n        Choreographer.getInstance().postFrameCallback(mFrameCallback);\n        long startMS = SystemClock.uptimeMillis();\n        long d = syncTimer(startMS);\n        if (d < 0) {\n            removeMessages(UPDATE);\n            return;\n        }\n        d = mDanmakuView.drawDanmakus();\n        removeMessages(UPDATE);\n        if (d > mCordonTime2) {  // this situation may be cuased by ui-thread waiting of DanmakuView, so we sync-timer at once\n            timer.add(d);\n            mDrawTimes.clear();\n        }\n        if (!mDanmakusVisible) {\n            waitRendering(INDEFINITE_TIME);\n            return;\n        } else if (mRenderingState.nothingRendered && mIdleSleep) {\n            long dTime = mRenderingState.endTime - timer.currMillisecond;\n            if (dTime > 500) {\n                waitRendering(dTime - 10);\n                return;\n            }\n        }\n\n    }\n\n    private final long syncTimer(long startMS) {\n        if (mInSeekingAction || mInSyncAction) {\n            return 0;\n        }\n        mInSyncAction = true;\n        long d = 0;\n        long time = startMS - mTimeBase;\n        if (mNonBlockModeEnable) {\n            if (mCallback != null) {\n                mCallback.updateTimer(timer);\n                d = timer.lastInterval();\n            }\n        } else if (!mDanmakusVisible || mRenderingState.nothingRendered || mInWaitingState) {\n            timer.update(time);\n            mRemainingTime = 0;\n            if (mCallback != null) {\n                mCallback.updateTimer(timer);\n            }\n        } else {\n            long gapTime = time - timer.currMillisecond;\n            long averageTime = Math.max(mFrameUpdateRate, getAverageRenderingTime());\n            if (gapTime > 2000 || mRenderingState.consumingTime > mCordonTime || averageTime > mCordonTime) {\n                d = gapTime;\n                gapTime = 0;\n            } else {\n                d = averageTime + gapTime / mFrameUpdateRate;\n                d = Math.max(mFrameUpdateRate, d);\n                d = Math.min(mCordonTime, d);\n                long a = d - mLastDeltaTime;\n                if (a > 3 && a < 8 && mLastDeltaTime >= mFrameUpdateRate && mLastDeltaTime <= mCordonTime) {\n                    d = mLastDeltaTime;\n                }\n                gapTime -= d;\n                mLastDeltaTime = d;\n            }\n            mRemainingTime = gapTime;\n            timer.add(d);\n            if (mCallback != null) {\n                mCallback.updateTimer(timer);\n            }\n//            Log.e(\"DrawHandler\", time+\"|d:\" + d  + \"RemaingTime:\" + mRemainingTime + \",gapTime:\" + gapTime + \",rtim:\" + mRenderingState.consumingTime + \",average:\" + averageTime);\n        }\n\n        mInSyncAction = false;\n        return d;\n    }\n\n    private void syncTimerIfNeeded() {\n        if (mInWaitingState) {\n            syncTimer(SystemClock.uptimeMillis());\n        }\n    }\n\n    private void initRenderingConfigs() {\n        long averageFrameConsumingTime = 16;\n        mCordonTime = Math.max(33, (long) (averageFrameConsumingTime * 2.5f));\n        mCordonTime2 = (long) (mCordonTime * 2.5f);\n        mFrameUpdateRate = Math.max(16, averageFrameConsumingTime / 15 * 15);\n        mThresholdTime = mFrameUpdateRate + 3;\n//        Log.i(\"DrawHandler\", \"initRenderingConfigs test-fps:\" + averageFrameConsumingTime + \"ms,mCordonTime:\"\n//                + mCordonTime + \",mFrameRefreshingRate:\" + mFrameUpdateRate);\n    }\n\n    private void prepare(final Runnable runnable) {\n        if (drawTask == null) {\n            drawTask = createDrawTask(mDanmakuView.isDanmakuDrawingCacheEnabled(), timer,\n                    mDanmakuView.getContext(), mDanmakuView.getViewWidth(), mDanmakuView.getViewHeight(),\n                    mDanmakuView.isHardwareAccelerated(), new IDrawTask.TaskListener() {\n                        @Override\n                        public void ready() {\n                            initRenderingConfigs();\n                            runnable.run();\n                        }\n\n                        @Override\n                        public void onDanmakuAdd(BaseDanmaku danmaku) {\n                            if (danmaku.isTimeOut()) {\n                                return;\n                            }\n                            long delay = danmaku.getActualTime() - getCurrentTime();\n                            if (delay < mContext.mDanmakuFactory.MAX_DANMAKU_DURATION && (mInWaitingState || mRenderingState.nothingRendered)) {\n                                notifyRendering();\n                            } else if (delay > 0 && delay <= mContext.mDanmakuFactory.MAX_DANMAKU_DURATION) {\n                                sendEmptyMessageDelayed(NOTIFY_RENDERING, delay);\n                            }\n                        }\n\n                        @Override\n                        public void onDanmakuShown(BaseDanmaku danmaku) {\n                            if (mCallback != null) {\n                                mCallback.danmakuShown(danmaku);\n                            }\n                        }\n\n                        @Override\n                        public void onDanmakusDrawingFinished() {\n                            if (mCallback != null) {\n                                mCallback.drawingFinished();\n                            }\n                        }\n\n                        @Override\n                        public void onDanmakuConfigChanged() {\n                            redrawIfNeeded();\n                        }\n                    });\n        } else {\n            runnable.run();\n        }\n    }\n\n    public boolean isPrepared() {\n        return mReady;\n    }\n\n    private IDrawTask createDrawTask(boolean useDrwaingCache, DanmakuTimer timer,\n                                     Context context,\n                                     int width, int height,\n                                     boolean isHardwareAccelerated,\n                                     IDrawTask.TaskListener taskListener) {\n        mDisp = mContext.getDisplayer();\n        mDisp.setSize(width, height);\n        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();\n        mDisp.setDensities(displayMetrics.density, displayMetrics.densityDpi,\n                displayMetrics.scaledDensity);\n        mDisp.resetSlopPixel(mContext.scaleTextSize);\n        mDisp.setHardwareAccelerated(isHardwareAccelerated);\n        IDrawTask task = useDrwaingCache ?\n                new CacheManagingDrawTask(timer, mContext, taskListener)\n                : new DrawTask(timer, mContext, taskListener);\n        task.setParser(mParser);\n        task.prepare();\n        obtainMessage(NOTIFY_DISP_SIZE_CHANGED, false).sendToTarget();\n        return task;\n    }\n\n    public void seekTo(Long ms) {\n        mInSeekingAction = true;\n        mDesireSeekingTime = ms;\n        removeMessages(DrawHandler.UPDATE);\n        removeMessages(DrawHandler.RESUME);\n        removeMessages(DrawHandler.SEEK_POS);\n        obtainMessage(DrawHandler.SEEK_POS, ms).sendToTarget();\n    }\n\n    public void addDanmaku(BaseDanmaku item) {\n        if (drawTask != null) {\n            item.flags = mContext.mGlobalFlagValues;\n            item.setTimer(timer);\n            drawTask.addDanmaku(item);\n            obtainMessage(NOTIFY_RENDERING).sendToTarget();\n        }\n    }\n\n    public void invalidateDanmaku(BaseDanmaku item, boolean remeasure) {\n        if (drawTask != null && item != null) {\n            drawTask.invalidateDanmaku(item, remeasure);\n        }\n        redrawIfNeeded();\n    }\n\n    public void resume() {\n        removeMessages(DrawHandler.PAUSE);\n        sendEmptyMessage(DrawHandler.RESUME);\n    }\n\n    public void prepare() {\n        mReady = false;\n        if (Build.VERSION.SDK_INT < 16 && mContext.updateMethod == 0) {\n            mContext.updateMethod = 2;\n        }\n        if (mContext.updateMethod == 0) {\n            mFrameCallback = new FrameCallback();\n        }\n        mUpdateInSeparateThread = (mContext.updateMethod == 1);\n        sendEmptyMessage(DrawHandler.PREPARE);\n    }\n\n    public void pause() {\n        removeMessages(DrawHandler.RESUME);\n        syncTimerIfNeeded();\n        sendEmptyMessage(DrawHandler.PAUSE);\n    }\n\n    public void showDanmakus(Long position) {\n        if (mDanmakusVisible)\n            return;\n        mDanmakusVisible = true;\n        removeMessages(SHOW_DANMAKUS);\n        removeMessages(HIDE_DANMAKUS);\n        obtainMessage(SHOW_DANMAKUS, position).sendToTarget();\n    }\n\n    public long hideDanmakus(boolean quitDrawTask) {\n        if (!mDanmakusVisible)\n            return timer.currMillisecond;\n        mDanmakusVisible = false;\n        removeMessages(SHOW_DANMAKUS);\n        removeMessages(HIDE_DANMAKUS);\n        obtainMessage(HIDE_DANMAKUS, quitDrawTask).sendToTarget();\n        return timer.currMillisecond;\n    }\n\n    public void forceRender() {\n        removeMessages(FORCE_RENDER);\n        obtainMessage(FORCE_RENDER).sendToTarget();\n    }\n\n    public boolean getVisibility() {\n        return mDanmakusVisible;\n    }\n\n    public RenderingState draw(Canvas canvas) {\n        if (drawTask == null)\n            return mRenderingState;\n\n        if (!mInWaitingState) {\n            AbsDanmakuSync danmakuSync = mContext.danmakuSync;\n            if (danmakuSync != null) {\n                do {\n                    boolean isSyncPlayingState = danmakuSync.isSyncPlayingState();\n                    if (!isSyncPlayingState && quitFlag) {\n                        break;\n                    }\n                    int syncState = danmakuSync.getSyncState();\n                    if (syncState == AbsDanmakuSync.SYNC_STATE_PLAYING) {\n                        long fromTime = timer.currMillisecond;\n                        long toTime = danmakuSync.getUptimeMillis();\n                        long offset = toTime - fromTime;\n                        if (Math.abs(offset) > danmakuSync.getThresholdTimeMills()) {\n                            if (isSyncPlayingState && quitFlag) {\n                                resume();\n                            }\n                            drawTask.requestSync(fromTime, toTime, offset);\n                            timer.update(toTime);\n                            mTimeBase -= offset;\n                            mRemainingTime = 0;\n                        }\n                    } else if (syncState == AbsDanmakuSync.SYNC_STATE_HALT) {\n                        if (isSyncPlayingState && !quitFlag) {\n                            pause();\n                        }\n                    }\n                } while (false);\n            }\n        }\n        mDisp.setExtraData(canvas);\n        mRenderingState.set(drawTask.draw(mDisp));\n        recordRenderingTime();\n        return mRenderingState;\n    }\n\n    private void redrawIfNeeded() {\n        if (quitFlag && mDanmakusVisible) {\n            removeMessages(UPDATE_WHEN_PAUSED);\n            sendEmptyMessageDelayed(UPDATE_WHEN_PAUSED, 100);\n        }\n    }\n\n    private void notifyRendering() {\n        if (!mInWaitingState) {\n            return;\n        }\n        if(drawTask != null) {\n            drawTask.requestClear();\n        }\n        if (mUpdateInSeparateThread) {\n            synchronized (this) {\n                mDrawTimes.clear();\n            }\n            synchronized (drawTask) {\n                drawTask.notifyAll();\n            }\n        } else {\n            mDrawTimes.clear();\n            removeMessages(UPDATE);\n            sendEmptyMessage(UPDATE);\n        }\n        mInWaitingState = false;\n    }\n\n    private void waitRendering(long dTime) {\n        if (isStop() || !isPrepared() || mInSeekingAction) {\n            return;\n        }\n        mRenderingState.sysTime = SystemClock.uptimeMillis();\n        mInWaitingState = true;\n        if (mUpdateInSeparateThread) {\n            if (mThread == null) {\n                return;\n            }\n            try {\n                synchronized (drawTask) {\n                    if (dTime == INDEFINITE_TIME) {\n                        drawTask.wait();\n                    } else {\n                        drawTask.wait(dTime);\n                    }\n                    sendEmptyMessage(NOTIFY_RENDERING);\n                }\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        } else {\n            if (dTime == INDEFINITE_TIME) {\n                removeMessages(NOTIFY_RENDERING);\n                removeMessages(UPDATE);\n            } else {\n                removeMessages(NOTIFY_RENDERING);\n                removeMessages(UPDATE);\n                sendEmptyMessageDelayed(NOTIFY_RENDERING, dTime);\n            }\n        }\n    }\n\n    private synchronized long getAverageRenderingTime() {\n        int frames = mDrawTimes.size();\n        if(frames <= 0)\n            return 0;\n        Long first = mDrawTimes.peekFirst();\n        Long last = mDrawTimes.peekLast();\n        if (first == null || last == null) {\n            return 0;\n        }\n        long dtime = last - first;\n        return dtime / frames;\n    }\n\n    private synchronized void recordRenderingTime() {\n        long lastTime = SystemClock.uptimeMillis();\n        mDrawTimes.addLast(lastTime);\n        int frames = mDrawTimes.size();\n        if (frames > MAX_RECORD_SIZE) {\n            mDrawTimes.removeFirst();\n            frames = MAX_RECORD_SIZE;\n        }\n    }\n\n    public IDisplayer getDisplayer(){\n        return mDisp;\n    }\n\n    public void notifyDispSizeChanged(int width, int height) {\n        if (mDisp == null) {\n            return;\n        }\n        if (mDisp.getWidth() != width || mDisp.getHeight() != height) {\n            mDisp.setSize(width, height);\n            obtainMessage(NOTIFY_DISP_SIZE_CHANGED, true).sendToTarget();\n        }\n    }\n\n    public void removeAllDanmakus(boolean isClearDanmakusOnScreen) {\n        if (drawTask != null) {\n            drawTask.removeAllDanmakus(isClearDanmakusOnScreen);\n        }\n    }\n\n    public void removeAllLiveDanmakus() {\n        if (drawTask != null) {\n            drawTask.removeAllLiveDanmakus();\n        }\n    }\n\n    public IDanmakus getCurrentVisibleDanmakus() {\n        if (drawTask != null) {\n            return drawTask.getVisibleDanmakusOnTime(getCurrentTime());\n        }\n\n        return null;\n    }\n\n    public long getCurrentTime() {\n        if (!mReady) {\n            return 0;\n        }\n        if (mInSeekingAction) {\n            return mDesireSeekingTime;\n        }\n        if (quitFlag || !mInWaitingState) {\n            return timer.currMillisecond - mRemainingTime;\n        }\n        return SystemClock.uptimeMillis() - mTimeBase;\n    }\n\n    public void clearDanmakusOnScreen() {\n        obtainMessage(CLEAR_DANMAKUS_ON_SCREEN).sendToTarget();\n    }\n\n    public DanmakuContext getConfig() {\n        return mContext;\n    }\n\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/controller/DrawHelper.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.controller;\r\n\r\nimport android.graphics.Canvas;\r\nimport android.graphics.Color;\r\nimport android.graphics.Paint;\r\nimport android.graphics.PorterDuff;\r\nimport android.graphics.PorterDuffXfermode;\r\nimport android.graphics.RectF;\r\n\r\npublic class DrawHelper {\r\n\r\n    public static Paint PAINT, PAINT_FPS;\r\n\r\n    public static RectF RECT;\r\n    \r\n    private static boolean USE_DRAWCOLOR_TO_CLEAR_CANVAS = true;\r\n    \r\n    private static boolean USE_DRAWCOLOR_MODE_CLEAR = true; \r\n    \r\n    static {\r\n        PAINT = new Paint();\r\n        PAINT.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));\r\n        PAINT.setColor(Color.TRANSPARENT);\r\n        RECT = new RectF();\r\n    }\r\n    \r\n    public static void useDrawColorToClearCanvas(boolean use, boolean useClearMode) {\r\n        USE_DRAWCOLOR_TO_CLEAR_CANVAS = use;\r\n        USE_DRAWCOLOR_MODE_CLEAR = useClearMode;\r\n    }\r\n\r\n    public static void drawFPS(Canvas canvas, String text) {\r\n        if (PAINT_FPS == null) {\r\n            PAINT_FPS = new Paint();\r\n            PAINT_FPS.setColor(Color.RED);\r\n            PAINT_FPS.setTextSize(30);\r\n        }\r\n        int top = canvas.getHeight() - 50;\r\n        \r\n        clearCanvas(canvas, 10, top - 50, (int) (PAINT_FPS.measureText(text) + 20), canvas.getHeight());\r\n        canvas.drawText(text, 10, top, PAINT_FPS);\r\n    }\r\n\r\n    public static void clearCanvas(Canvas canvas) {\r\n        if (USE_DRAWCOLOR_TO_CLEAR_CANVAS) {\r\n            if(USE_DRAWCOLOR_MODE_CLEAR) {\r\n                canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);\r\n            } else {\r\n                canvas.drawColor(Color.TRANSPARENT);\r\n            }\r\n        } else {\r\n            RECT.set(0, 0, canvas.getWidth(), canvas.getHeight());\r\n            clearCanvas(canvas, RECT);\r\n        }\r\n    }\r\n    \r\n    public static void fillTransparent(Canvas canvas){\r\n        canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);\r\n    }\r\n\r\n    public static void clearCanvas(Canvas canvas, float left, float top, float right, float bottom) {\r\n        RECT.set(left, top, right, bottom);\r\n        clearCanvas(canvas, RECT);\r\n    }\r\n\r\n    private static void clearCanvas(Canvas canvas, RectF rect) {\r\n        if (rect.width() <= 0 || rect.height() <= 0) {\r\n            return;\r\n        }\r\n        canvas.drawRect(rect, PAINT);\r\n    }\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/controller/DrawTask.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.controller;\r\n\r\nimport android.graphics.Canvas;\r\n\r\nimport master.flame.danmaku.danmaku.model.AbsDisplayer;\r\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\r\nimport master.flame.danmaku.danmaku.model.DanmakuTimer;\r\nimport master.flame.danmaku.danmaku.model.IDanmakus;\r\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext;\r\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext.ConfigChangedCallback;\r\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext.DanmakuConfigTag;\r\nimport master.flame.danmaku.danmaku.model.android.Danmakus;\r\nimport master.flame.danmaku.danmaku.parser.BaseDanmakuParser;\r\nimport master.flame.danmaku.danmaku.renderer.IRenderer;\r\nimport master.flame.danmaku.danmaku.renderer.IRenderer.RenderingState;\r\nimport master.flame.danmaku.danmaku.renderer.android.DanmakuRenderer;\r\nimport master.flame.danmaku.danmaku.util.SystemClock;\r\n\r\npublic class DrawTask implements IDrawTask {\r\n\r\n    protected final DanmakuContext mContext;\r\n    \r\n    protected final AbsDisplayer mDisp;\r\n\r\n    protected IDanmakus danmakuList;\r\n\r\n    protected BaseDanmakuParser mParser;\r\n\r\n    TaskListener mTaskListener;\r\n\r\n    final IRenderer mRenderer;\r\n\r\n    DanmakuTimer mTimer;\r\n\r\n    private IDanmakus danmakus = new Danmakus(Danmakus.ST_BY_LIST);\r\n\r\n    protected boolean clearRetainerFlag;\r\n\r\n    private long mStartRenderTime = 0;\r\n\r\n    private final RenderingState mRenderingState = new RenderingState();\r\n\r\n    protected boolean mReadyState;\r\n\r\n    private long mLastBeginMills;\r\n\r\n    private long mLastEndMills;\r\n\r\n    protected int mPlayState;\r\n\r\n    private boolean mIsHidden;\r\n\r\n    private BaseDanmaku mLastDanmaku;\r\n\r\n    private Danmakus mLiveDanmakus = new Danmakus(Danmakus.ST_BY_LIST);\r\n\r\n    private IDanmakus mRunningDanmakus;\r\n\r\n    private boolean mRequestRender;\r\n\r\n    private ConfigChangedCallback mConfigChangedCallback = new ConfigChangedCallback() {\r\n        @Override\r\n        public boolean onDanmakuConfigChanged(DanmakuContext config, DanmakuConfigTag tag, Object... values) {\r\n            return DrawTask.this.onDanmakuConfigChanged(config, tag, values);\r\n        }\r\n    };\r\n\r\n    public DrawTask(DanmakuTimer timer, DanmakuContext context,\r\n            TaskListener taskListener) {\r\n        if (context == null) {\r\n            throw new IllegalArgumentException(\"context is null\");\r\n        }\r\n        mContext = context;\r\n        mDisp = context.getDisplayer();\r\n        mTaskListener = taskListener;\r\n        mRenderer = new DanmakuRenderer(context);\r\n        mRenderer.setOnDanmakuShownListener(new IRenderer.OnDanmakuShownListener() {\r\n\r\n            @Override\r\n            public void onDanmakuShown(BaseDanmaku danmaku) {\r\n                if (mTaskListener != null) {\r\n                    mTaskListener.onDanmakuShown(danmaku);\r\n                }\r\n            }\r\n        });\r\n        mRenderer.setVerifierEnabled(mContext.isPreventOverlappingEnabled() || mContext.isMaxLinesLimited());\r\n        initTimer(timer);\r\n        Boolean enable = mContext.isDuplicateMergingEnabled();\r\n        if (enable != null) {\r\n            if(enable) {\r\n                mContext.mDanmakuFilters.registerFilter(DanmakuFilters.TAG_DUPLICATE_FILTER);\r\n            } else {\r\n                mContext.mDanmakuFilters.unregisterFilter(DanmakuFilters.TAG_DUPLICATE_FILTER);\r\n            }\r\n        }\r\n    }\r\n\r\n    protected void initTimer(DanmakuTimer timer) {\r\n        mTimer = timer;\r\n    }\r\n\r\n    @Override\r\n    public synchronized void addDanmaku(BaseDanmaku item) {\r\n        if (danmakuList == null)\r\n            return;\r\n        if (item.isLive) {\r\n            mLiveDanmakus.addItem(item);\r\n            removeUnusedLiveDanmakusIn(10);\r\n        }\r\n        item.index = danmakuList.size();\r\n        boolean subAdded = true;\r\n        if (mLastBeginMills <= item.getActualTime() && item.getActualTime() <= mLastEndMills) {\r\n            synchronized (danmakus) {\r\n                subAdded = danmakus.addItem(item);\r\n            }\r\n        } else if (item.isLive) {\r\n            subAdded = false;\r\n        }\r\n        boolean added = false;\r\n        synchronized (danmakuList) {\r\n            added = danmakuList.addItem(item);\r\n        }\r\n        if (!subAdded || !added) {\r\n            mLastBeginMills = mLastEndMills = 0;\r\n        }\r\n        if (added && mTaskListener != null) {\r\n            mTaskListener.onDanmakuAdd(item);\r\n        }\r\n        if (mLastDanmaku == null || (item != null && mLastDanmaku != null && item.getActualTime() > mLastDanmaku.getActualTime())) {\r\n            mLastDanmaku = item;\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void invalidateDanmaku(BaseDanmaku item, boolean remeasure) {\r\n        mContext.getDisplayer().getCacheStuffer().clearCache(item);\r\n        item.requestFlags |= BaseDanmaku.FLAG_REQUEST_INVALIDATE;\r\n        if (remeasure) {\r\n            item.paintWidth = -1;\r\n            item.paintHeight = -1;\r\n            item.requestFlags |= BaseDanmaku.FLAG_REQUEST_REMEASURE;\r\n            item.measureResetFlag++;\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public synchronized void removeAllDanmakus(boolean isClearDanmakusOnScreen) {\r\n        if (danmakuList == null || danmakuList.isEmpty())\r\n            return;\r\n        synchronized (danmakuList) {\r\n            if (!isClearDanmakusOnScreen) {\r\n                long beginMills = mTimer.currMillisecond - mContext.mDanmakuFactory.MAX_DANMAKU_DURATION - 100;\r\n                long endMills = mTimer.currMillisecond + mContext.mDanmakuFactory.MAX_DANMAKU_DURATION;\r\n                IDanmakus tempDanmakus = danmakuList.subnew(beginMills, endMills);\r\n                if (tempDanmakus != null)\r\n                    danmakus = tempDanmakus;\r\n            }\r\n            danmakuList.clear();\r\n        }\r\n    }\r\n\r\n    protected void onDanmakuRemoved(BaseDanmaku danmaku) {\r\n        // override by CacheManagingDrawTask\r\n    }\r\n\r\n    @Override\r\n    public synchronized void removeAllLiveDanmakus() {\r\n        if (danmakus == null || danmakus.isEmpty())\r\n            return;\r\n        synchronized (danmakus) {\r\n            danmakus.forEachSync(new IDanmakus.DefaultConsumer<BaseDanmaku>() {\r\n                @Override\r\n                public int accept(BaseDanmaku danmaku) {\r\n                    if (danmaku.isLive) {\r\n                        onDanmakuRemoved(danmaku);\r\n                        return ACTION_REMOVE;\r\n                    }\r\n                    return ACTION_CONTINUE;\r\n                }\r\n            });\r\n        }\r\n    }\r\n\r\n    protected synchronized void removeUnusedLiveDanmakusIn(final int msec) {\r\n        if (danmakuList == null || danmakuList.isEmpty() || mLiveDanmakus.isEmpty())\r\n            return;\r\n        mLiveDanmakus.forEachSync(new IDanmakus.DefaultConsumer<BaseDanmaku>() {\r\n            long startTime = SystemClock.uptimeMillis();\r\n\r\n            @Override\r\n            public int accept(BaseDanmaku danmaku) {\r\n                boolean isTimeout = danmaku.isTimeOut();\r\n                if (SystemClock.uptimeMillis() - startTime > msec) {\r\n                    return ACTION_BREAK;\r\n                }\r\n                if (isTimeout) {\r\n                    danmakuList.removeItem(danmaku);\r\n                    onDanmakuRemoved(danmaku);\r\n                    return ACTION_REMOVE;\r\n                } else {\r\n                    return ACTION_BREAK;\r\n                }\r\n\r\n            }\r\n        });\r\n    }\r\n\r\n    @Override\r\n    public IDanmakus getVisibleDanmakusOnTime(long time) {\r\n        long beginMills = time - mContext.mDanmakuFactory.MAX_DANMAKU_DURATION - 100;\r\n        long endMills = time + mContext.mDanmakuFactory.MAX_DANMAKU_DURATION;\r\n        IDanmakus subDanmakus = null;\r\n        int i = 0;\r\n        while (i++ < 3) {  //avoid ConcurrentModificationException\r\n            try {\r\n                subDanmakus = danmakuList.subnew(beginMills, endMills);\r\n                break;\r\n            } catch (Exception e) {\r\n\r\n            }\r\n        }\r\n        final IDanmakus visibleDanmakus = new Danmakus();\r\n        if (null != subDanmakus && !subDanmakus.isEmpty()) {\r\n            subDanmakus.forEachSync(new IDanmakus.DefaultConsumer<BaseDanmaku>() {\r\n                @Override\r\n                public int accept(BaseDanmaku danmaku) {\r\n                    if (danmaku.isShown() && !danmaku.isOutside()) {\r\n                        visibleDanmakus.addItem(danmaku);\r\n                    }\r\n                    return ACTION_CONTINUE;\r\n                }\r\n            });\r\n        }\r\n\r\n        return visibleDanmakus;\r\n    }\r\n\r\n    @Override\r\n    public synchronized RenderingState draw(AbsDisplayer displayer) {\r\n        return drawDanmakus(displayer,mTimer);\r\n    }\r\n\r\n    @Override\r\n    public void reset() {\r\n        if (danmakus != null)\r\n            danmakus = new Danmakus();\r\n        if (mRenderer != null)\r\n            mRenderer.clear();\r\n    }\r\n\r\n    @Override\r\n    public void seek(long mills) {\r\n        reset();\r\n        mContext.mGlobalFlagValues.updateVisibleFlag();\r\n        mContext.mGlobalFlagValues.updateFirstShownFlag();\r\n        mContext.mGlobalFlagValues.updateSyncOffsetTimeFlag();\r\n        mContext.mGlobalFlagValues.updatePrepareFlag();\r\n        mRunningDanmakus = new Danmakus(Danmakus.ST_BY_LIST);\r\n        mStartRenderTime = mills < 1000 ? 0 : mills;\r\n        mRenderingState.reset();\r\n        mRenderingState.endTime = mStartRenderTime;\r\n        mLastBeginMills = mLastEndMills = 0;\r\n\r\n        if (danmakuList != null) {\r\n            BaseDanmaku last = danmakuList.last();\r\n            if (last != null && !last.isTimeOut()) {\r\n                mLastDanmaku = last;\r\n            }\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void clearDanmakusOnScreen(long currMillis) {\r\n        reset();\r\n        mContext.mGlobalFlagValues.updateVisibleFlag();\r\n        mContext.mGlobalFlagValues.updateFirstShownFlag();\r\n        mStartRenderTime = currMillis;\r\n    }\r\n\r\n    @Override\r\n    public void start() {\r\n        mContext.registerConfigChangedCallback(mConfigChangedCallback);\r\n    }\r\n\r\n    @Override\r\n    public void quit() {\r\n        mContext.unregisterAllConfigChangedCallbacks();\r\n        if (mRenderer != null)\r\n            mRenderer.release();\r\n    }\r\n\r\n    public void prepare() {\r\n        if (mParser == null) {\r\n            return;\r\n        }\r\n        loadDanmakus(mParser);\r\n        mLastBeginMills = mLastEndMills = 0;\r\n        if (mTaskListener != null) {\r\n            mTaskListener.ready();\r\n            mReadyState = true;\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void onPlayStateChanged(int state) {\r\n        mPlayState = state;\r\n    }\r\n\r\n    protected void loadDanmakus(BaseDanmakuParser parser) {\r\n        danmakuList = parser.setConfig(mContext).setDisplayer(mDisp).setTimer(mTimer).setListener(new BaseDanmakuParser.Listener() {\r\n            @Override\r\n            public void onDanmakuAdd(BaseDanmaku danmaku) {\r\n                if (mTaskListener != null) {\r\n                    mTaskListener.onDanmakuAdd(danmaku);\r\n                }\r\n            }\r\n        }).getDanmakus();\r\n        mContext.mGlobalFlagValues.resetAll();\r\n        if(danmakuList != null) {\r\n            mLastDanmaku = danmakuList.last();\r\n        }\r\n    }\r\n\r\n    public void setParser(BaseDanmakuParser parser) {\r\n        mParser = parser;\r\n        mReadyState = false;\r\n    }\r\n\r\n    protected RenderingState drawDanmakus(AbsDisplayer disp, DanmakuTimer timer) {\r\n        if (clearRetainerFlag) {\r\n            mRenderer.clearRetainer();\r\n            clearRetainerFlag = false;\r\n        }\r\n        if (danmakuList != null) {\r\n            Canvas canvas = (Canvas) disp.getExtraData();\r\n            DrawHelper.clearCanvas(canvas);\r\n            if (mIsHidden && !mRequestRender) {\r\n                return mRenderingState;\r\n            }\r\n\r\n            mRequestRender = false;\r\n            RenderingState renderingState = mRenderingState;\r\n            // prepare screenDanmakus\r\n            long beginMills = timer.currMillisecond - mContext.mDanmakuFactory.MAX_DANMAKU_DURATION - 100;\r\n            long endMills = timer.currMillisecond + mContext.mDanmakuFactory.MAX_DANMAKU_DURATION;\r\n            IDanmakus screenDanmakus = danmakus;\r\n            if(mLastBeginMills > beginMills || timer.currMillisecond > mLastEndMills) {\r\n                screenDanmakus = danmakuList.sub(beginMills, endMills);\r\n                if (screenDanmakus != null) {\r\n                    danmakus = screenDanmakus;\r\n                }\r\n                mLastBeginMills = beginMills;\r\n                mLastEndMills = endMills;\r\n            } else {\r\n                beginMills = mLastBeginMills;\r\n                endMills = mLastEndMills;\r\n            }\r\n\r\n            // prepare runningDanmakus to draw (in sync-mode)\r\n            IDanmakus runningDanmakus = mRunningDanmakus;\r\n            beginTracing(renderingState, runningDanmakus, screenDanmakus);\r\n            if (runningDanmakus != null && !runningDanmakus.isEmpty()) {\r\n                mRenderingState.isRunningDanmakus = true;\r\n                mRenderer.draw(disp, runningDanmakus, 0, mRenderingState);\r\n            }\r\n\r\n            // draw screenDanmakus\r\n            mRenderingState.isRunningDanmakus = false;\r\n            if (screenDanmakus != null && !screenDanmakus.isEmpty()) {\r\n                mRenderer.draw(mDisp, screenDanmakus, mStartRenderTime, renderingState);\r\n                endTracing(renderingState);\r\n                if (renderingState.nothingRendered) {\r\n                    if(mLastDanmaku != null && mLastDanmaku.isTimeOut()) {\r\n                        mLastDanmaku = null;\r\n                        if (mTaskListener != null) {\r\n                            mTaskListener.onDanmakusDrawingFinished();\r\n                        }\r\n                    }\r\n                    if (renderingState.beginTime == RenderingState.UNKNOWN_TIME) {\r\n                        renderingState.beginTime = beginMills;\r\n                    }\r\n                    if (renderingState.endTime == RenderingState.UNKNOWN_TIME) {\r\n                        renderingState.endTime = endMills;\r\n                    }\r\n                }\r\n                return renderingState;\r\n            } else {\r\n                renderingState.nothingRendered = true;\r\n                renderingState.beginTime = beginMills;\r\n                renderingState.endTime = endMills;\r\n                return renderingState;\r\n            }\r\n        }\r\n        return null;\r\n    }\r\n\r\n    @Override\r\n    public void requestClear() {\r\n        mLastBeginMills = mLastEndMills = 0;\r\n        mIsHidden = false;\r\n    }\r\n\r\n    @Override\r\n    public void requestClearRetainer() {\r\n        clearRetainerFlag = true;\r\n    }\r\n\r\n    @Override\r\n    public void requestSync(long fromTimeMills, long toTimeMills, final long offsetMills) {\r\n        // obtain the running-danmakus which was drawn on screen\r\n        IDanmakus runningDanmakus = mRenderingState.obtainRunningDanmakus();\r\n        mRunningDanmakus = runningDanmakus;\r\n        // set offset time for each running-danmakus\r\n        runningDanmakus.forEachSync(new IDanmakus.DefaultConsumer<BaseDanmaku>() {\r\n            @Override\r\n            public int accept(BaseDanmaku danmaku) {\r\n                if (danmaku.isOutside()) {\r\n                    return ACTION_REMOVE;\r\n                }\r\n                danmaku.setTimeOffset(offsetMills + danmaku.timeOffset);\r\n                if (danmaku.timeOffset == 0) {\r\n                    return ACTION_REMOVE;\r\n                }\r\n                return ACTION_CONTINUE;\r\n            }\r\n        });\r\n        mStartRenderTime = toTimeMills;\r\n    }\r\n\r\n    public boolean onDanmakuConfigChanged(DanmakuContext config, DanmakuConfigTag tag,\r\n            Object... values) {\r\n        boolean handled = handleOnDanmakuConfigChanged(config, tag, values);\r\n        if (mTaskListener != null) {\r\n            mTaskListener.onDanmakuConfigChanged();\r\n        }\r\n        return handled;\r\n    }\r\n\r\n    protected boolean handleOnDanmakuConfigChanged(DanmakuContext config, DanmakuConfigTag tag, Object[] values) {\r\n        boolean handled = false;\r\n        if (tag == null || DanmakuConfigTag.MAXIMUM_NUMS_IN_SCREEN.equals(tag)) {\r\n            handled = true;\r\n        } else if (DanmakuConfigTag.DUPLICATE_MERGING_ENABLED.equals(tag)) {\r\n            Boolean enable = (Boolean) values[0];\r\n            if (enable != null) {\r\n                if (enable) {\r\n                    mContext.mDanmakuFilters.registerFilter(DanmakuFilters.TAG_DUPLICATE_FILTER);\r\n                } else {\r\n                    mContext.mDanmakuFilters.unregisterFilter(DanmakuFilters.TAG_DUPLICATE_FILTER);\r\n                }\r\n                handled = true;\r\n            }\r\n        } else if (DanmakuConfigTag.SCALE_TEXTSIZE.equals(tag) || DanmakuConfigTag.SCROLL_SPEED_FACTOR.equals(tag) || DanmakuConfigTag.DANMAKU_MARGIN.equals(tag)) {\r\n            requestClearRetainer();\r\n            handled = false;\r\n        } else if (DanmakuConfigTag.MAXIMUN_LINES.equals(tag) || DanmakuConfigTag.OVERLAPPING_ENABLE.equals(tag)) {\r\n            if (mRenderer != null) {\r\n                mRenderer.setVerifierEnabled(mContext.isPreventOverlappingEnabled() || mContext.isMaxLinesLimited());\r\n            }\r\n            handled = true;\r\n        } else if (DanmakuConfigTag.ALIGN_BOTTOM.equals(tag)) {\r\n            Boolean enable = (Boolean) values[0];\r\n            if (enable != null) {\r\n                if (mRenderer != null) {\r\n                    mRenderer.alignBottom(enable);\r\n                }\r\n                handled = true;\r\n            }\r\n        }\r\n        return handled;\r\n    }\r\n\r\n    @Override\r\n    public void requestHide() {\r\n        mIsHidden = true;\r\n    }\r\n\r\n    @Override\r\n    public void requestRender() {\r\n        this.mRequestRender = true;\r\n    }\r\n\r\n    private void beginTracing(RenderingState renderingState, IDanmakus runningDanmakus, IDanmakus screenDanmakus) {\r\n        renderingState.reset();\r\n        renderingState.timer.update(SystemClock.uptimeMillis());\r\n        renderingState.indexInScreen = 0;\r\n        renderingState.totalSizeInScreen = (runningDanmakus != null ? runningDanmakus.size() : 0) + (screenDanmakus != null ? screenDanmakus.size() : 0);\r\n    }\r\n\r\n    private void endTracing(RenderingState renderingState) {\r\n        renderingState.nothingRendered = (renderingState.totalDanmakuCount == 0);\r\n        if (renderingState.nothingRendered) {\r\n            renderingState.beginTime = RenderingState.UNKNOWN_TIME;\r\n        }\r\n        BaseDanmaku lastDanmaku = renderingState.lastDanmaku;\r\n        renderingState.lastDanmaku = null;\r\n        renderingState.endTime = lastDanmaku != null ? lastDanmaku.getActualTime() : RenderingState.UNKNOWN_TIME;\r\n        renderingState.consumingTime = renderingState.timer.update(SystemClock.uptimeMillis());\r\n    }\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/controller/IDanmakuView.java",
    "content": "\npackage master.flame.danmaku.controller;\n\nimport android.view.View;\n\nimport master.flame.danmaku.controller.DrawHandler.Callback;\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\nimport master.flame.danmaku.danmaku.model.IDanmakus;\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext;\nimport master.flame.danmaku.danmaku.parser.BaseDanmakuParser;\n\npublic interface IDanmakuView {\n    \n    public final static int THREAD_TYPE_NORMAL_PRIORITY = 0x0;\n    public final static int THREAD_TYPE_MAIN_THREAD = 0x1;\n    public final static int THREAD_TYPE_HIGH_PRIORITY = 0x2;\n    public final static int THREAD_TYPE_LOW_PRIORITY = 0x3;\n    \n\n    public boolean isPrepared();\n    \n    public boolean isPaused();\n\n    public boolean isHardwareAccelerated();\n    /**\n     * \n     * @param type One of THREAD_TYPE_MAIN_THREAD, THREAD_TYPE_HIGH_PRIORITY, THREAD_TYPE_NORMAL_PRIORITY, or THREAD_TYPE_LOW_PRIORITY.\n     */\n    public void setDrawingThreadType(int type);\n\n    public void enableDanmakuDrawingCache(boolean enable);\n\n    public boolean isDanmakuDrawingCacheEnabled();\n\n    public void showFPS(boolean show);\n    \n    /**\n     * danmaku.isLive == true的情况下,请在非UI线程中使用此方法,避免可能卡住主线程\n     * @param item\n     */\n    public void addDanmaku(BaseDanmaku item);\n\n    public void invalidateDanmaku(BaseDanmaku item, boolean remeasure);\n    \n    public void removeAllDanmakus(boolean isClearDanmakusOnScreen);\n    \n    public void removeAllLiveDanmakus();\n\n    public IDanmakus getCurrentVisibleDanmakus();\n    \n    public void setCallback(Callback callback);\n    \n    /**\n     * for getting the accurate play-time. use this method intead of parser.getTimer().currMillisecond\n     * @return\n     */\n    public long getCurrentTime();\n\n    public DanmakuContext getConfig();\n    \n    // ------------- Android View方法  --------------------\n    \n    public View getView();\n\n    public int getWidth();\n\n    public int getHeight();\n\n    public void setVisibility(int visibility);\n    \n    public boolean isShown();\n    \n\n    // ------------- 播放控制 -------------------\n    \n    public void prepare(BaseDanmakuParser parser, DanmakuContext config);\n\n    public void seekTo(Long ms);\n\n    public void start();\n\n    public void start(long postion);\n\n    public void stop();\n\n    public void pause();\n\n    public void resume();\n\n    public void release();\n    \n    public void toggle();\n    \n    public void show();\n    \n    public void hide();\n    \n    /**\n     * show the danmakuview again if you called hideAndPauseDrawTask()\n     * @param position The position you want to resume\n     * @see #hideAndPauseDrawTask\n     */\n    public void showAndResumeDrawTask(Long position);\n    \n    /**\n     * hide the danmakuview and pause the drawtask\n     * @return the paused position\n     * @see #showAndResumeDrawTask\n     */\n    public long hideAndPauseDrawTask();\n\n    public void clearDanmakusOnScreen();\n\n    // ------------- Click Listener -------------------\n    public interface OnDanmakuClickListener {\n        /**\n         * @param danmakus all to be clicked, this value may be empty;\n         *                 danmakus.last() is the latest danmaku which may be null;\n         * @return True if the event was handled, false otherwise.\n         */\n        boolean onDanmakuClick(IDanmakus danmakus);\n\n        boolean onDanmakuLongClick(IDanmakus danmakus);\n\n        boolean onViewClick(IDanmakuView view);\n    }\n\n    public void setOnDanmakuClickListener(OnDanmakuClickListener listener);\n\n    public void setOnDanmakuClickListener(OnDanmakuClickListener listener, float xOff, float yOff);\n\n    public OnDanmakuClickListener getOnDanmakuClickListener();\n\n    public float getXOff();\n\n    public float getYOff();\n\n    void forceRender();\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/controller/IDanmakuViewController.java",
    "content": "package master.flame.danmaku.controller;\n\nimport android.content.Context;\n\n/**\n * For internal control. DO NOT ACCESS this interface.\n */\npublic interface IDanmakuViewController {\n\n    public boolean isViewReady();\n\n    public int getViewWidth();\n\n    public int getViewHeight();\n\n    public Context getContext();\n\n    public long drawDanmakus();\n\n    public void clear();\n\n    public boolean isHardwareAccelerated();\n\n    public boolean isDanmakuDrawingCacheEnabled();\n\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/controller/IDrawTask.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.controller;\r\n\r\nimport master.flame.danmaku.danmaku.model.AbsDisplayer;\r\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\r\nimport master.flame.danmaku.danmaku.model.IDanmakus;\r\nimport master.flame.danmaku.danmaku.parser.BaseDanmakuParser;\r\nimport master.flame.danmaku.danmaku.renderer.IRenderer.RenderingState;\r\n\r\npublic interface IDrawTask {\r\n    public static final int PLAY_STATE_PLAYING = 1;\r\n    public static final int PLAY_STATE_PAUSE = 2;\r\n\r\n    public void addDanmaku(BaseDanmaku item);\r\n\r\n    public void removeAllDanmakus(boolean isClearDanmakusOnScreen);\r\n\r\n    public void removeAllLiveDanmakus();\r\n\r\n    public void clearDanmakusOnScreen(long currMillis);\r\n\r\n\tpublic IDanmakus getVisibleDanmakusOnTime(long time);\r\n\r\n    public RenderingState draw(AbsDisplayer displayer);\r\n\r\n    public void reset();\r\n\r\n    public void seek(long mills);\r\n\r\n    public void start();\r\n\r\n    public void quit();\r\n\r\n    public void prepare();\r\n\r\n    public void onPlayStateChanged(int state);\r\n\r\n    public void requestClear();\r\n\r\n    void requestClearRetainer();\r\n\r\n    void requestSync(long fromTimeMills, long toTimeMills, long offsetMills);\r\n\r\n    public void setParser(BaseDanmakuParser parser);\r\n\r\n    void invalidateDanmaku(BaseDanmaku item, boolean remeasure);\r\n\r\n    public interface TaskListener {\r\n        public void ready();\r\n\r\n        public void onDanmakuAdd(BaseDanmaku danmaku);\r\n\r\n        public void onDanmakuShown(BaseDanmaku danmaku);\r\n\r\n        public void onDanmakuConfigChanged();\r\n\r\n        public void onDanmakusDrawingFinished();\r\n    }\r\n\r\n    public void requestHide();\r\n\r\n    void requestRender();\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/controller/UpdateThread.java",
    "content": "\npackage master.flame.danmaku.controller;\n\npublic class UpdateThread extends Thread {\n\n    public UpdateThread(String name) {\n        super(name);\n    }\n\n    volatile boolean mIsQuited;\n\n    public void quit() {\n        mIsQuited = true;\n    }\n    \n    public boolean isQuited() {\n        return mIsQuited;\n    }\n\n    @Override\n    public void run() {\n        if (mIsQuited)\n            return;\n    }\n\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/loader/ILoader.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.loader;\r\n\r\nimport java.io.InputStream;\r\n\r\nimport master.flame.danmaku.danmaku.parser.IDataSource;\r\n\r\npublic interface ILoader {\r\n    /**\r\n     * @return data source\r\n     */\r\n    IDataSource<?> getDataSource();\r\n    /**\r\n     * @param uri 弹幕文件地址(http:// file://)\r\n     */\r\n    void load(String uri) throws IllegalDataException;\r\n    /**\r\n     * \r\n     * @param in stream from Internet or local file\r\n     */\r\n    void load(InputStream in) throws IllegalDataException;\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/loader/IllegalDataException.java",
    "content": "package master.flame.danmaku.danmaku.loader;\r\n/**\r\n * Thrown when data is loading which can not be reasonably deal with.\r\n * @author yrom\r\n *\r\n */\r\npublic class IllegalDataException extends Exception {\r\n\t\r\n\tprivate static final long serialVersionUID = 10441759254L;\r\n\r\n\tpublic IllegalDataException() {\r\n\t\tsuper();\r\n\t}\r\n\r\n\tpublic IllegalDataException(String detailMessage, Throwable throwable) {\r\n\t\tsuper(detailMessage, throwable);\r\n\t}\r\n\r\n\tpublic IllegalDataException(String detailMessage) {\r\n\t\tsuper(detailMessage);\r\n\t}\r\n\r\n\tpublic IllegalDataException(Throwable throwable) {\r\n\t\tsuper(throwable);\r\n\t}\r\n\t\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/loader/android/AcFunDanmakuLoader.java",
    "content": "package master.flame.danmaku.danmaku.loader.android;\r\n\r\nimport java.io.InputStream;\r\n\r\nimport master.flame.danmaku.danmaku.loader.ILoader;\r\nimport master.flame.danmaku.danmaku.loader.IllegalDataException;\r\nimport master.flame.danmaku.danmaku.parser.android.JSONSource;\r\nimport android.net.Uri;\r\n/**\r\n * Ac danmaku loader\r\n * @author yrom\r\n *\r\n */\r\npublic class AcFunDanmakuLoader implements ILoader{\r\n\tprivate AcFunDanmakuLoader(){}\r\n\tprivate static volatile AcFunDanmakuLoader instance;\r\n\tprivate JSONSource dataSource;\r\n\t\r\n\tpublic static ILoader instance() {\r\n\t\tif(instance == null){\r\n\t\t\tsynchronized (AcFunDanmakuLoader.class){\r\n\t\t\t\tif(instance == null)\r\n\t\t\t\t\tinstance = new AcFunDanmakuLoader();\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn instance;\r\n\t}\r\n\t\r\n\t@Override\r\n\tpublic JSONSource getDataSource() {\r\n\t\treturn dataSource;\r\n\t}\r\n\t\r\n\t@Override\r\n\tpublic void load(String uri) throws IllegalDataException {\r\n\t\ttry {\r\n\t\t\tdataSource = new JSONSource(Uri.parse(uri));\r\n\t\t} catch (Exception e) {\r\n\t\t\tthrow new IllegalDataException(e);\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void load(InputStream in) throws IllegalDataException {\r\n\t\ttry {\r\n\t\t\tdataSource = new JSONSource(in);\r\n\t\t} catch (Exception e) {\r\n\t\t\tthrow new IllegalDataException(e);\r\n\t\t}\r\n\t}\r\n\t\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/loader/android/BiliDanmakuLoader.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.loader.android;\r\n\r\nimport java.io.InputStream;\r\n\r\nimport master.flame.danmaku.danmaku.loader.ILoader;\r\nimport master.flame.danmaku.danmaku.loader.IllegalDataException;\r\nimport master.flame.danmaku.danmaku.parser.android.AndroidFileSource;\r\n\r\npublic class BiliDanmakuLoader implements ILoader {\r\n\r\n    private static BiliDanmakuLoader _instance;\r\n\r\n    private AndroidFileSource dataSource;\r\n\r\n    private BiliDanmakuLoader() {\r\n\r\n    }\r\n\r\n    public static BiliDanmakuLoader instance() {\r\n        if (_instance == null) {\r\n            _instance = new BiliDanmakuLoader();\r\n        }\r\n        return _instance;\r\n    }\r\n\r\n    public void load(String uri) throws IllegalDataException {\r\n        try {            \r\n            dataSource = new AndroidFileSource(uri);\r\n        } catch (Exception e) {\r\n        \tthrow new IllegalDataException(e);\r\n        }\r\n    }\r\n\r\n    public void load(InputStream stream) {\r\n        dataSource = new AndroidFileSource(stream);\r\n    }\r\n\r\n    @Override\r\n    public AndroidFileSource getDataSource() {\r\n        return dataSource;\r\n    }\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/loader/android/DanmakuLoaderFactory.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.loader.android;\r\n\r\nimport master.flame.danmaku.danmaku.loader.ILoader;\r\n\r\npublic class DanmakuLoaderFactory {\r\n\r\n    public static String TAG_BILI = \"bili\";\r\n    public static String TAG_ACFUN = \"acfun\";\r\n    \r\n    public static ILoader create(String tag) {\r\n        if (TAG_BILI.equalsIgnoreCase(tag)) {\r\n            return BiliDanmakuLoader.instance();\r\n        } else if(TAG_ACFUN.equalsIgnoreCase(tag))\r\n        \treturn AcFunDanmakuLoader.instance();\r\n        return null;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/AbsDanmakuSync.java",
    "content": "package master.flame.danmaku.danmaku.model;\n\npublic abstract class AbsDanmakuSync {\n\n    public static final int SYNC_STATE_HALT = 1;\n\n    public static final int SYNC_STATE_PLAYING = 2;\n\n    /**\n     * Get the uptime of timer synchronization\n     *\n     * @return\n     */\n    public abstract long getUptimeMillis();\n\n    /**\n     * Get the state of timer synchronization\n     *\n     * @return SYNC_STATE_HALT or SYNC_STATE_PLAYING\n     */\n    public abstract int getSyncState();\n\n    /**\n     * Get the threshold-time of timer synchronization\n     * This value should be greater than or equal to 1000L\n     *\n     * @return\n     */\n    public long getThresholdTimeMills() {\n        return 1500L;\n    }\n\n    /**\n     * synchronize pause/resume state with outside playback\n     * @return\n     */\n    public boolean isSyncPlayingState() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/AbsDisplayer.java",
    "content": "package master.flame.danmaku.danmaku.model;\n\nimport master.flame.danmaku.danmaku.model.android.BaseCacheStuffer;\n\npublic abstract class AbsDisplayer<T, F> implements IDisplayer {\n    \n    public abstract T getExtraData();\n    \n    public abstract void setExtraData(T data);\n\n    @Override\n    public boolean isHardwareAccelerated() {\n        return false;\n    }\n\n    public abstract void drawDanmaku(BaseDanmaku danmaku, T canvas, float left, float top, boolean fromWorkerThread);\n\n    public abstract void clearTextHeightCache();\n\n    public abstract void setTypeFace(F font);\n\n    public abstract void setFakeBoldText(boolean bold);\n\n    public abstract void setTransparency(int newTransparency);\n\n    public abstract void setScaleTextSizeFactor(float factor);\n\n    public abstract void setCacheStuffer(BaseCacheStuffer cacheStuffer);\n\n    public abstract BaseCacheStuffer getCacheStuffer();\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/AlphaValue.java",
    "content": "\r\npackage master.flame.danmaku.danmaku.model;\r\n\r\npublic class AlphaValue {\r\n\r\n    public static int MAX = 255;\r\n\r\n    public static int TRANSPARENT = 0;\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/BaseDanmaku.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.model;\r\n\r\nimport android.util.SparseArray;\r\n\r\npublic abstract class BaseDanmaku {\r\n\r\n    public final static String DANMAKU_BR_CHAR = \"/n\";\r\n\r\n    public final static int TYPE_SCROLL_RL = 1;\r\n\r\n    public final static int TYPE_SCROLL_LR = 6;\r\n\r\n    public final static int TYPE_FIX_TOP = 5;\r\n\r\n    public final static int TYPE_FIX_BOTTOM = 4;\r\n\r\n    public final static int TYPE_SPECIAL = 7;\r\n\r\n    public final static int TYPE_MOVEABLE_XXX = 0; // TODO: add more type\r\n\r\n    public final static int INVISIBLE = 0;\r\n\r\n    public final static int VISIBLE = 1;\r\n\r\n    public final static int FLAG_REQUEST_REMEASURE = 0x1;\r\n    public final static int FLAG_REQUEST_INVALIDATE = 0x2;\r\n\r\n    /**\r\n     * 显示时间(毫秒)\r\n     */\r\n    private long time;\r\n\r\n    /**\r\n     * 偏移时间\r\n     */\r\n    public long timeOffset;\r\n\r\n    /**\r\n     * 文本\r\n     */\r\n    public CharSequence text;\r\n\r\n    /**\r\n     * 多行文本: 如果有包含换行符需事先拆分到lines\r\n     */\r\n    public String[] lines;\r\n\r\n    /**\r\n     * 保存一些数据的引用(库内部使用, 外部使用请用tag)\r\n     */\r\n    public Object obj;\r\n\r\n    /**\r\n     * 可保存一些自定义数据的引用(外部使用).\r\n     * 除非主动set null,否则不会自动释放引用.\r\n     * 确定你会主动set null, 否则不要使用这个字段引用大内存的对象实例.\r\n     */\r\n    public Object tag;\r\n\r\n    /**\r\n     * 文本颜色\r\n     */\r\n    public int textColor;\r\n\r\n    /**\r\n     * Z轴角度\r\n     */\r\n    public float rotationZ;\r\n\r\n    /**\r\n     * Y轴角度\r\n     */\r\n    public float rotationY;\r\n\r\n    /**\r\n     * 阴影/描边颜色\r\n     */\r\n    public int textShadowColor;\r\n\r\n    /**\r\n     * 下划线颜色,0表示无下划线\r\n     */\r\n    public int underlineColor = 0;\r\n\r\n    /**\r\n     * 字体大小\r\n     */\r\n    public float textSize = -1;\r\n\r\n    /**\r\n     * 框的颜色,0表示无框\r\n     */\r\n    public int borderColor = 0;\r\n\r\n    /**\r\n     * 内边距(像素)\r\n     */\r\n    public int padding = 0;\r\n\r\n    /**\r\n     * 弹幕优先级,0为低优先级,>0为高优先级不会被过滤器过滤\r\n     */\r\n    public byte priority = 0;\r\n\r\n    /**\r\n     * 占位宽度\r\n     */\r\n    public float paintWidth = -1;\r\n\r\n    /**\r\n     * 占位高度\r\n     */\r\n    public float paintHeight = -1;\r\n\r\n    /**\r\n     * 存活时间(毫秒)\r\n     */\r\n    public Duration duration;\r\n\r\n    /**\r\n     * 索引/编号\r\n     */\r\n    public int index;\r\n\r\n    /**\r\n     * 是否可见\r\n     */\r\n    public int visibility;\r\n\r\n    /**\r\n     * 重置位 visible\r\n     */\r\n    private int visibleResetFlag = 0;\r\n\r\n    /**\r\n     * 重置位 measure\r\n     */\r\n    public int measureResetFlag = 0;\r\n\r\n    /**\r\n     * 重置位 offset time\r\n     */\r\n    public int syncTimeOffsetResetFlag = 0;\r\n\r\n    /**\r\n     * 重置位 prepare\r\n     */\r\n    public int prepareResetFlag = -1;\r\n\r\n    /**\r\n     * 绘制用缓存\r\n     */\r\n    public IDrawingCache<?> cache;\r\n\r\n    /**\r\n     * 是否是直播弹幕\r\n     */\r\n    public boolean isLive;\r\n\r\n    /**\r\n     * 临时, 是否在同线程创建缓存\r\n     */\r\n    public boolean forceBuildCacheInSameThread;\r\n\r\n    /**\r\n     * 弹幕发布者id, 0表示游客\r\n     */\r\n    public int userId = 0;\r\n\r\n    /**\r\n     * 弹幕发布者id\r\n     */\r\n    public String userHash;\r\n\r\n    /**\r\n     * 是否游客\r\n     */\r\n    public boolean isGuest;\r\n\r\n    /**\r\n     * 计时\r\n     */\r\n    protected DanmakuTimer mTimer;\r\n\r\n    /**\r\n     * 透明度\r\n     */\r\n    protected int alpha = AlphaValue.MAX;\r\n\r\n    public int mFilterParam = 0;\r\n\r\n    public int filterResetFlag = -1;\r\n\r\n    public GlobalFlagValues flags = null;\r\n\r\n    public int requestFlags = 0;\r\n\r\n    /**\r\n     * 标记是否首次显示，首次显示后将置为FIRST_SHOWN_RESET_FLAG\r\n     */\r\n    public int firstShownFlag = -1;\r\n\r\n    private SparseArray<Object> mTags = new SparseArray<>();\r\n\r\n    public long getDuration() {\r\n        return duration.value;\r\n    }\r\n\r\n    public void setDuration(Duration duration) {\r\n        this.duration = duration;\r\n    }\r\n\r\n    public int draw(IDisplayer displayer) {\r\n        return displayer.draw(this);\r\n    }\r\n\r\n    public boolean isMeasured() {\r\n        return paintWidth > -1 && paintHeight > -1\r\n                && measureResetFlag == flags.MEASURE_RESET_FLAG;\r\n    }\r\n\r\n    public void measure(IDisplayer displayer, boolean fromWorkerThread) {\r\n        displayer.measure(this, fromWorkerThread);\r\n        this.measureResetFlag = flags.MEASURE_RESET_FLAG;\r\n    }\r\n\r\n    public boolean isPrepared() {\r\n        return this.prepareResetFlag == flags.PREPARE_RESET_FLAG;\r\n    }\r\n\r\n    public void prepare(IDisplayer displayer, boolean fromWorkerThread) {\r\n        displayer.prepare(this, fromWorkerThread);\r\n        this.prepareResetFlag = flags.PREPARE_RESET_FLAG;\r\n    }\r\n\r\n    public IDrawingCache<?> getDrawingCache() {\r\n        return cache;\r\n    }\r\n\r\n    public boolean isShown() {\r\n        return this.visibility == VISIBLE\r\n                && visibleResetFlag == flags.VISIBLE_RESET_FLAG;\r\n    }\r\n\r\n    public boolean isTimeOut() {\r\n        return mTimer == null || isTimeOut(mTimer.currMillisecond);\r\n    }\r\n\r\n    public boolean isTimeOut(long ctime) {\r\n        return ctime - getActualTime() >= duration.value;\r\n    }\r\n\r\n    public boolean isOutside() {\r\n        return mTimer == null || isOutside(mTimer.currMillisecond);\r\n    }\r\n\r\n    public boolean isOutside(long ctime) {\r\n        long dtime = ctime - getActualTime();\r\n        return dtime <= 0 || dtime >= duration.value;\r\n    }\r\n\r\n    public boolean isLate() {\r\n        return mTimer == null || mTimer.currMillisecond < getActualTime();\r\n    }\r\n\r\n    public boolean hasPassedFilter() {\r\n        if (filterResetFlag != flags.FILTER_RESET_FLAG) {\r\n            mFilterParam = 0;\r\n            return false;\r\n        }\r\n        return true;\r\n    }\r\n\r\n    public boolean isFiltered() {\r\n        return filterResetFlag == flags.FILTER_RESET_FLAG && mFilterParam != 0;\r\n    }\r\n\r\n    public boolean isFilteredBy(int flag) {\r\n        return filterResetFlag == flags.FILTER_RESET_FLAG && (mFilterParam & flag) == flag;\r\n    }\r\n\r\n    public void setVisibility(boolean b) {\r\n        if (b) {\r\n            this.visibleResetFlag = flags.VISIBLE_RESET_FLAG;\r\n            this.visibility = VISIBLE;\r\n        } else\r\n            this.visibility = INVISIBLE;\r\n    }\r\n\r\n    public abstract void layout(IDisplayer displayer, float x, float y);\r\n\r\n    public abstract float[] getRectAtTime(IDisplayer displayer, long currTime);\r\n\r\n    public abstract float getLeft();\r\n\r\n    public abstract float getTop();\r\n\r\n    public abstract float getRight();\r\n\r\n    public abstract float getBottom();\r\n\r\n    /**\r\n     * return the type of Danmaku\r\n     *\r\n     * @return TYPE_SCROLL_RL = 0 TYPE_SCROLL_RL = 1 TYPE_SCROLL_LR = 2\r\n     * TYPE_FIX_TOP = 3; TYPE_FIX_BOTTOM = 4;\r\n     */\r\n    public abstract int getType();\r\n\r\n    public DanmakuTimer getTimer() {\r\n        return mTimer;\r\n    }\r\n\r\n    public void setTimer(DanmakuTimer timer) {\r\n        mTimer = timer;\r\n    }\r\n\r\n    public int getAlpha() {\r\n        return alpha;\r\n    }\r\n\r\n    public void setTag(Object tag) {\r\n        this.tag = tag;\r\n    }\r\n\r\n    public void setTag(int key, Object tag) {\r\n        this.mTags.put(key, tag);\r\n    }\r\n\r\n    public Object getTag(int key) {\r\n        if (mTags == null) {\r\n            return null;\r\n        }\r\n        return mTags.get(key);\r\n    }\r\n\r\n    public void setTimeOffset(long timeOffset) {\r\n        this.timeOffset = timeOffset;\r\n        this.syncTimeOffsetResetFlag = flags.SYNC_TIME_OFFSET_RESET_FLAG;\r\n    }\r\n\r\n    public void setTime(long time) {\r\n        this.time = time;\r\n        this.timeOffset = 0;\r\n    }\r\n\r\n    public long getTime() {\r\n        return time;\r\n    }\r\n\r\n    public long getActualTime() {\r\n        if (flags == null || flags.SYNC_TIME_OFFSET_RESET_FLAG != this.syncTimeOffsetResetFlag) {\r\n            this.timeOffset = 0;\r\n            return time;\r\n        }\r\n        return time + timeOffset;\r\n    }\r\n\r\n    public boolean isOffset() {\r\n        if (flags == null || flags.SYNC_TIME_OFFSET_RESET_FLAG != this.syncTimeOffsetResetFlag) {\r\n            this.timeOffset = 0;\r\n            return false;\r\n        }\r\n        return timeOffset != 0;\r\n    }\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/Danmaku.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.model;\r\n\r\nimport master.flame.danmaku.danmaku.util.DanmakuUtils;\r\n\r\npublic class Danmaku extends BaseDanmaku {\r\n\r\n    public Danmaku(CharSequence text) {\r\n        DanmakuUtils.fillText(this, text);\r\n    }\r\n\r\n    @Override\r\n    public boolean isShown() {\r\n        return false;\r\n    }\r\n\r\n    @Override\r\n    public void layout(IDisplayer displayer, float x, float y) {\r\n\r\n    }\r\n\r\n    @Override\r\n    public float[] getRectAtTime(IDisplayer displayer, long time) {\r\n        return null;\r\n    }\r\n\r\n    @Override\r\n    public float getLeft() {\r\n        return 0;\r\n    }\r\n\r\n    @Override\r\n    public float getTop() {\r\n        return 0;\r\n    }\r\n\r\n    @Override\r\n    public float getRight() {\r\n        return 0;\r\n    }\r\n\r\n    @Override\r\n    public float getBottom() {\r\n        return 0;\r\n    }\r\n\r\n    @Override\r\n    public int getType() {\r\n        return 0;\r\n    }\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/DanmakuTimer.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.model;\r\n\r\npublic class DanmakuTimer {\r\n    public long currMillisecond;\r\n\r\n    private long lastInterval;\r\n\r\n    public DanmakuTimer() {\r\n\r\n    }\r\n\r\n    public DanmakuTimer(long curr) {\r\n        update(curr);\r\n    }\r\n\r\n    public long update(long curr) {\r\n        lastInterval = curr - currMillisecond;\r\n        currMillisecond = curr;\r\n        return lastInterval;\r\n    }\r\n\r\n    public long add(long mills) {\r\n        return update(currMillisecond + mills);\r\n    }\r\n\r\n    public long lastInterval() {\r\n        return lastInterval;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/Duration.java",
    "content": "\npackage master.flame.danmaku.danmaku.model;\n\npublic class Duration implements Cloneable {\n\n    private long mInitialDuration;\n\n    private float factor = 1.0f;\n\n    public long value;\n\n    public Duration(long initialDuration) {\n        mInitialDuration = initialDuration;\n        value = initialDuration;\n    }\n    \n    public void setValue(long initialDuration) {\n        mInitialDuration = initialDuration;\n        value = (long) (mInitialDuration * factor);\n    }\n\n    public void setFactor(float f) {\n        if (factor != f) {\n            factor = f;\n            value = (long) (mInitialDuration * f);\n        }\n    }\n\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/FBDanmaku.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.model;\r\n\r\npublic class FBDanmaku extends FTDanmaku {\r\n\r\n    public FBDanmaku(Duration duration) {\r\n        super(duration);\r\n    }\r\n\r\n    @Override\r\n    public int getType() {\r\n        return TYPE_FIX_BOTTOM;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/FTDanmaku.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.model;\r\n\r\n/**\r\n * 顶部固定弹幕\r\n */\r\npublic class FTDanmaku extends BaseDanmaku {\r\n\r\n    private float x = 0;\r\n\r\n    protected float y = -1;\r\n\r\n    private float[] RECT = null;\r\n\r\n    private float mLastLeft;\r\n\r\n    private float mLastPaintWidth;\r\n\r\n    private int mLastDispWidth;\r\n\r\n    public FTDanmaku(Duration duration) {\r\n        this.duration = duration;\r\n    }\r\n\r\n    @Override\r\n    public void layout(IDisplayer displayer, float x, float y) {\r\n        if (mTimer != null) {\r\n            long deltaDuration = mTimer.currMillisecond - getActualTime();\r\n            if (deltaDuration > 0 && deltaDuration < duration.value) {\r\n                if (!this.isShown()) {\r\n                    this.x = getLeft(displayer);\r\n                    this.y = y;\r\n                    this.setVisibility(true);\r\n                }\r\n                return;\r\n            }\r\n\r\n            this.setVisibility(false);\r\n            this.y = -1;\r\n            this.x = displayer.getWidth();\r\n        }\r\n\r\n    }\r\n\r\n    protected float getLeft(IDisplayer displayer) {\r\n        if (mLastDispWidth == displayer.getWidth() && mLastPaintWidth == paintWidth) {\r\n            return mLastLeft;\r\n        }\r\n        float left = (displayer.getWidth() - paintWidth) / 2;\r\n        mLastDispWidth = displayer.getWidth();\r\n        mLastPaintWidth = paintWidth;\r\n        mLastLeft = left;\r\n        return left;\r\n    }\r\n\r\n    @Override\r\n    public float[] getRectAtTime(IDisplayer displayer, long time) {\r\n        if (!isMeasured())\r\n            return null;\r\n        float left = getLeft(displayer);\r\n        if (RECT == null) {\r\n            RECT = new float[4];\r\n        }\r\n        RECT[0] = left;\r\n        RECT[1] = y;\r\n        RECT[2] = left + paintWidth;\r\n        RECT[3] = y + paintHeight;\r\n        return RECT;\r\n    }\r\n\r\n    @Override\r\n    public float getLeft() {\r\n        return x;\r\n    }\r\n\r\n    @Override\r\n    public float getTop() {\r\n        return y;\r\n    }\r\n\r\n    @Override\r\n    public float getRight() {\r\n        return x + paintWidth;\r\n    }\r\n\r\n    @Override\r\n    public float getBottom() {\r\n        return y + paintHeight;\r\n    }\r\n\r\n    @Override\r\n    public int getType() {\r\n        return TYPE_FIX_TOP;\r\n    }\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/GlobalFlagValues.java",
    "content": "package master.flame.danmaku.danmaku.model;\n\npublic class GlobalFlagValues {\n\n    public int MEASURE_RESET_FLAG = 0;\n    public int VISIBLE_RESET_FLAG = 0;\n    public int FILTER_RESET_FLAG = 0;\n    public int FIRST_SHOWN_RESET_FLAG = 0;\n    public int SYNC_TIME_OFFSET_RESET_FLAG = 0;\n    public int PREPARE_RESET_FLAG = 0;\n\n    public void resetAll() {\n        VISIBLE_RESET_FLAG = 0;\n        MEASURE_RESET_FLAG = 0;\n        FILTER_RESET_FLAG = 0;\n        FIRST_SHOWN_RESET_FLAG = 0;\n        SYNC_TIME_OFFSET_RESET_FLAG = 0;\n        PREPARE_RESET_FLAG = 0;\n    }\n\n    public void updateAll() {\n        VISIBLE_RESET_FLAG++;\n        MEASURE_RESET_FLAG++;\n        FILTER_RESET_FLAG++;\n        FIRST_SHOWN_RESET_FLAG++;\n        SYNC_TIME_OFFSET_RESET_FLAG++;\n        PREPARE_RESET_FLAG++;\n    }\n\n    public void updateVisibleFlag() {\n        VISIBLE_RESET_FLAG++;\n    }\n\n    public void updateMeasureFlag() {\n        MEASURE_RESET_FLAG++;\n    }\n\n    public void updateFilterFlag() {\n        FILTER_RESET_FLAG++;\n    }\n\n    public void updateFirstShownFlag() {\n        FIRST_SHOWN_RESET_FLAG++;\n    }\n\n    public void updateSyncOffsetTimeFlag() {\n        SYNC_TIME_OFFSET_RESET_FLAG++;\n    }\n\n    public void updatePrepareFlag() {\n        PREPARE_RESET_FLAG++;\n    }\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/ICacheManager.java",
    "content": "/*\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage master.flame.danmaku.danmaku.model;\n\n/**\n * Created by ch on 15-11-15.\n */\npublic interface ICacheManager {\n    void addDanmaku(BaseDanmaku danmaku);\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/IDanmakuIterator.java",
    "content": "package master.flame.danmaku.danmaku.model;\n\npublic interface IDanmakuIterator {\n\n    public BaseDanmaku next();\n    \n    public boolean hasNext();\n    \n    public void reset();\n\n    public void remove();\n    \n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/IDanmakus.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.model;\r\n\r\nimport java.util.Collection;\r\nimport java.util.Comparator;\r\n\r\nimport master.flame.danmaku.danmaku.util.DanmakuUtils;\r\n\r\npublic interface IDanmakus {\r\n\r\n    abstract class Consumer<Progress, Result> {\r\n\r\n        public static final int ACTION_CONTINUE = 0;\r\n        public static final int ACTION_BREAK = 1;\r\n        public static final int ACTION_REMOVE = 2;\r\n        public static final int ACTION_REMOVE_AND_BREAK = 3;\r\n\r\n        /**\r\n         * Performs this operation on the given argument.\r\n         *\r\n         * @param t the input argument\r\n         * @return next action of the loop\r\n         *\r\n         * @see #ACTION_CONTINUE\r\n         * @see #ACTION_BREAK\r\n         * @see #ACTION_REMOVE\r\n         */\r\n        public abstract int accept(Progress t);\r\n\r\n        public void before() {\r\n\r\n        }\r\n\r\n        public void after() {\r\n\r\n        }\r\n\r\n        public Result result() {\r\n            return null;\r\n        }\r\n    }\r\n\r\n    abstract class DefaultConsumer<Progress> extends Consumer<Progress, Void> {\r\n\r\n    }\r\n\r\n    int ST_BY_TIME = 0;\r\n\r\n    int ST_BY_YPOS = 1;\r\n\r\n    int ST_BY_YPOS_DESC = 2;\r\n\r\n    /**\r\n     * this type is used to iterate/remove/insert elements, not support sub/subnew\r\n     */\r\n    int ST_BY_LIST = 4;\r\n\r\n\r\n    boolean addItem(BaseDanmaku item);\r\n\r\n    boolean removeItem(BaseDanmaku item);\r\n    \r\n    IDanmakus subnew(long startTime, long endTime);\r\n\r\n    IDanmakus sub(long startTime, long endTime);\r\n\r\n    int size();\r\n\r\n    void clear();\r\n    \r\n    BaseDanmaku first();\r\n    \r\n    BaseDanmaku last();\r\n    \r\n    boolean contains(BaseDanmaku item);\r\n\r\n    boolean isEmpty();\r\n    \r\n    void setSubItemsDuplicateMergingEnabled(boolean enable);\r\n\r\n    Collection<BaseDanmaku> getCollection();\r\n\r\n    void forEachSync(Consumer<? super BaseDanmaku, ?> consumer);\r\n\r\n    void forEach(Consumer<? super BaseDanmaku, ?> consumer);\r\n\r\n    Object obtainSynchronizer();\r\n\r\n    class BaseComparator implements Comparator<BaseDanmaku> {\r\n\r\n        protected boolean mDuplicateMergingEnable;\r\n\r\n        public BaseComparator(boolean duplicateMergingEnabled) {\r\n            setDuplicateMergingEnabled(duplicateMergingEnabled);\r\n        }\r\n\r\n        public void setDuplicateMergingEnabled(boolean enable) {\r\n            mDuplicateMergingEnable = enable;\r\n        }\r\n\r\n        @Override\r\n        public int compare(BaseDanmaku obj1, BaseDanmaku obj2) {\r\n            if (mDuplicateMergingEnable && DanmakuUtils.isDuplicate(obj1, obj2)) {\r\n                return 0;\r\n            }\r\n            return DanmakuUtils.compare(obj1, obj2);\r\n        }\r\n\r\n    }\r\n\r\n    class TimeComparator extends BaseComparator {\r\n\r\n        public TimeComparator(boolean duplicateMergingEnabled) {\r\n            super(duplicateMergingEnabled);\r\n        }\r\n\r\n        @Override\r\n        public int compare(BaseDanmaku obj1, BaseDanmaku obj2) {\r\n            return super.compare(obj1, obj2);\r\n        }\r\n    }\r\n\r\n    class YPosComparator extends BaseComparator {\r\n\r\n        public YPosComparator(boolean duplicateMergingEnabled) {\r\n            super(duplicateMergingEnabled);\r\n        }\r\n\r\n        @Override\r\n        public int compare(BaseDanmaku obj1, BaseDanmaku obj2) {\r\n            if (mDuplicateMergingEnable && DanmakuUtils.isDuplicate(obj1, obj2)) {\r\n                return 0;\r\n            }\r\n            return Float.compare(obj1.getTop(), obj2.getTop());\r\n        }\r\n    }\r\n\r\n    class YPosDescComparator extends BaseComparator {\r\n\r\n        public YPosDescComparator(boolean duplicateMergingEnabled) {\r\n            super(duplicateMergingEnabled);\r\n        }\r\n\r\n        @Override\r\n        public int compare(BaseDanmaku obj1, BaseDanmaku obj2) {\r\n            if (mDuplicateMergingEnable && DanmakuUtils.isDuplicate(obj1, obj2)) {\r\n                return 0;\r\n            }\r\n            return Float.compare(obj2.getTop(), obj1.getTop());\r\n        }\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/IDisplayer.java",
    "content": "/*\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage master.flame.danmaku.danmaku.model;\n\n\npublic interface IDisplayer {\n\n    int DANMAKU_STYLE_DEFAULT = -1; // 自动\n    int DANMAKU_STYLE_NONE = 0; // 无\n    int DANMAKU_STYLE_SHADOW = 1; // 阴影\n    int DANMAKU_STYLE_STROKEN = 2; // 描边\n    int DANMAKU_STYLE_PROJECTION = 3; // 投影\n\n    int getWidth();\n\n    int getHeight();\n\n    float getDensity();\n\n    int getDensityDpi();\n\n    int draw(BaseDanmaku danmaku);\n\n    void recycle(BaseDanmaku danmaku);\n\n    float getScaledDensity();\n\n    int getSlopPixel();\n\n    void prepare(BaseDanmaku danmaku, boolean fromWorkerThread);\n\n    void measure(BaseDanmaku danmaku, boolean fromWorkerThread);\n\n    float getStrokeWidth();\n\n    void setHardwareAccelerated(boolean enable);\n\n    boolean isHardwareAccelerated();\n\n    int getMaximumCacheWidth();\n\n    int getMaximumCacheHeight();\n\n\n    ////////////////// setter ///////////////////////////\n\n    void resetSlopPixel(float factor);\n\n    void setDensities(float density, int densityDpi, float scaledDensity);\n\n    void setSize(int width, int height);\n\n    void setDanmakuStyle(int style, float[] data);\n\n    void setMargin(int m);\n\n    int getMargin();\n\n    void setAllMarginTop(int m);\n\n    int getAllMarginTop();\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/IDrawingCache.java",
    "content": "\r\npackage master.flame.danmaku.danmaku.model;\r\n\r\npublic interface IDrawingCache<T> {\r\n\r\n    public void build(int w, int h, int density, boolean checkSizeEquals, int bitsPerPixel);\r\n    \r\n    public void erase();\r\n\r\n    public T get();\r\n\r\n    public void destroy();\r\n\r\n    public int size();\r\n    \r\n    public int width();\r\n    \r\n    public int height();\r\n    \r\n    public boolean hasReferences();\r\n    \r\n    public void increaseReference();\r\n    \r\n    public void decreaseReference();\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/L2RDanmaku.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.model;\r\n\r\n\r\npublic class L2RDanmaku extends R2LDanmaku {\r\n\r\n    public L2RDanmaku(Duration duration) {\r\n        super(duration); \r\n    }\r\n    \r\n    @Override\r\n    public void layout(IDisplayer displayer, float x, float y) {\r\n        if (mTimer != null) {\r\n            long currMS = mTimer.currMillisecond;\r\n            long deltaDuration = currMS - getActualTime();\r\n            if (deltaDuration > 0 && deltaDuration < duration.value) {\r\n                this.x = getAccurateLeft(displayer, currMS);\r\n                if (!this.isShown()) {\r\n                    this.y = y;\r\n                    this.setVisibility(true);\r\n                }\r\n                mLastTime = currMS;\r\n                return;\r\n            }\r\n            mLastTime = currMS;\r\n        }\r\n        this.setVisibility(false);\r\n    }\r\n    \r\n    @Override\r\n    public float[] getRectAtTime(IDisplayer displayer, long time) {\r\n        if (!isMeasured())\r\n            return null;\r\n        float left = getAccurateLeft(displayer, time);\r\n        if (RECT == null) {\r\n            RECT = new float[4];\r\n        }\r\n        RECT[0] = left;\r\n        RECT[1] = y;\r\n        RECT[2] = left + paintWidth;\r\n        RECT[3] = y + paintHeight;\r\n        return RECT;\r\n    }\r\n\r\n    protected float getAccurateLeft(IDisplayer displayer, long currTime) {\r\n        long elapsedTime = currTime - getActualTime();\r\n        if (elapsedTime >= duration.value) {\r\n            return displayer.getWidth();\r\n        }\r\n        return mStepX * elapsedTime - paintWidth;\r\n    }\r\n\r\n    @Override\r\n    public float getLeft() {\r\n        return x;\r\n    }\r\n\r\n    @Override\r\n    public float getTop() {\r\n        return y;\r\n    }\r\n\r\n    @Override\r\n    public float getRight() {\r\n        return x + paintWidth;\r\n    }\r\n\r\n    @Override\r\n    public float getBottom() {\r\n        return y + paintHeight;\r\n    }\r\n\r\n    @Override\r\n    public int getType() {\r\n        return TYPE_SCROLL_LR;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/R2LDanmaku.java",
    "content": "/*\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage master.flame.danmaku.danmaku.model;\n\n\n\npublic class R2LDanmaku extends BaseDanmaku {\n    \n    protected static final long MAX_RENDERING_TIME = 100;\n    \n    protected static final long CORDON_RENDERING_TIME = 40;\n\n    protected float x = 0;\n\n    protected float y = -1;\n\n    protected int mDistance;\n\n    protected float[] RECT = null;\n\n    protected float mStepX;\n\n    protected long mLastTime;\n\n    public R2LDanmaku(Duration duration) {\n        this.duration = duration;\n    }\n\n    @Override\n    public void layout(IDisplayer displayer, float x, float y) {\n        if (mTimer != null) {\n            long currMS = mTimer.currMillisecond;\n            long deltaDuration = currMS - getActualTime();\n            if (deltaDuration > 0 && deltaDuration < duration.value) {\n                this.x = getAccurateLeft(displayer, currMS);\n                if (!this.isShown()) {\n                    this.y = y;\n                    this.setVisibility(true);\n                }\n                mLastTime = currMS;\n                return;\n            }\n            mLastTime = currMS;\n        }\n        this.setVisibility(false);\n    }\n\n    protected float getAccurateLeft(IDisplayer displayer, long currTime) {\n        long elapsedTime = currTime - getActualTime();\n        if (elapsedTime >= duration.value) {\n            return -paintWidth;\n        }\n\n        return displayer.getWidth() - elapsedTime * mStepX;\n    }\n\n    @Override\n    public float[] getRectAtTime(IDisplayer displayer, long time) {\n        if (!isMeasured())\n            return null;\n        float left = getAccurateLeft(displayer, time);\n        if (RECT == null) {\n            RECT = new float[4];\n        }\n        RECT[0] = left;\n        RECT[1] = y;\n        RECT[2] = left + paintWidth;\n        RECT[3] = y + paintHeight;\n        return RECT;\n    }\n\n    @Override\n    public float getLeft() {\n        return x;\n    }\n\n    @Override\n    public float getTop() {\n        return y;\n    }\n\n    @Override\n    public float getRight() {\n        return x + paintWidth;\n    }\n\n    @Override\n    public float getBottom() {\n        return y + paintHeight;\n    }\n\n    @Override\n    public int getType() {\n        return TYPE_SCROLL_RL;\n    }\n    \n    @Override\n    public void measure(IDisplayer displayer, boolean fromWorkerThread) {\n        super.measure(displayer, fromWorkerThread);\n        mDistance = (int) (displayer.getWidth() + paintWidth);\n        mStepX = mDistance / (float) duration.value;\n    }\n\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/SpecialDanmaku.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.model;\r\n\r\npublic class SpecialDanmaku extends BaseDanmaku {\r\n\r\n    public static class ScaleFactor {\r\n        int flag = 0;\r\n        float scaleX;\r\n        float scaleY;\r\n        int width;\r\n        int height;\r\n\r\n        public ScaleFactor(int width, int height, float scaleX, float scaleY) {\r\n            update(width, height, scaleX, scaleY);\r\n        }\r\n\r\n        public void update(int width, int height, float scaleX, float scaleY) {\r\n            if (Float.compare(this.scaleX, scaleX) != 0 || Float.compare(this.scaleY, scaleY) != 0) {\r\n                flag++;\r\n            }\r\n            this.width = width;\r\n            this.height = height;\r\n            this.scaleX = scaleX;\r\n            this.scaleY = scaleY;\r\n        }\r\n\r\n        public boolean isUpdated(int flag, int width, int height) {\r\n            return this.flag != flag && (this.width != width || this.height != height);\r\n        }\r\n    }\r\n\r\n    private class Point {\r\n        float x, y;\r\n\r\n        public Point(float x, float y) {\r\n            this.x = x;\r\n            this.y = y;\r\n        }\r\n\r\n        public float getDistance(Point p) {\r\n            float _x = Math.abs(this.x - p.x);\r\n            float _y = Math.abs(this.y - p.y);\r\n            return (float) Math.sqrt(_x * _x + _y * _y);\r\n        }\r\n    }\r\n\r\n    public class LinePath {\r\n        Point pBegin, pEnd;\r\n        public long duration, beginTime, endTime;\r\n        float delatX, deltaY;\r\n\r\n        public void setPoints(Point pBegin, Point pEnd) {\r\n            this.pBegin = pBegin;\r\n            this.pEnd = pEnd;\r\n            this.delatX = pEnd.x - pBegin.x;\r\n            this.deltaY = pEnd.y - pBegin.y;\r\n        }\r\n\r\n        public float getDistance() {\r\n            return pEnd.getDistance(pBegin);\r\n        }\r\n\r\n        public float[] getBeginPoint() {\r\n            return new float[]{\r\n                    pBegin.x, pBegin.y\r\n            };\r\n        }\r\n\r\n        public float[] getEndPoint() {\r\n            return new float[]{\r\n                    pEnd.x, pEnd.y\r\n            };\r\n        }\r\n\r\n    }\r\n\r\n    public float beginX, beginY;\r\n\r\n    public float endX, endY;\r\n\r\n    public float deltaX, deltaY;\r\n\r\n    public long translationDuration;\r\n\r\n    public long translationStartDelay;\r\n\r\n    private ScaleFactor mScaleFactor;\r\n\r\n    private int mScaleFactorChangedFlag;\r\n\r\n    private int mCurrentWidth = 0;\r\n\r\n    private int mCurrentHeight = 0;\r\n\r\n    /**\r\n     * Linear.easeIn or Quadratic.easeOut\r\n     */\r\n    public boolean isQuadraticEaseOut = false;\r\n\r\n    public int beginAlpha;\r\n\r\n    public int endAlpha;\r\n\r\n    public int deltaAlpha;\r\n\r\n    public long alphaDuration;\r\n\r\n    public float rotateX, rotateZ;\r\n\r\n    public float pivotX, pivotY;\r\n\r\n    private float[] currStateValues = new float[4];\r\n\r\n    public LinePath[] linePaths;\r\n\r\n    @Override\r\n    public void measure(IDisplayer displayer, boolean fromWorkerThread) {\r\n        super.measure(displayer, fromWorkerThread);\r\n        if (mCurrentWidth == 0 || mCurrentHeight == 0) {\r\n            mCurrentWidth = displayer.getWidth();\r\n            mCurrentHeight = displayer.getHeight();\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void layout(IDisplayer displayer, float x, float y) {\r\n        getRectAtTime(displayer, mTimer.currMillisecond);\r\n    }\r\n\r\n    @Override\r\n    public float[] getRectAtTime(IDisplayer displayer, long currTime) {\r\n\r\n        if (!isMeasured())\r\n            return null;\r\n\r\n        if (mScaleFactor.isUpdated(this.mScaleFactorChangedFlag, mCurrentWidth, mCurrentHeight)) {\r\n            float scaleX = mScaleFactor.scaleX;\r\n            float scaleY = mScaleFactor.scaleY;\r\n            setTranslationData(beginX * scaleX, beginY * scaleY, endX * scaleX, endY * scaleY, translationDuration, translationStartDelay);\r\n            if (linePaths != null && linePaths.length > 0) {\r\n                int length = linePaths.length;\r\n                float[][] points = new float[length + 1][2];\r\n                for (int j = 0; j < length; j++) {\r\n                    points[j] = linePaths[j].getBeginPoint();\r\n                    points[j + 1] = linePaths[j].getEndPoint();\r\n                }\r\n                for (int i = 0; i < points.length; i++) {\r\n                    points[i][0] *= scaleX;\r\n                    points[i][1] *= scaleY;\r\n                }\r\n                setLinePathData(points);\r\n            }\r\n            this.mScaleFactorChangedFlag = mScaleFactor.flag;\r\n            this.mCurrentWidth = mScaleFactor.width;\r\n            this.mCurrentHeight = mScaleFactor.height;\r\n        }\r\n\r\n        long deltaTime = currTime - getActualTime();\r\n\r\n        // caculate alpha\r\n        if (alphaDuration > 0 && deltaAlpha != 0) {\r\n            if (deltaTime >= alphaDuration) {\r\n                alpha = endAlpha;\r\n            } else {\r\n                float alphaProgress = deltaTime / (float) alphaDuration;\r\n                int vectorAlpha = (int) (deltaAlpha * alphaProgress);\r\n                alpha = beginAlpha + vectorAlpha;\r\n            }\r\n        }\r\n\r\n        // caculate x y\r\n        float currX = beginX;\r\n        float currY = beginY;\r\n        long dtime = deltaTime - translationStartDelay;\r\n        if (translationDuration > 0 && dtime >= 0 && dtime <= translationDuration) {\r\n            float tranalationProgress = 0f;\r\n            if (linePaths != null) {\r\n                LinePath currentLinePath = null;\r\n                for (LinePath line : linePaths) {\r\n                    if (dtime >= line.beginTime && dtime < line.endTime) {\r\n                        currentLinePath = line;\r\n                        break;\r\n                    } else {\r\n                        currX = line.pEnd.x;\r\n                        currY = line.pEnd.y;\r\n                    }\r\n                }\r\n                if (currentLinePath != null) {\r\n                    float deltaX = currentLinePath.delatX;\r\n                    float deltaY = currentLinePath.deltaY;\r\n                    tranalationProgress = (deltaTime - currentLinePath.beginTime) / (float) currentLinePath.duration;\r\n                    float beginX = currentLinePath.pBegin.x;\r\n                    float beginY = currentLinePath.pBegin.y;\r\n                    if (deltaX != 0) {\r\n                        float vectorX = deltaX * tranalationProgress;\r\n                        currX = beginX + vectorX;\r\n                    }\r\n                    if (deltaY != 0) {\r\n                        float vectorY = deltaY * tranalationProgress;\r\n                        currY = beginY + vectorY;\r\n                    }\r\n                }\r\n            } else {\r\n                tranalationProgress = isQuadraticEaseOut ? getQuadEaseOutProgress(dtime, translationDuration) : dtime / (float) translationDuration;\r\n                if (deltaX != 0) {\r\n                    float vectorX = deltaX * tranalationProgress;\r\n                    currX = beginX + vectorX;\r\n                }\r\n                if (deltaY != 0) {\r\n                    float vectorY = deltaY * tranalationProgress;\r\n                    currY = beginY + vectorY;\r\n                }\r\n            }\r\n        } else if (dtime > translationDuration) {\r\n            currX = endX;\r\n            currY = endY;\r\n        }\r\n\r\n        currStateValues[0] = currX;\r\n        currStateValues[1] = currY;\r\n        currStateValues[2] = currX + paintWidth;\r\n        currStateValues[3] = currY + paintHeight;\r\n\r\n        this.setVisibility(!isOutside());\r\n\r\n        return currStateValues;\r\n    }\r\n\r\n    private final static float getQuadEaseOutProgress(long ctime, long duration) {\r\n//            Math.easeOutQuad = function (t, b, c, d) {\r\n//                t /= d;\r\n//                return -c * t*(t-2) + b;\r\n//            };\r\n        float t = ctime;\r\n//        float b = 0f;\r\n        float c = 1.0f;\r\n        float d = duration;\r\n        return -c * (t /= d) * (t - 2); // + b;\r\n    }\r\n\r\n    @Override\r\n    public float getLeft() {\r\n        return currStateValues[0];\r\n    }\r\n\r\n    @Override\r\n    public float getTop() {\r\n        return currStateValues[1];\r\n    }\r\n\r\n    @Override\r\n    public float getRight() {\r\n        return currStateValues[2];\r\n    }\r\n\r\n    @Override\r\n    public float getBottom() {\r\n        return currStateValues[3];\r\n    }\r\n\r\n    @Override\r\n    public int getType() {\r\n        return TYPE_SPECIAL;\r\n    }\r\n\r\n    public void setTranslationData(float beginX, float beginY, float endX, float endY,\r\n                                   long translationDuration, long translationStartDelay) {\r\n        this.beginX = beginX;\r\n        this.beginY = beginY;\r\n        this.endX = endX;\r\n        this.endY = endY;\r\n        this.deltaX = endX - beginX;\r\n        this.deltaY = endY - beginY;\r\n        this.translationDuration = translationDuration;\r\n        this.translationStartDelay = translationStartDelay;\r\n    }\r\n\r\n    public void setAlphaData(int beginAlpha, int endAlpha, long alphaDuration) {\r\n        this.beginAlpha = beginAlpha;\r\n        this.endAlpha = endAlpha;\r\n        this.deltaAlpha = endAlpha - beginAlpha;\r\n        this.alphaDuration = alphaDuration;\r\n        if (beginAlpha != AlphaValue.MAX) {\r\n            alpha = beginAlpha;\r\n        }\r\n    }\r\n\r\n    public void setLinePathData(float[][] points) {\r\n        if (points != null) {\r\n            int length = points.length;\r\n            this.beginX = points[0][0];\r\n            this.beginY = points[0][1];\r\n            this.endX = points[length - 1][0];\r\n            this.endY = points[length - 1][1];\r\n            if (points.length > 1) {\r\n                linePaths = new LinePath[points.length - 1];\r\n                for (int i = 0; i < linePaths.length; i++) {\r\n                    linePaths[i] = new LinePath();\r\n                    linePaths[i].setPoints(new Point(points[i][0], points[i][1]), new Point(\r\n                            points[i + 1][0], points[i + 1][1]));\r\n                }\r\n                float totalDistance = 0;\r\n                for (LinePath line : linePaths) {\r\n                    totalDistance += line.getDistance();\r\n                }\r\n                LinePath lastLine = null;\r\n                for (LinePath line : linePaths) {\r\n                    line.duration = (long) ((line.getDistance() / totalDistance) * translationDuration);\r\n                    line.beginTime = (lastLine == null ? 0 : lastLine.endTime);\r\n                    line.endTime = line.beginTime + line.duration;\r\n                    lastLine = line;\r\n                }\r\n\r\n            }\r\n        }\r\n    }\r\n\r\n    public void setScaleFactor(ScaleFactor scaleFactor) {\r\n        this.mScaleFactor = scaleFactor;\r\n        this.mScaleFactorChangedFlag = scaleFactor.flag;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/android/AndroidDisplayer.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.model.android;\r\n\r\nimport android.annotation.SuppressLint;\r\nimport android.graphics.Camera;\r\nimport android.graphics.Canvas;\r\nimport android.graphics.Matrix;\r\nimport android.graphics.Paint;\r\nimport android.graphics.Paint.Style;\r\nimport android.graphics.Typeface;\r\nimport android.os.Build;\r\nimport android.text.TextPaint;\r\n\r\nimport java.util.HashMap;\r\nimport java.util.Map;\r\n\r\nimport master.flame.danmaku.danmaku.model.AbsDisplayer;\r\nimport master.flame.danmaku.danmaku.model.AlphaValue;\r\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\r\nimport master.flame.danmaku.danmaku.renderer.IRenderer;\r\n\r\npublic class AndroidDisplayer extends AbsDisplayer<Canvas, Typeface> {\r\n\r\n    public static class DisplayerConfig {\r\n        private float sLastScaleTextSize;\r\n        private final Map<Float, Float> sCachedScaleSize = new HashMap<>(10);\r\n\r\n        public final TextPaint PAINT, PAINT_DUPLICATE;\r\n\r\n        private Paint ALPHA_PAINT;\r\n\r\n        private Paint UNDERLINE_PAINT;\r\n\r\n        private Paint BORDER_PAINT;\r\n\r\n        /**\r\n         * 下划线高度\r\n         */\r\n        public int UNDERLINE_HEIGHT = 4;\r\n\r\n        /**\r\n         * 边框厚度\r\n         */\r\n        public static final int BORDER_WIDTH = 4;\r\n\r\n        /**\r\n         * 阴影半径\r\n         */\r\n        private float SHADOW_RADIUS = 4.0f;\r\n\r\n        /**\r\n         * 描边宽度\r\n         */\r\n        private float STROKE_WIDTH = 3.5f;\r\n\r\n        /**\r\n         * 投影参数\r\n         */\r\n        public float sProjectionOffsetX = 1.0f;\r\n        public float sProjectionOffsetY = 1.0f;\r\n        private int sProjectionAlpha = 0xCC;\r\n\r\n        /**\r\n         * 开启阴影，可动态改变\r\n         */\r\n        public boolean CONFIG_HAS_SHADOW = false;\r\n        private boolean HAS_SHADOW = CONFIG_HAS_SHADOW;\r\n\r\n        /**\r\n         * 开启描边，可动态改变\r\n         */\r\n        public boolean CONFIG_HAS_STROKE = true;\r\n        private boolean HAS_STROKE = CONFIG_HAS_STROKE;\r\n\r\n        /**\r\n         * 开启投影，可动态改变\r\n         */\r\n        public boolean CONFIG_HAS_PROJECTION = false;\r\n        public boolean HAS_PROJECTION = CONFIG_HAS_PROJECTION;\r\n\r\n        /**\r\n         * 开启抗锯齿，可动态改变\r\n         */\r\n        public boolean CONFIG_ANTI_ALIAS = true;\r\n        private boolean ANTI_ALIAS = CONFIG_ANTI_ALIAS;\r\n        private boolean isTranslucent;\r\n        private int transparency = AlphaValue.MAX;\r\n        private float scaleTextSize = 1.0f;\r\n        private boolean isTextScaled = false;\r\n        private int margin = 0;\r\n        private int allMarginTop = 0;\r\n\r\n        public DisplayerConfig() {\r\n            PAINT = new TextPaint();\r\n            PAINT.setStrokeWidth(STROKE_WIDTH);\r\n            PAINT_DUPLICATE = new TextPaint(PAINT);\r\n            ALPHA_PAINT = new Paint();\r\n            UNDERLINE_PAINT = new Paint();\r\n            UNDERLINE_PAINT.setStrokeWidth(UNDERLINE_HEIGHT);\r\n            UNDERLINE_PAINT.setStyle(Style.STROKE);\r\n            BORDER_PAINT = new Paint();\r\n            BORDER_PAINT.setStyle(Style.STROKE);\r\n            BORDER_PAINT.setStrokeWidth(BORDER_WIDTH);\r\n        }\r\n\r\n        public void setTypeface(Typeface typeface) {\r\n            this.PAINT.setTypeface(typeface);\r\n        }\r\n\r\n        public void setShadowRadius(float shadowRadius) {\r\n            SHADOW_RADIUS = shadowRadius;\r\n        }\r\n\r\n        public void setStrokeWidth(float s) {\r\n            PAINT.setStrokeWidth(s);\r\n            STROKE_WIDTH = s;\r\n        }\r\n\r\n        public void setProjectionConfig(float offsetX, float offsetY, int alpha) {\r\n            if (sProjectionOffsetX != offsetX || sProjectionOffsetY != offsetY || sProjectionAlpha != alpha) {\r\n                sProjectionOffsetX = (offsetX > 1.0f) ? offsetX : 1.0f;\r\n                sProjectionOffsetY = (offsetY > 1.0f) ? offsetY : 1.0f;\r\n                sProjectionAlpha = (alpha < 0) ? 0 : ((alpha > 255) ? 255 : alpha);\r\n            }\r\n        }\r\n\r\n        public void setFakeBoldText(boolean fakeBoldText) {\r\n            PAINT.setFakeBoldText(fakeBoldText);\r\n        }\r\n\r\n        public void setTransparency(int newTransparency) {\r\n            isTranslucent = (newTransparency != AlphaValue.MAX);\r\n            transparency = newTransparency;\r\n        }\r\n\r\n        public void setScaleTextSizeFactor(float factor) {\r\n            isTextScaled = (factor != 1f);\r\n            scaleTextSize = factor;\r\n        }\r\n\r\n        private void applyTextScaleConfig(BaseDanmaku danmaku, Paint paint) {\r\n            if (!isTextScaled) {\r\n                return;\r\n            }\r\n            Float size = sCachedScaleSize.get(danmaku.textSize);\r\n            if (size == null || sLastScaleTextSize != scaleTextSize) {\r\n                sLastScaleTextSize = scaleTextSize;\r\n                size = danmaku.textSize * scaleTextSize;\r\n                sCachedScaleSize.put(danmaku.textSize, size);\r\n            }\r\n            paint.setTextSize(size);\r\n        }\r\n\r\n        public boolean hasStroke(BaseDanmaku danmaku) {\r\n            return (HAS_STROKE || HAS_PROJECTION) && STROKE_WIDTH > 0 && danmaku.textShadowColor != 0;\r\n        }\r\n\r\n        public Paint getBorderPaint(BaseDanmaku danmaku) {\r\n            BORDER_PAINT.setColor(danmaku.borderColor);\r\n            return BORDER_PAINT;\r\n        }\r\n\r\n        public Paint getUnderlinePaint(BaseDanmaku danmaku) {\r\n            UNDERLINE_PAINT.setColor(danmaku.underlineColor);\r\n            return UNDERLINE_PAINT;\r\n        }\r\n\r\n        public TextPaint getPaint(BaseDanmaku danmaku, boolean fromWorkerThread) {\r\n            TextPaint paint;\r\n            if (fromWorkerThread) {\r\n                paint = PAINT;\r\n            } else {\r\n                paint = PAINT_DUPLICATE;\r\n                paint.set(PAINT);\r\n            }\r\n            paint.setTextSize(danmaku.textSize);\r\n            applyTextScaleConfig(danmaku, paint);\r\n\r\n            //ignore the transparent textShadowColor\r\n            if (!HAS_SHADOW || SHADOW_RADIUS <= 0 || danmaku.textShadowColor == 0) {\r\n                paint.clearShadowLayer();\r\n            } else {\r\n                paint.setShadowLayer(SHADOW_RADIUS, 0, 0, danmaku.textShadowColor);\r\n            }\r\n            paint.setAntiAlias(ANTI_ALIAS);\r\n            return paint;\r\n        }\r\n\r\n        public void applyPaintConfig(BaseDanmaku danmaku, Paint paint, boolean stroke) {\r\n\r\n            if (isTranslucent) {\r\n                if (stroke) {\r\n                    paint.setStyle(HAS_PROJECTION ? Style.FILL : Style.FILL_AND_STROKE);\r\n                    paint.setColor(danmaku.textShadowColor & 0x00FFFFFF);\r\n                    int alpha = HAS_PROJECTION ? (int) (sProjectionAlpha * ((float) transparency / AlphaValue.MAX))\r\n                            : transparency;\r\n                    paint.setAlpha(alpha);\r\n                } else {\r\n                    paint.setStyle(Style.FILL);\r\n                    paint.setColor(danmaku.textColor & 0x00FFFFFF);\r\n                    paint.setAlpha(transparency);\r\n                }\r\n            } else {\r\n                if (stroke) {\r\n                    paint.setStyle(HAS_PROJECTION ? Style.FILL : Style.FILL_AND_STROKE);\r\n                    paint.setColor(danmaku.textShadowColor & 0x00FFFFFF);\r\n                    int alpha = HAS_PROJECTION ? sProjectionAlpha : AlphaValue.MAX;\r\n                    paint.setAlpha(alpha);\r\n                } else {\r\n                    paint.setStyle(Style.FILL);\r\n                    paint.setColor(danmaku.textColor & 0x00FFFFFF);\r\n                    paint.setAlpha(AlphaValue.MAX);\r\n                }\r\n            }\r\n\r\n            if (danmaku.getType() == BaseDanmaku.TYPE_SPECIAL) {\r\n                paint.setAlpha(danmaku.getAlpha());\r\n            }\r\n\r\n        }\r\n\r\n        public void clearTextHeightCache() {\r\n            sCachedScaleSize.clear();\r\n        }\r\n\r\n        public float getStrokeWidth() {\r\n            if (HAS_SHADOW && HAS_STROKE) {\r\n                return Math.max(SHADOW_RADIUS, STROKE_WIDTH);\r\n            }\r\n            if (HAS_SHADOW) {\r\n                return SHADOW_RADIUS;\r\n            }\r\n            if (HAS_STROKE) {\r\n                return STROKE_WIDTH;\r\n            }\r\n            return 0f;\r\n        }\r\n\r\n        public void definePaintParams(boolean fromWorkerThread) {\r\n            HAS_STROKE = CONFIG_HAS_STROKE;\r\n            HAS_SHADOW = CONFIG_HAS_SHADOW;\r\n            HAS_PROJECTION = CONFIG_HAS_PROJECTION;\r\n            ANTI_ALIAS = CONFIG_ANTI_ALIAS;\r\n        }\r\n    }\r\n\r\n    private Camera camera = new Camera();\r\n\r\n    private Matrix matrix = new Matrix();\r\n\r\n    private final DisplayerConfig mDisplayConfig = new DisplayerConfig();\r\n\r\n    private BaseCacheStuffer sStuffer = new SimpleTextCacheStuffer();\r\n\r\n    public AndroidDisplayer() {\r\n\r\n    }\r\n\r\n    @SuppressLint(\"NewApi\")\r\n    private static final int getMaximumBitmapWidth(Canvas c) {\r\n        if (Build.VERSION.SDK_INT >= 14) {\r\n            return c.getMaximumBitmapWidth();\r\n        } else {\r\n            return c.getWidth();\r\n        }\r\n    }\r\n\r\n    @SuppressLint(\"NewApi\")\r\n    private static final int getMaximumBitmapHeight(Canvas c) {\r\n        if (Build.VERSION.SDK_INT >= 14) {\r\n            return c.getMaximumBitmapHeight();\r\n        } else {\r\n            return c.getHeight();\r\n        }\r\n    }\r\n\r\n    public void setTypeFace(Typeface font) {\r\n        mDisplayConfig.setTypeface(font);\r\n    }\r\n\r\n    public void setShadowRadius(float s) {\r\n        mDisplayConfig.setShadowRadius(s);\r\n    }\r\n\r\n    public void setPaintStorkeWidth(float s) {\r\n        mDisplayConfig.setStrokeWidth(s);\r\n    }\r\n\r\n    public void setProjectionConfig(float offsetX, float offsetY, int alpha) {\r\n        mDisplayConfig.setProjectionConfig(offsetX, offsetY, alpha);\r\n    }\r\n\r\n    public void setFakeBoldText(boolean fakeBoldText) {\r\n        mDisplayConfig.setFakeBoldText(fakeBoldText);\r\n    }\r\n\r\n    @Override\r\n    public void setTransparency(int newTransparency) {\r\n        mDisplayConfig.setTransparency(newTransparency);\r\n    }\r\n\r\n    @Override\r\n    public void setScaleTextSizeFactor(float factor) {\r\n        mDisplayConfig.setScaleTextSizeFactor(factor);\r\n    }\r\n\r\n    @Override\r\n    public void setCacheStuffer(BaseCacheStuffer cacheStuffer) {\r\n        if (cacheStuffer != sStuffer) {\r\n            sStuffer = cacheStuffer;\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public BaseCacheStuffer getCacheStuffer() {\r\n        return sStuffer;\r\n    }\r\n\r\n    @Override\r\n    public void setMargin(int m) {\r\n        mDisplayConfig.margin = m;\r\n    }\r\n\r\n    @Override\r\n    public int getMargin() {\r\n        return mDisplayConfig.margin;\r\n    }\r\n\r\n    @Override\r\n    public void setAllMarginTop(int m) {\r\n        mDisplayConfig.allMarginTop = m;\r\n    }\r\n\r\n    @Override\r\n    public int getAllMarginTop() {\r\n        return mDisplayConfig.allMarginTop;\r\n    }\r\n\r\n    public Canvas canvas;\r\n\r\n    private int width;\r\n\r\n    private int height;\r\n\r\n    private float locationZ;\r\n\r\n    private float density = 1;\r\n\r\n    private int densityDpi = 160;\r\n\r\n    private float scaledDensity = 1;\r\n\r\n    private int mSlopPixel = 0;\r\n\r\n    private boolean mIsHardwareAccelerated = true;\r\n\r\n    private int mMaximumBitmapWidth = 2048;\r\n\r\n    private int mMaximumBitmapHeight = 2048;\r\n\r\n    private void update(Canvas c) {\r\n        canvas = c;\r\n        if (c != null) {\r\n            width = c.getWidth();\r\n            height = c.getHeight();\r\n            if (mIsHardwareAccelerated) {\r\n                mMaximumBitmapWidth = getMaximumBitmapWidth(c);\r\n                mMaximumBitmapHeight = getMaximumBitmapHeight(c);\r\n            }\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public int getWidth() {\r\n        return width;\r\n    }\r\n\r\n    @Override\r\n    public int getHeight() {\r\n        return height;\r\n    }\r\n\r\n    @Override\r\n    public float getDensity() {\r\n        return density;\r\n    }\r\n\r\n    @Override\r\n    public int getDensityDpi() {\r\n        return densityDpi;\r\n    }\r\n\r\n    @Override\r\n    public int draw(BaseDanmaku danmaku) {\r\n        float top = danmaku.getTop();\r\n        float left = danmaku.getLeft();\r\n        if (canvas != null) {\r\n\r\n            Paint alphaPaint = null;\r\n            boolean needRestore = false;\r\n            if (danmaku.getType() == BaseDanmaku.TYPE_SPECIAL) {\r\n                if (danmaku.getAlpha() == AlphaValue.TRANSPARENT) {\r\n                    return IRenderer.NOTHING_RENDERING;\r\n                }\r\n                if (danmaku.rotationZ != 0 || danmaku.rotationY != 0) {\r\n                    saveCanvas(danmaku, canvas, left, top);\r\n                    needRestore = true;\r\n                }\r\n\r\n                int alpha = danmaku.getAlpha();\r\n                if (alpha != AlphaValue.MAX) {\r\n                    alphaPaint = mDisplayConfig.ALPHA_PAINT;\r\n                    alphaPaint.setAlpha(danmaku.getAlpha());\r\n                }\r\n            }\r\n\r\n            // skip drawing when danmaku is transparent\r\n            if (alphaPaint != null && alphaPaint.getAlpha() == AlphaValue.TRANSPARENT) {\r\n                return IRenderer.NOTHING_RENDERING;\r\n            }\r\n\r\n            // drawing cache\r\n            boolean cacheDrawn = sStuffer.drawCache(danmaku, canvas, left, top, alphaPaint, mDisplayConfig.PAINT);\r\n            int result = IRenderer.CACHE_RENDERING;\r\n            if (!cacheDrawn) {\r\n                if (alphaPaint != null) {\r\n                    mDisplayConfig.PAINT.setAlpha(alphaPaint.getAlpha());\r\n                    mDisplayConfig.PAINT_DUPLICATE.setAlpha(alphaPaint.getAlpha());\r\n                } else {\r\n                    resetPaintAlpha(mDisplayConfig.PAINT);\r\n                }\r\n                drawDanmaku(danmaku, canvas, left, top, false);\r\n                result = IRenderer.TEXT_RENDERING;\r\n            }\r\n\r\n            if (needRestore) {\r\n                restoreCanvas(canvas);\r\n            }\r\n\r\n            return result;\r\n        }\r\n\r\n        return IRenderer.NOTHING_RENDERING;\r\n    }\r\n\r\n    @Override\r\n    public void recycle(BaseDanmaku danmaku) {\r\n        if (sStuffer != null) {\r\n            sStuffer.releaseResource(danmaku);\r\n        }\r\n    }\r\n\r\n    private void resetPaintAlpha(Paint paint) {\r\n        if (paint.getAlpha() != AlphaValue.MAX) {\r\n            paint.setAlpha(AlphaValue.MAX);\r\n        }\r\n    }\r\n\r\n    private void restoreCanvas(Canvas canvas) {\r\n        canvas.restore();\r\n    }\r\n\r\n    private int saveCanvas(BaseDanmaku danmaku, Canvas canvas, float left, float top) {\r\n        camera.save();\r\n        if (locationZ !=0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {\r\n            camera.setLocation(0, 0, locationZ);\r\n        }\r\n        camera.rotateY(-danmaku.rotationY);\r\n        camera.rotateZ(-danmaku.rotationZ);\r\n        camera.getMatrix(matrix);\r\n        matrix.preTranslate(-left, -top);\r\n        matrix.postTranslate(left , top);\r\n        camera.restore();\r\n        int count = canvas.save();\r\n        canvas.concat(matrix);\r\n        return count;\r\n    }\r\n\r\n    @Override\r\n    public synchronized void drawDanmaku(BaseDanmaku danmaku, Canvas canvas, float left, float top,\r\n                                         boolean fromWorkerThread) {\r\n        if (sStuffer != null) {\r\n            sStuffer.drawDanmaku(danmaku, canvas, left, top, fromWorkerThread, mDisplayConfig);\r\n        }\r\n    }\r\n\r\n    private synchronized TextPaint getPaint(BaseDanmaku danmaku, boolean fromWorkerThread) {\r\n        return mDisplayConfig.getPaint(danmaku, fromWorkerThread);\r\n    }\r\n\r\n    @Override\r\n    public void prepare(BaseDanmaku danmaku, boolean fromWorkerThread) {\r\n        if (sStuffer != null) {\r\n            sStuffer.prepare(danmaku, fromWorkerThread);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void measure(BaseDanmaku danmaku, boolean fromWorkerThread) {\r\n        TextPaint paint = getPaint(danmaku, fromWorkerThread);\r\n        if (mDisplayConfig.HAS_STROKE) {\r\n            mDisplayConfig.applyPaintConfig(danmaku, paint, true);\r\n        }\r\n        calcPaintWH(danmaku, paint, fromWorkerThread);\r\n        if (mDisplayConfig.HAS_STROKE) {\r\n            mDisplayConfig.applyPaintConfig(danmaku, paint, false);\r\n        }\r\n    }\r\n\r\n    private void calcPaintWH(BaseDanmaku danmaku, TextPaint paint, boolean fromWorkerThread) {\r\n        sStuffer.measure(danmaku, paint, fromWorkerThread);\r\n        setDanmakuPaintWidthAndHeight(danmaku, danmaku.paintWidth, danmaku.paintHeight);\r\n    }\r\n\r\n    private void setDanmakuPaintWidthAndHeight(BaseDanmaku danmaku, float w, float h) {\r\n        float pw = w + 2 * danmaku.padding;\r\n        float ph = h + 2 * danmaku.padding;\r\n        if (danmaku.borderColor != 0) {\r\n            pw += 2 * mDisplayConfig.BORDER_WIDTH;\r\n            ph += 2 * mDisplayConfig.BORDER_WIDTH;\r\n        }\r\n        danmaku.paintWidth = pw + getStrokeWidth();\r\n        danmaku.paintHeight = ph;\r\n    }\r\n\r\n    @Override\r\n    public void clearTextHeightCache() {\r\n        sStuffer.clearCaches();\r\n        mDisplayConfig.clearTextHeightCache();\r\n    }\r\n\r\n    @Override\r\n    public float getScaledDensity() {\r\n        return scaledDensity;\r\n    }\r\n\r\n    @Override\r\n    public void resetSlopPixel(float factor) {\r\n        float d = Math.max(factor, getWidth() / DanmakuFactory.BILI_PLAYER_WIDTH); //correct for low density and high resolution\r\n        float slop = d * DanmakuFactory.DANMAKU_MEDIUM_TEXTSIZE;\r\n        mSlopPixel = (int) slop;\r\n        if (factor > 1f)\r\n            mSlopPixel = (int) (slop * factor);\r\n    }\r\n\r\n    @Override\r\n    public int getSlopPixel() {\r\n        return mSlopPixel;\r\n    }\r\n\r\n    @Override\r\n    public void setDensities(float density, int densityDpi, float scaledDensity) {\r\n        this.density = density;\r\n        this.densityDpi = densityDpi;\r\n        this.scaledDensity = scaledDensity;\r\n    }\r\n\r\n    @Override\r\n    public void setSize(int width, int height) {\r\n        this.width = width;\r\n        this.height = height;\r\n        this.locationZ = (float) (width / 2f / Math.tan((Math.PI / 180) * (55f / 2f)));\r\n    }\r\n\r\n    @Override\r\n    public void setDanmakuStyle(int style, float[] values) {\r\n        switch (style) {\r\n            case DANMAKU_STYLE_NONE:\r\n                mDisplayConfig.CONFIG_HAS_SHADOW = false;\r\n                mDisplayConfig.CONFIG_HAS_STROKE = false;\r\n                mDisplayConfig.CONFIG_HAS_PROJECTION = false;\r\n                break;\r\n            case DANMAKU_STYLE_SHADOW:\r\n                mDisplayConfig.CONFIG_HAS_SHADOW = true;\r\n                mDisplayConfig.CONFIG_HAS_STROKE = false;\r\n                mDisplayConfig.CONFIG_HAS_PROJECTION = false;\r\n                setShadowRadius(values[0]);\r\n                break;\r\n            case DANMAKU_STYLE_DEFAULT:\r\n            case DANMAKU_STYLE_STROKEN:\r\n                mDisplayConfig.CONFIG_HAS_SHADOW = false;\r\n                mDisplayConfig.CONFIG_HAS_STROKE = true;\r\n                mDisplayConfig.CONFIG_HAS_PROJECTION = false;\r\n                setPaintStorkeWidth(values[0]);\r\n                break;\r\n            case DANMAKU_STYLE_PROJECTION:\r\n                mDisplayConfig.CONFIG_HAS_SHADOW = false;\r\n                mDisplayConfig.CONFIG_HAS_STROKE = false;\r\n                mDisplayConfig.CONFIG_HAS_PROJECTION = true;\r\n                setProjectionConfig(values[0], values[1], (int) values[2]);\r\n                break;\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void setExtraData(Canvas data) {\r\n        update(data);\r\n    }\r\n\r\n    @Override\r\n    public Canvas getExtraData() {\r\n        return this.canvas;\r\n    }\r\n\r\n    @Override\r\n    public float getStrokeWidth() {\r\n        return mDisplayConfig.getStrokeWidth();\r\n    }\r\n\r\n    @Override\r\n    public void setHardwareAccelerated(boolean enable) {\r\n        mIsHardwareAccelerated = enable;\r\n    }\r\n\r\n    @Override\r\n    public boolean isHardwareAccelerated() {\r\n        return mIsHardwareAccelerated;\r\n    }\r\n\r\n    @Override\r\n    public int getMaximumCacheWidth() {\r\n        return mMaximumBitmapWidth;\r\n    }\r\n\r\n    @Override\r\n    public int getMaximumCacheHeight() {\r\n        return mMaximumBitmapHeight;\r\n    }\r\n\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/android/BaseCacheStuffer.java",
    "content": "package master.flame.danmaku.danmaku.model.android;\n\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.text.TextPaint;\n\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\nimport master.flame.danmaku.danmaku.model.IDrawingCache;\n\n/**\n * Created by ch on 15-7-16.\n */\npublic abstract class BaseCacheStuffer {\n\n    public static abstract class Proxy {\n        /**\n         * 在弹幕显示前使用新的text,使用新的text\n         * @param danmaku\n         * @param fromWorkerThread 是否在工作(非UI)线程,在true的情况下可以做一些耗时操作(例如更新Span的drawblae或者其他IO操作)\n         * @return 如果不需重置，直接返回danmaku.text\n         */\n        public abstract void prepareDrawing(BaseDanmaku danmaku, boolean fromWorkerThread);\n\n        public abstract void releaseResource(BaseDanmaku danmaku);\n\n    }\n\n    protected Proxy mProxy;\n\n    public void prepare(BaseDanmaku danmaku, boolean fromWorkerThread) {\n        if (mProxy != null) {\n            mProxy.prepareDrawing(danmaku, fromWorkerThread);\n        }\n    }\n\n    /**\n     * set paintWidth, paintHeight to danmaku\n     * @param danmaku\n     * @param fromWorkerThread\n     */\n    public abstract void measure(BaseDanmaku danmaku, TextPaint paint, boolean fromWorkerThread);\n\n    /**\n     * clear caches which created by this stuffer\n     */\n    public abstract void clearCaches();\n\n    public abstract void drawDanmaku(BaseDanmaku danmaku, Canvas canvas, float left, float top, boolean fromWorkerThread, AndroidDisplayer.DisplayerConfig displayerConfig);\n\n    public boolean drawCache(BaseDanmaku danmaku, Canvas canvas, float left, float top, Paint alphaPaint, TextPaint paint) {\n        IDrawingCache<?> cache = danmaku.getDrawingCache();\n        if (cache != null) {\n            DrawingCacheHolder holder = (DrawingCacheHolder) cache.get();\n            if (holder != null) {\n                return holder.draw(canvas, left, top, alphaPaint);\n            }\n        }\n        return false;\n    }\n\n    public void clearCache(BaseDanmaku danmaku) {\n\n    }\n\n    public void setProxy(Proxy adapter) {\n        mProxy = adapter;\n    }\n\n    public void releaseResource(BaseDanmaku danmaku) {\n        if (mProxy != null) {\n            mProxy.releaseResource(danmaku);\n        }\n    }\n\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/android/CachingPolicy.java",
    "content": "package master.flame.danmaku.danmaku.model.android;\n\n/**\n * Created by ch on 17/4/28. <br/>\n * The cacing policy apply to {@link master.flame.danmaku.controller.CacheManagingDrawTask}\n * 提供缓存相关的策略设置: <br/>\n * 1.缓存格式  ARGB_4444  ARGB_8888 <br/>\n * 2.缓存池总容量大小百分比系数(0.0~1.0) <br/>\n * 3.过期缓存回收频率 <br/>\n * 4.缓存回收条件内存占比阈值 <br/>\n * 5.可复用缓存尺寸调节\n */\n\npublic class CachingPolicy {\n\n    public final static int BMP_BPP_ARGB_4444 = 16;\n    public final static int BMP_BPP_ARGB_8888 = 32;\n    public final static int CACHE_PERIOD_AUTO = 0;\n    public final static int CACHE_PERIOD_NOT_RECYCLE = -1;\n\n    public final static CachingPolicy POLICY_LAZY = new CachingPolicy(BMP_BPP_ARGB_4444, 0.3f, CACHE_PERIOD_AUTO, 50, 0.01f);\n    public final static CachingPolicy POLICY_GREEDY = new CachingPolicy(BMP_BPP_ARGB_4444, 0.5f, CACHE_PERIOD_NOT_RECYCLE, 50, 0.005f);\n    public final static CachingPolicy POLICY_DEFAULT = POLICY_LAZY;\n\n\n    public CachingPolicy(int bitsPerPixelOfCache, float maxCachePoolSizeFactorPercentage, long periodOfRecycle, int reusableOffsetPixel, float forceRecyleThreshold) {\n        this.bitsPerPixelOfCache = bitsPerPixelOfCache;\n        /* Note: as of {@link android.os.Build.VERSION_CODES#KITKAT},\n        * any bitmap created with this configuration will be created\n        * using {@link #ARGB_8888} instead.*/\n        if (android.os.Build.VERSION.SDK_INT >= 19) {\n            this.bitsPerPixelOfCache = BMP_BPP_ARGB_8888;\n        }\n        this.maxCachePoolSizeFactorPercentage = maxCachePoolSizeFactorPercentage;\n        this.periodOfRecycle = periodOfRecycle;\n        this.reusableOffsetPixel = reusableOffsetPixel;\n        this.forceRecyleThreshold = forceRecyleThreshold;\n    }\n\n    /**\n     * 缓存bitmap的格式, ARGB_4444 = 16  ARGB_8888 = 32\n     * use BMP_BPP_ARGB_4444 or BMP_BPP_ARGB_8888\n     *\n     * Note: as of {@link android.os.Build.VERSION_CODES#KITKAT},\n     * any bitmap created with this configuration will be created\n     * using {@link #ARGB_8888} instead.\n     */\n    public int bitsPerPixelOfCache = BMP_BPP_ARGB_4444;\n\n    /**\n     * 0.0 ~ 1.0, 超过0.5的话有OOM风险\n     */\n    public float maxCachePoolSizeFactorPercentage = 0.3f;\n\n    /**\n     * 回收周期\n     *\n     * @see CACHE_PERIOD_AUTO 0: 默认\n     * @see CACHE_PERIOD_NOT_RECYCLE -1： 不回收\n     */\n    public long periodOfRecycle = CACHE_PERIOD_AUTO;\n\n//    public DanmakuTimer recyleTimer = new DanmakuTimer(SystemClock.uptimeMillis());\n\n    /**\n     * 内存占用大小超过总容量一定比例值(forceRecyleThreshold值)的缓存,在回收时进行主动回收,忽略CACHE_PERIOD_NOT_RECYCLE\n     */\n    public float forceRecyleThreshold = 0.01f;\n\n    /**\n     * @see master.flame.danmaku.controller.CacheManagingDrawTask.CacheManager#findReusableCache\n     */\n    public int reusableOffsetPixel = 0;\n\n    public int maxTimesOfStrictReusableFinds = 20;\n\n    public int maxTimesOfReusableFinds = 150;\n\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/android/DanmakuContext.java",
    "content": "\npackage master.flame.danmaku.danmaku.model.android;\n\nimport android.graphics.Typeface;\n\nimport java.lang.ref.WeakReference;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport master.flame.danmaku.controller.DanmakuFilters;\nimport master.flame.danmaku.controller.DanmakuFilters.IDanmakuFilter;\nimport master.flame.danmaku.danmaku.model.AbsDanmakuSync;\nimport master.flame.danmaku.danmaku.model.AbsDisplayer;\nimport master.flame.danmaku.danmaku.model.AlphaValue;\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\nimport master.flame.danmaku.danmaku.model.GlobalFlagValues;\nimport master.flame.danmaku.danmaku.model.IDanmakus;\n\npublic class DanmakuContext implements Cloneable {\n\n    public static DanmakuContext create() {\n        return new DanmakuContext();\n    }\n\n    public enum DanmakuConfigTag {\n        FT_DANMAKU_VISIBILITY, FB_DANMAKU_VISIBILITY, L2R_DANMAKU_VISIBILITY, R2L_DANMAKU_VISIBILIY, SPECIAL_DANMAKU_VISIBILITY, TYPEFACE, TRANSPARENCY, SCALE_TEXTSIZE, MAXIMUM_NUMS_IN_SCREEN, DANMAKU_STYLE, DANMAKU_BOLD, COLOR_VALUE_WHITE_LIST, USER_ID_BLACK_LIST, USER_HASH_BLACK_LIST, SCROLL_SPEED_FACTOR, BLOCK_GUEST_DANMAKU, DUPLICATE_MERGING_ENABLED, MAXIMUN_LINES, OVERLAPPING_ENABLE, ALIGN_BOTTOM, DANMAKU_MARGIN, DANMAKU_SYNC;\n\n        public boolean isVisibilityRelatedTag() {\n            return this.equals(FT_DANMAKU_VISIBILITY) || this.equals(FB_DANMAKU_VISIBILITY)\n                    || this.equals(L2R_DANMAKU_VISIBILITY) || this.equals(R2L_DANMAKU_VISIBILIY)\n                    || this.equals(SPECIAL_DANMAKU_VISIBILITY) || this.equals(COLOR_VALUE_WHITE_LIST)\n                    || this.equals(USER_ID_BLACK_LIST);\n        }\n    }\n\n    /**\n     * 默认字体\n     */\n    public Typeface mFont = null;\n\n    /**\n     * paint alpha:0-255\n     */\n    public int transparency = AlphaValue.MAX;\n\n    public float scaleTextSize = 1.0f;\n\n    public int margin = 0;\n\n    /**\n     * 弹幕显示隐藏设置\n     */\n    public boolean FTDanmakuVisibility = true;\n\n    public boolean FBDanmakuVisibility = true;\n\n    public boolean L2RDanmakuVisibility = true;\n\n    public boolean R2LDanmakuVisibility = true;\n\n    public boolean SpecialDanmakuVisibility = true;\n    \n    List<Integer> mFilterTypes = new ArrayList<Integer>();\n\n    /**\n     * 同屏弹幕数量 -1 按绘制效率自动调整 0 无限制 n 同屏最大显示n个弹幕\n     */\n    public int maximumNumsInScreen = -1;\n\n    /**\n     * 默认滚动速度系数\n     */\n    public float scrollSpeedFactor = 1.0f;\n\n    public AbsDanmakuSync danmakuSync;\n\n    List<Integer> mColorValueWhiteList = new ArrayList<Integer>();\n    \n    List<Integer> mUserIdBlackList = new ArrayList<Integer>(); \n    \n    List<String> mUserHashBlackList = new ArrayList<String>();\n\n    private List<WeakReference<ConfigChangedCallback>> mCallbackList;\n\n    private boolean mBlockGuestDanmaku = false;\n\n    private boolean mDuplicateMergingEnable = false;\n\n    private boolean mIsAlignBottom = false;\n\n    private BaseCacheStuffer mCacheStuffer;\n\n    private boolean mIsMaxLinesLimited;\n\n    private boolean mIsPreventOverlappingEnabled;\n\n    public AbsDisplayer mDisplayer = new AndroidDisplayer();\n\n    public GlobalFlagValues mGlobalFlagValues = new GlobalFlagValues();\n\n    public DanmakuFilters mDanmakuFilters = new DanmakuFilters();\n\n    public DanmakuFactory mDanmakuFactory = DanmakuFactory.create();\n\n    public CachingPolicy cachingPolicy = CachingPolicy.POLICY_DEFAULT;\n\n    private IDanmakus.BaseComparator mBaseComparator;\n\n    public IDanmakus.BaseComparator getBaseComparator() {\n        return mBaseComparator;\n    }\n\n    public void setBaseComparator(IDanmakus.BaseComparator baseComparator) {\n        this.mBaseComparator = baseComparator;\n    }\n\n    public AbsDisplayer getDisplayer() {\n        return mDisplayer;\n    }\n\n    /**\n     * 0 默认 Choreographer驱动DrawHandler线程刷新 <br />\n     * 1 \"DFM Update\"单独线程刷新 <br />\n     * 2 DrawHandler线程自驱动刷新\n     *\n     * Note: 在系统{@link android.os.Build.VERSION_CODES#JELLY_BEAN}以下, 0方式会被2方式代替\n     */\n    public byte updateMethod = 0;\n\n    /**\n     * set typeface\n     * \n     * @param font\n     */\n    public DanmakuContext setTypeface(Typeface font) {\n        if (mFont != font) {\n            mFont = font;\n            mDisplayer.clearTextHeightCache();\n            mDisplayer.setTypeFace(font);\n            notifyConfigureChanged(DanmakuConfigTag.TYPEFACE);\n        }\n        return this;\n    }\n\n    public DanmakuContext setDanmakuTransparency(float p) {\n        int newTransparency = (int) (p * AlphaValue.MAX);\n        if (newTransparency != transparency) {\n            transparency = newTransparency;\n            mDisplayer.setTransparency(newTransparency);\n            notifyConfigureChanged(DanmakuConfigTag.TRANSPARENCY, p);\n        }\n        return this;\n    }\n\n    public DanmakuContext setScaleTextSize(float p) {\n        if (scaleTextSize != p) {\n            scaleTextSize = p;\n            mDisplayer.clearTextHeightCache();\n            mDisplayer.setScaleTextSizeFactor(p);\n            mGlobalFlagValues.updateMeasureFlag();\n            mGlobalFlagValues.updateVisibleFlag();\n            notifyConfigureChanged(DanmakuConfigTag.SCALE_TEXTSIZE, p);\n        }\n        return this;\n    }\n\n    public DanmakuContext setDanmakuMargin(int m) {\n        if (margin != m) {\n            margin = m;\n            mDisplayer.setMargin(m);\n            mGlobalFlagValues.updateFilterFlag();\n            mGlobalFlagValues.updateVisibleFlag();\n            notifyConfigureChanged(DanmakuConfigTag.DANMAKU_MARGIN, m);\n        }\n        return this;\n    }\n\n    public DanmakuContext setMarginTop(int m) {\n        mDisplayer.setAllMarginTop(m);\n        return this;\n    }\n\n    /**\n     * @return 是否显示顶部弹幕\n     */\n    public boolean getFTDanmakuVisibility() {\n        return FTDanmakuVisibility;\n    }\n\n    /**\n     * 设置是否显示顶部弹幕\n     * \n     * @param visible\n     */\n    public DanmakuContext setFTDanmakuVisibility(boolean visible) {\n        setDanmakuVisible(visible, BaseDanmaku.TYPE_FIX_TOP);\n        setFilterData(DanmakuFilters.TAG_TYPE_DANMAKU_FILTER, mFilterTypes);\n        mGlobalFlagValues.updateFilterFlag();\n        if (FTDanmakuVisibility != visible) {\n            FTDanmakuVisibility = visible;\n            notifyConfigureChanged(DanmakuConfigTag.FT_DANMAKU_VISIBILITY, visible);\n        }\n        return this;\n    }\n\n    private <T> void setFilterData(String tag, T data) {\n        setFilterData(tag, data, true);\n    }\n\n    private <T> void setFilterData(String tag, T data, boolean primary) {\n        @SuppressWarnings(\"unchecked\")\n        IDanmakuFilter<T> filter = (IDanmakuFilter<T>) mDanmakuFilters.get(tag, primary);\n        filter.setData(data);\n    }\n\n    private void setDanmakuVisible(boolean visible, int type) {\n        if (visible) {\n            mFilterTypes.remove(Integer.valueOf(type));\n        } else if (!mFilterTypes.contains(Integer.valueOf(type))) {\n            mFilterTypes.add(type);\n        }\n    }\n\n    /**\n     * @return 是否显示底部弹幕\n     */\n    public boolean getFBDanmakuVisibility() {\n        return FBDanmakuVisibility;\n    }\n\n    /**\n     * 设置是否显示底部弹幕\n     * \n     * @param visible\n     */\n    public DanmakuContext setFBDanmakuVisibility(boolean visible) {\n        setDanmakuVisible(visible, BaseDanmaku.TYPE_FIX_BOTTOM);\n        setFilterData(DanmakuFilters.TAG_TYPE_DANMAKU_FILTER, mFilterTypes);\n        mGlobalFlagValues.updateFilterFlag();\n        if (FBDanmakuVisibility != visible) {\n            FBDanmakuVisibility = visible;\n            notifyConfigureChanged(DanmakuConfigTag.FB_DANMAKU_VISIBILITY, visible);\n        }\n        return this;\n    }\n\n    /**\n     * @return 是否显示左右滚动弹幕\n     */\n    public boolean getL2RDanmakuVisibility() {\n        return L2RDanmakuVisibility;\n    }\n\n    /**\n     * 设置是否显示左右滚动弹幕\n     * \n     * @param visible\n     */\n    public DanmakuContext setL2RDanmakuVisibility(boolean visible) {\n        setDanmakuVisible(visible, BaseDanmaku.TYPE_SCROLL_LR);\n        setFilterData(DanmakuFilters.TAG_TYPE_DANMAKU_FILTER, mFilterTypes);\n        mGlobalFlagValues.updateFilterFlag();\n        if(L2RDanmakuVisibility != visible){\n            L2RDanmakuVisibility = visible;\n            notifyConfigureChanged(DanmakuConfigTag.L2R_DANMAKU_VISIBILITY, visible);\n        }\n        return this;\n    }\n\n    /**\n     * @return 是否显示右左滚动弹幕\n     */\n    public boolean getR2LDanmakuVisibility() {\n        return R2LDanmakuVisibility;\n    }\n\n    /**\n     * 设置是否显示右左滚动弹幕\n     * \n     * @param visible\n     */\n    public DanmakuContext setR2LDanmakuVisibility(boolean visible) {\n        setDanmakuVisible(visible, BaseDanmaku.TYPE_SCROLL_RL);\n        setFilterData(DanmakuFilters.TAG_TYPE_DANMAKU_FILTER, mFilterTypes);\n        mGlobalFlagValues.updateFilterFlag();\n        if (R2LDanmakuVisibility != visible) {\n            R2LDanmakuVisibility = visible;\n            notifyConfigureChanged(DanmakuConfigTag.R2L_DANMAKU_VISIBILIY, visible);\n        }\n        return this;\n    }\n\n    /**\n     * @return 是否显示特殊弹幕\n     */\n    public boolean getSpecialDanmakuVisibility() {\n        return SpecialDanmakuVisibility;\n    }\n\n    /**\n     * 设置是否显示特殊弹幕\n     * \n     * @param visible\n     */\n    public DanmakuContext setSpecialDanmakuVisibility(boolean visible) {\n        setDanmakuVisible(visible, BaseDanmaku.TYPE_SPECIAL);\n        setFilterData(DanmakuFilters.TAG_TYPE_DANMAKU_FILTER, mFilterTypes);\n        mGlobalFlagValues.updateFilterFlag();\n        if (SpecialDanmakuVisibility != visible) {\n            SpecialDanmakuVisibility = visible;\n            notifyConfigureChanged(DanmakuConfigTag.SPECIAL_DANMAKU_VISIBILITY, visible);\n        }\n        return this;\n    }\n\n    /**\n     * 设置同屏弹幕密度 -1自动 0无限制\n     * \n     * @param maxSize\n     * @return\n     */\n    public DanmakuContext setMaximumVisibleSizeInScreen(int maxSize) {\n        maximumNumsInScreen = maxSize;\n        // 无限制\n        if (maxSize == 0) {\n            mDanmakuFilters.unregisterFilter(DanmakuFilters.TAG_QUANTITY_DANMAKU_FILTER);\n            mDanmakuFilters.unregisterFilter(DanmakuFilters.TAG_ELAPSED_TIME_FILTER);\n            notifyConfigureChanged(DanmakuConfigTag.MAXIMUM_NUMS_IN_SCREEN, maxSize);\n            return this;\n        }\n        // 自动调整\n        if (maxSize == -1) {\n            mDanmakuFilters.unregisterFilter(DanmakuFilters.TAG_QUANTITY_DANMAKU_FILTER);\n            mDanmakuFilters.registerFilter(DanmakuFilters.TAG_ELAPSED_TIME_FILTER);\n            notifyConfigureChanged(DanmakuConfigTag.MAXIMUM_NUMS_IN_SCREEN, maxSize);\n            return this;\n        }\n        setFilterData(DanmakuFilters.TAG_QUANTITY_DANMAKU_FILTER, maxSize);\n        mGlobalFlagValues.updateFilterFlag();\n        notifyConfigureChanged(DanmakuConfigTag.MAXIMUM_NUMS_IN_SCREEN, maxSize);\n        return this;\n    }\n\n    /**\n     * 设置描边样式\n     * \n     * @param style DANMAKU_STYLE_NONE DANMAKU_STYLE_SHADOW or\n     *            DANMAKU_STYLE_STROKEN or DANMAKU_STYLE_PROJECTION\n     * @param values\n     *        DANMAKU_STYLE_SHADOW 阴影模式下，values传入阴影半径\n     *        DANMAKU_STYLE_STROKEN 描边模式下，values传入描边宽度\n     *        DANMAKU_STYLE_PROJECTION\n     *            投影模式下，values传入offsetX, offsetY, alpha\n     *                offsetX/offsetY: x/y 方向上的偏移量\n     *                alpha: 投影透明度 [0...255]\n     * @return\n     */\n    public DanmakuContext setDanmakuStyle(int style, float... values) {\n        mDisplayer.setDanmakuStyle(style, values);\n        notifyConfigureChanged(DanmakuConfigTag.DANMAKU_STYLE, style, values);\n        return this;\n    }\n\n    /**\n     * 设置是否粗体显示,对某些字体无效\n     * \n     * @param bold\n     * @return\n     */\n    public DanmakuContext setDanmakuBold(boolean bold) {\n        mDisplayer.setFakeBoldText(bold);\n        notifyConfigureChanged(DanmakuConfigTag.DANMAKU_BOLD, bold);\n        return this;\n    }\n    \n    /**\n     * 设置色彩过滤弹幕白名单\n     * @param colors\n     * @return\n     */\n    public DanmakuContext setColorValueWhiteList(Integer... colors) {\n        mColorValueWhiteList.clear();\n        if (colors == null || colors.length == 0) {\n            mDanmakuFilters.unregisterFilter(DanmakuFilters.TAG_TEXT_COLOR_DANMAKU_FILTER);\n        } else {\n            Collections.addAll(mColorValueWhiteList, colors);\n            setFilterData(DanmakuFilters.TAG_TEXT_COLOR_DANMAKU_FILTER, mColorValueWhiteList);\n        }\n        mGlobalFlagValues.updateFilterFlag();\n        notifyConfigureChanged(DanmakuConfigTag.COLOR_VALUE_WHITE_LIST, mColorValueWhiteList);\n        return this;\n    }\n    \n    public List<Integer> getColorValueWhiteList(){\n        return mColorValueWhiteList;\n    }\n    \n    /**\n     * 设置屏蔽弹幕用户hash\n     * @param hashes \n     * @return\n     */\n    public DanmakuContext setUserHashBlackList(String... hashes) {\n        mUserHashBlackList.clear();\n        if (hashes == null || hashes.length == 0) {\n            mDanmakuFilters.unregisterFilter(DanmakuFilters.TAG_USER_HASH_FILTER);\n        } else {\n            Collections.addAll(mUserHashBlackList, hashes);\n            setFilterData(DanmakuFilters.TAG_USER_HASH_FILTER, mUserHashBlackList);\n        }\n        mGlobalFlagValues.updateFilterFlag();\n        notifyConfigureChanged(DanmakuConfigTag.USER_HASH_BLACK_LIST, mUserHashBlackList);\n        return this;\n    }\n    \n    public DanmakuContext removeUserHashBlackList(String... hashes){\n        if(hashes == null || hashes.length == 0) {\n            return this;\n        }\n        for (String hash : hashes) {\n            mUserHashBlackList.remove(hash);\n        }\n        setFilterData(DanmakuFilters.TAG_USER_HASH_FILTER, mUserHashBlackList);\n        mGlobalFlagValues.updateFilterFlag();\n        notifyConfigureChanged(DanmakuConfigTag.USER_HASH_BLACK_LIST, mUserHashBlackList);\n        return this;\n    }\n    \n    /**\n     * 添加屏蔽用户\n     * @param hashes\n     * @return\n     */\n    public DanmakuContext addUserHashBlackList(String... hashes){\n        if(hashes == null || hashes.length == 0) {\n            return this;\n        }\n        Collections.addAll(mUserHashBlackList, hashes);\n        setFilterData(DanmakuFilters.TAG_USER_HASH_FILTER, mUserHashBlackList);\n        mGlobalFlagValues.updateFilterFlag();\n        notifyConfigureChanged(DanmakuConfigTag.USER_HASH_BLACK_LIST, mUserHashBlackList);\n        return this;\n    }\n    \n    public List<String> getUserHashBlackList(){\n        return mUserHashBlackList;\n    }\n    \n    \n    /**\n     * 设置屏蔽弹幕用户id , 0 表示游客弹幕\n     * @param ids \n     * @return\n     */\n    public DanmakuContext setUserIdBlackList(Integer... ids) {\n        mUserIdBlackList.clear();\n        if (ids == null || ids.length == 0) {\n            mDanmakuFilters.unregisterFilter(DanmakuFilters.TAG_USER_ID_FILTER);\n        } else {\n            Collections.addAll(mUserIdBlackList, ids);\n            setFilterData(DanmakuFilters.TAG_USER_ID_FILTER, mUserIdBlackList);\n        }\n        mGlobalFlagValues.updateFilterFlag();\n        notifyConfigureChanged(DanmakuConfigTag.USER_ID_BLACK_LIST, mUserIdBlackList);\n        return this;\n    }\n    \n    public DanmakuContext removeUserIdBlackList(Integer... ids){\n        if(ids == null || ids.length == 0) {\n            return this;\n        }\n        for (Integer id : ids) {\n            mUserIdBlackList.remove(id);\n        }\n        setFilterData(DanmakuFilters.TAG_USER_ID_FILTER, mUserIdBlackList);\n        mGlobalFlagValues.updateFilterFlag();\n        notifyConfigureChanged(DanmakuConfigTag.USER_ID_BLACK_LIST, mUserIdBlackList);\n        return this;\n    }\n    \n    /**\n     * 添加屏蔽用户\n     * @param ids\n     * @return\n     */\n    public DanmakuContext addUserIdBlackList(Integer... ids){\n        if(ids == null || ids.length == 0) {\n            return this;\n        }\n        Collections.addAll(mUserIdBlackList, ids);\n        setFilterData(DanmakuFilters.TAG_USER_ID_FILTER, mUserIdBlackList);\n        mGlobalFlagValues.updateFilterFlag();\n        notifyConfigureChanged(DanmakuConfigTag.USER_ID_BLACK_LIST, mUserIdBlackList);\n        return this;\n    }\n    \n    public List<Integer> getUserIdBlackList(){\n        return mUserIdBlackList;\n    }\n    \n    /**\n     * 设置是否屏蔽游客弹幕\n     * @param block true屏蔽，false不屏蔽\n     * @return\n     */\n    public DanmakuContext blockGuestDanmaku(boolean block) {\n        if (mBlockGuestDanmaku != block) {\n            mBlockGuestDanmaku = block;\n            if (block) {\n                setFilterData(DanmakuFilters.TAG_GUEST_FILTER, block);\n            } else {\n                mDanmakuFilters.unregisterFilter(DanmakuFilters.TAG_GUEST_FILTER);\n            }\n            mGlobalFlagValues.updateFilterFlag();\n            notifyConfigureChanged(DanmakuConfigTag.BLOCK_GUEST_DANMAKU, block);\n        }\n        return this;\n    }\n    \n    /**\n     * 设置弹幕滚动速度系数,只对滚动弹幕有效\n     * @param p\n     * @return\n     */\n    public DanmakuContext setScrollSpeedFactor(float p){\n        if (scrollSpeedFactor != p) {\n            scrollSpeedFactor = p;\n            mDanmakuFactory.updateDurationFactor(p);\n            mGlobalFlagValues.updateMeasureFlag();\n            mGlobalFlagValues.updateVisibleFlag();\n            notifyConfigureChanged(DanmakuConfigTag.SCROLL_SPEED_FACTOR, p);\n        }\n        return this;\n    }\n    \n    /**\n     * 设置是否启用合并重复弹幕\n     * @param enable\n     * @return\n     */\n    public DanmakuContext setDuplicateMergingEnabled(boolean enable) {\n        if (mDuplicateMergingEnable != enable) {\n            mDuplicateMergingEnable = enable;\n            mGlobalFlagValues.updateFilterFlag();\n            notifyConfigureChanged(DanmakuConfigTag.DUPLICATE_MERGING_ENABLED, enable);\n        }\n        return this;\n    }\n\n    public boolean isDuplicateMergingEnabled() {\n        return mDuplicateMergingEnable;\n    }\n\n    public DanmakuContext alignBottom(boolean enable) {\n        if (mIsAlignBottom != enable) {\n            mIsAlignBottom = enable;\n            notifyConfigureChanged(DanmakuConfigTag.ALIGN_BOTTOM, enable);\n            mGlobalFlagValues.updateVisibleFlag();\n        }\n        return this;\n    }\n\n    public boolean isAlignBottom() {\n        return mIsAlignBottom;\n    }\n\n    /**\n     * 设置最大显示行数\n     * @param pairs map<K,V> 设置null取消行数限制\n     * K = (BaseDanmaku.TYPE_SCROLL_RL|BaseDanmaku.TYPE_SCROLL_LR|BaseDanmaku.TYPE_FIX_TOP|BaseDanmaku.TYPE_FIX_BOTTOM)\n     * V = 最大行数\n     * @return\n     */\n    public DanmakuContext setMaximumLines(Map<Integer, Integer> pairs) {\n        mIsMaxLinesLimited = (pairs != null);\n        if (pairs == null) {\n            mDanmakuFilters.unregisterFilter(DanmakuFilters.TAG_MAXIMUN_LINES_FILTER, false);\n        } else {\n            setFilterData(DanmakuFilters.TAG_MAXIMUN_LINES_FILTER, pairs, false);\n        }\n        mGlobalFlagValues.updateFilterFlag();\n        notifyConfigureChanged(DanmakuConfigTag.MAXIMUN_LINES, pairs);\n        return this;\n    }\n\n    @Deprecated\n    public DanmakuContext setOverlapping(Map<Integer, Boolean> pairs) {\n        return preventOverlapping(pairs);\n    }\n\n    /**\n     * 设置防弹幕重叠\n     * @param pairs map<K,V> 设置null恢复默认设置,默认为允许重叠\n     * K = (BaseDanmaku.TYPE_SCROLL_RL|BaseDanmaku.TYPE_SCROLL_LR|BaseDanmaku.TYPE_FIX_TOP|BaseDanmaku.TYPE_FIX_BOTTOM)\n     * V = true|false 是否重叠\n     * @return\n     */\n    public DanmakuContext preventOverlapping(Map<Integer, Boolean> pairs) {\n        mIsPreventOverlappingEnabled = (pairs != null);\n        if (pairs == null) {\n            mDanmakuFilters.unregisterFilter(DanmakuFilters.TAG_OVERLAPPING_FILTER, false);\n        } else {\n            setFilterData(DanmakuFilters.TAG_OVERLAPPING_FILTER, pairs, false);\n        }\n        mGlobalFlagValues.updateFilterFlag();\n        notifyConfigureChanged(DanmakuConfigTag.OVERLAPPING_ENABLE, pairs);\n        return this;\n    }\n\n    public boolean isMaxLinesLimited() {\n        return mIsMaxLinesLimited;\n    }\n\n    public boolean isPreventOverlappingEnabled() {\n        return mIsPreventOverlappingEnabled;\n    }\n\n    /**\n     * 设置缓存绘制填充器，默认使用{@link SimpleTextCacheStuffer}只支持纯文字显示, 如果需要图文混排请设置{@link SpannedCacheStuffer}\n     * 如果需要定制其他样式请扩展{@link SimpleTextCacheStuffer}|{@link SpannedCacheStuffer}\n     * @param cacheStuffer\n     * @param cacheStufferAdapter\n     */\n    public DanmakuContext setCacheStuffer(BaseCacheStuffer cacheStuffer, BaseCacheStuffer.Proxy cacheStufferAdapter) {\n        this.mCacheStuffer = cacheStuffer;\n        if (this.mCacheStuffer != null) {\n            this.mCacheStuffer.setProxy(cacheStufferAdapter);\n            mDisplayer.setCacheStuffer(this.mCacheStuffer);\n        }\n        return this;\n    }\n\n    public DanmakuContext setDanmakuSync(AbsDanmakuSync danmakuSync) {\n        this.danmakuSync = danmakuSync;\n        return this;\n    }\n\n    public DanmakuContext setCachingPolicy(CachingPolicy cachingPolicy) {\n        this.cachingPolicy = cachingPolicy;\n        return this;\n    }\n    \n    public interface ConfigChangedCallback {\n        public boolean onDanmakuConfigChanged(DanmakuContext config, DanmakuConfigTag tag,\n                Object... value);\n    }\n\n    public void registerConfigChangedCallback(ConfigChangedCallback listener) {\n        if (listener == null || mCallbackList == null) {\n            mCallbackList = Collections.synchronizedList(new ArrayList<WeakReference<ConfigChangedCallback>>());\n        }\n        for (WeakReference<ConfigChangedCallback> configReferer : mCallbackList) {\n            if (listener.equals(configReferer.get())) {\n                return;\n            }\n        }\n        mCallbackList.add(new WeakReference<ConfigChangedCallback>(listener));\n    }\n\n    public void unregisterConfigChangedCallback(ConfigChangedCallback listener) {\n        if (listener == null || mCallbackList == null)\n            return;\n        for (WeakReference<ConfigChangedCallback> configReferer : mCallbackList) {\n            if (listener.equals(configReferer.get())) {\n                mCallbackList.remove(listener);\n                return;\n            }\n        }\n    }\n\n    public void unregisterAllConfigChangedCallbacks() {\n        if (mCallbackList != null) {\n            mCallbackList.clear();\n            mCallbackList = null;\n        }\n    }\n\n    private void notifyConfigureChanged(DanmakuConfigTag tag, Object... values) {\n        if (mCallbackList != null) {\n            for (WeakReference<ConfigChangedCallback> configReferer : mCallbackList) {\n                ConfigChangedCallback cb = configReferer.get();\n                if (cb != null) {\n                    cb.onDanmakuConfigChanged(this, tag, values);\n                }\n            }\n        }\n    }\n\n    public DanmakuContext registerFilter(DanmakuFilters.BaseDanmakuFilter filter) {\n        mDanmakuFilters.registerFilter(filter);\n        mGlobalFlagValues.updateFilterFlag();\n        return this;\n    }\n\n    public DanmakuContext unregisterFilter(DanmakuFilters.BaseDanmakuFilter filter) {\n        mDanmakuFilters.unregisterFilter(filter);\n        mGlobalFlagValues.updateFilterFlag();\n        return this;\n    }\n\n    public DanmakuContext resetContext() {\n        mDisplayer = new AndroidDisplayer();\n        mGlobalFlagValues = new GlobalFlagValues();\n//        mDanmakuFilters = new DanmakuFilters();\n        mDanmakuFilters.clear();\n        mDanmakuFactory = DanmakuFactory.create();\n        return this;\n    }\n\n    @Override\n    public Object clone() throws CloneNotSupportedException {\n        return super.clone();\n    }\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/android/DanmakuFactory.java",
    "content": "/*\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage master.flame.danmaku.danmaku.model.android;\n\nimport android.util.Log;\n\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\nimport master.flame.danmaku.danmaku.model.Duration;\nimport master.flame.danmaku.danmaku.model.FBDanmaku;\nimport master.flame.danmaku.danmaku.model.FTDanmaku;\nimport master.flame.danmaku.danmaku.model.IDisplayer;\nimport master.flame.danmaku.danmaku.model.L2RDanmaku;\nimport master.flame.danmaku.danmaku.model.R2LDanmaku;\nimport master.flame.danmaku.danmaku.model.SpecialDanmaku;\n\npublic class DanmakuFactory {\n\n    public final static float OLD_BILI_PLAYER_WIDTH = 539;\n\n    public final static float BILI_PLAYER_WIDTH = 682;\n\n    public final static float OLD_BILI_PLAYER_HEIGHT = 385;\n\n    public final static float BILI_PLAYER_HEIGHT = 438;\n\n    public final static long COMMON_DANMAKU_DURATION = 3800; // B站原始分辨率下弹幕存活时间\n\n    public final static int DANMAKU_MEDIUM_TEXTSIZE = 25;\n\n    public final static long MIN_DANMAKU_DURATION = 4000;\n\n    public final static long MAX_DANMAKU_DURATION_HIGH_DENSITY = 9000;\n\n    public int CURRENT_DISP_WIDTH = 0, CURRENT_DISP_HEIGHT = 0;\n\n    private SpecialDanmaku.ScaleFactor mScaleFactor = null;\n\n    private float CURRENT_DISP_SIZE_FACTOR = 1.0f;\n\n    public long REAL_DANMAKU_DURATION = COMMON_DANMAKU_DURATION;\n\n    public long MAX_DANMAKU_DURATION = MIN_DANMAKU_DURATION;\n\n    public Duration MAX_Duration_Scroll_Danmaku;\n\n    public Duration MAX_Duration_Fix_Danmaku;\n\n    public Duration MAX_Duration_Special_Danmaku;\n\n    public IDisplayer sLastDisp;\n    private DanmakuContext sLastConfig;\n\n    public static DanmakuFactory create() {\n        return new DanmakuFactory();\n    }\n\n    protected DanmakuFactory() {\n\n    }\n\n    public void resetDurationsData() {\n        sLastDisp = null;\n        CURRENT_DISP_WIDTH = CURRENT_DISP_HEIGHT = 0;\n        MAX_Duration_Scroll_Danmaku = null;\n        MAX_Duration_Fix_Danmaku = null;\n        MAX_Duration_Special_Danmaku = null;\n        MAX_DANMAKU_DURATION = MIN_DANMAKU_DURATION;\n    }\n\n    public void notifyDispSizeChanged(DanmakuContext context) {\n        sLastConfig = context;\n        sLastDisp = context.getDisplayer();\n        createDanmaku(BaseDanmaku.TYPE_SCROLL_RL, context);\n    }\n\n    public BaseDanmaku createDanmaku(int type) {\n        return createDanmaku(type, sLastConfig);\n    }\n\n    public BaseDanmaku createDanmaku(int type, DanmakuContext context) {\n        if (context == null)\n            return null;\n        sLastConfig = context;\n        sLastDisp = context.getDisplayer();\n        return createDanmaku(type, sLastDisp.getWidth(), sLastDisp.getHeight(), CURRENT_DISP_SIZE_FACTOR, context.scrollSpeedFactor);\n    }\n\n    public BaseDanmaku createDanmaku(int type, IDisplayer disp, float viewportScale, float scrollSpeedFactor) {\n        if (disp == null)\n            return null;\n        sLastDisp = disp;\n        return createDanmaku(type, disp.getWidth(), disp.getHeight(), viewportScale, scrollSpeedFactor);\n    }\n\n    /**\n     * 创建弹幕数据请尽量使用此方法,参考BiliDanmakuParser或AcfunDanmakuParser\n     *\n     * @param type           弹幕类型\n     * @param viewportWidth  danmakuview宽度,会影响滚动弹幕的存活时间(duration)\n     * @param viewportHeight danmakuview高度\n     * @param viewportScale  缩放比例,会影响滚动弹幕的存活时间(duration)\n     * @return\n     */\n    public BaseDanmaku createDanmaku(int type, int viewportWidth, int viewportHeight,\n                                     float viewportScale, float scrollSpeedFactor) {\n        return createDanmaku(type, (float) viewportWidth, (float) viewportHeight, viewportScale, scrollSpeedFactor);\n    }\n\n    /**\n     * 创建弹幕数据请尽量使用此方法,参考BiliDanmakuParser或AcfunDanmakuParser\n     *\n     * @param type               弹幕类型\n     * @param viewportWidth      danmakuview宽度,会影响滚动弹幕的存活时间(duration)\n     * @param viewportHeight     danmakuview高度\n     * @param viewportSizeFactor 会影响滚动弹幕的速度/存活时间(duration)\n     * @return\n     */\n    public BaseDanmaku createDanmaku(int type, float viewportWidth, float viewportHeight,\n                                     float viewportSizeFactor, float scrollSpeedFactor) {\n        int oldDispWidth = CURRENT_DISP_WIDTH;\n        int oldDispHeight = CURRENT_DISP_HEIGHT;\n        boolean sizeChanged = updateViewportState(viewportWidth, viewportHeight, viewportSizeFactor);\n        if (MAX_Duration_Scroll_Danmaku == null) {\n            MAX_Duration_Scroll_Danmaku = new Duration(REAL_DANMAKU_DURATION);\n            MAX_Duration_Scroll_Danmaku.setFactor(scrollSpeedFactor);\n        } else if (sizeChanged) {\n            MAX_Duration_Scroll_Danmaku.setValue(REAL_DANMAKU_DURATION);\n        }\n\n        if (MAX_Duration_Fix_Danmaku == null) {\n            MAX_Duration_Fix_Danmaku = new Duration(COMMON_DANMAKU_DURATION);\n        }\n\n        float scaleX = 1f;\n        float scaleY = 1f;\n        if (sizeChanged && viewportWidth > 0) {\n            updateMaxDanmakuDuration();\n            if (oldDispWidth > 0 && oldDispHeight > 0) {\n                scaleX = viewportWidth / (float) oldDispWidth;\n                scaleY = viewportHeight / (float) oldDispHeight;\n            }\n            updateScaleFactor((int) viewportWidth, (int) viewportHeight, scaleX, scaleY);\n            if (viewportHeight > 0) {\n                updateSpecialDanmakusDate((int) viewportWidth, (int) viewportHeight, scaleX, scaleY);\n            }\n        }\n\n        BaseDanmaku instance = null;\n        switch (type) {\n            case 1: // 从右往左滚动\n                instance = new R2LDanmaku(MAX_Duration_Scroll_Danmaku);\n                break;\n            case 4: // 底端固定\n                instance = new FBDanmaku(MAX_Duration_Fix_Danmaku);\n                break;\n            case 5: // 顶端固定\n                instance = new FTDanmaku(MAX_Duration_Fix_Danmaku);\n                break;\n            case 6: // 从左往右滚动\n                instance = new L2RDanmaku(MAX_Duration_Scroll_Danmaku);\n                break;\n            case 7: // 特殊弹幕\n                instance = new SpecialDanmaku();\n                updateScaleFactor((int) viewportWidth, (int) viewportHeight, scaleX, scaleY);\n                ((SpecialDanmaku) instance).setScaleFactor(mScaleFactor);\n                break;\n        }\n        return instance;\n    }\n\n    private void updateScaleFactor(int width, int height, float scaleX, float scaleY) {\n        if (mScaleFactor == null) {\n            mScaleFactor = new SpecialDanmaku.ScaleFactor(width, height, scaleX, scaleY);\n        }\n        mScaleFactor.update(width, height, scaleX, scaleY);\n    }\n\n    public boolean updateViewportState(float viewportWidth, float viewportHeight,\n                                       float viewportSizeFactor) {\n        boolean sizeChanged = false;\n        if (CURRENT_DISP_WIDTH != (int) viewportWidth\n                || CURRENT_DISP_HEIGHT != (int) viewportHeight\n                || CURRENT_DISP_SIZE_FACTOR != viewportSizeFactor) {\n            sizeChanged = true;\n            REAL_DANMAKU_DURATION = (long) (COMMON_DANMAKU_DURATION * (viewportSizeFactor\n                    * viewportWidth / BILI_PLAYER_WIDTH));\n            REAL_DANMAKU_DURATION = Math.min(MAX_DANMAKU_DURATION_HIGH_DENSITY,\n                    REAL_DANMAKU_DURATION);\n            REAL_DANMAKU_DURATION = Math.max(MIN_DANMAKU_DURATION, REAL_DANMAKU_DURATION);\n\n            CURRENT_DISP_WIDTH = (int) viewportWidth;\n            CURRENT_DISP_HEIGHT = (int) viewportHeight;\n            CURRENT_DISP_SIZE_FACTOR = viewportSizeFactor;\n        }\n        return sizeChanged;\n    }\n\n    private synchronized void updateSpecialDanmakusDate(int width, int height, float scaleX, float scaleY) {\n        if (mScaleFactor != null) {\n            mScaleFactor.update(width, height, scaleX, scaleY);\n        }\n    }\n\n    public void updateMaxDanmakuDuration() {\n        long maxScrollDuration = (MAX_Duration_Scroll_Danmaku == null ? 0 : MAX_Duration_Scroll_Danmaku.value),\n                maxFixDuration = (MAX_Duration_Fix_Danmaku == null ? 0 : MAX_Duration_Fix_Danmaku.value),\n                maxSpecialDuration = (MAX_Duration_Special_Danmaku == null ? 0 : MAX_Duration_Special_Danmaku.value);\n\n        MAX_DANMAKU_DURATION = Math.max(maxScrollDuration, maxFixDuration);\n        MAX_DANMAKU_DURATION = Math.max(MAX_DANMAKU_DURATION, maxSpecialDuration);\n\n        MAX_DANMAKU_DURATION = Math.max(COMMON_DANMAKU_DURATION, MAX_DANMAKU_DURATION);\n        MAX_DANMAKU_DURATION = Math.max(REAL_DANMAKU_DURATION, MAX_DANMAKU_DURATION);\n    }\n\n    public void updateDurationFactor(float f) {\n        if (MAX_Duration_Scroll_Danmaku == null || MAX_Duration_Fix_Danmaku == null)\n            return;\n        MAX_Duration_Scroll_Danmaku.setFactor(f);\n        updateMaxDanmakuDuration();\n    }\n\n    /**\n     * Initial translation data of the special danmaku\n     *\n     * @param item\n     * @param beginX\n     * @param beginX\n     * @param beginY\n     * @param endX\n     * @param endY\n     * @param translationDuration\n     * @param translationStartDelay\n     */\n    public void fillTranslationData(BaseDanmaku item, float beginX, float beginY,\n                                    float endX, float endY, long translationDuration, long translationStartDelay,\n                                    float scaleX, float scaleY) {\n        if (item.getType() != BaseDanmaku.TYPE_SPECIAL)\n            return;\n        ((SpecialDanmaku) item).setTranslationData(beginX * scaleX, beginY * scaleY, endX * scaleX,\n                endY * scaleY, translationDuration, translationStartDelay);\n        updateSpecicalDanmakuDuration(item);\n    }\n\n    public static void fillLinePathData(BaseDanmaku item, float[][] points, float scaleX,\n                                        float scaleY) {\n        if (item.getType() != BaseDanmaku.TYPE_SPECIAL || points.length == 0\n                || points[0].length != 2)\n            return;\n        for (int i = 0; i < points.length; i++) {\n            points[i][0] *= scaleX;\n            points[i][1] *= scaleY;\n        }\n        ((SpecialDanmaku) item).setLinePathData(points);\n    }\n\n    /**\n     * Initial alpha data of the special danmaku\n     *\n     * @param item\n     * @param beginAlpha\n     * @param endAlpha\n     * @param alphaDuraion\n     */\n    public void fillAlphaData(BaseDanmaku item, int beginAlpha, int endAlpha,\n                              long alphaDuraion) {\n        if (item.getType() != BaseDanmaku.TYPE_SPECIAL)\n            return;\n        ((SpecialDanmaku) item).setAlphaData(beginAlpha, endAlpha, alphaDuraion);\n        updateSpecicalDanmakuDuration(item);\n    }\n\n    private void updateSpecicalDanmakuDuration(BaseDanmaku item) {\n        if (MAX_Duration_Special_Danmaku == null || (item.duration != null && item.duration.value > MAX_Duration_Special_Danmaku.value)) {\n            MAX_Duration_Special_Danmaku = item.duration;\n            updateMaxDanmakuDuration();\n        }\n    }\n\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/android/Danmakus.java",
    "content": "/*\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage master.flame.danmaku.danmaku.model.android;\n\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\nimport master.flame.danmaku.danmaku.model.Danmaku;\nimport master.flame.danmaku.danmaku.model.IDanmakus;\n\npublic class Danmakus implements IDanmakus {\n\n    public Collection<BaseDanmaku> items;\n\n    private Danmakus subItems;\n\n    private BaseDanmaku startItem, endItem;\n\n    private BaseDanmaku endSubItem;\n\n    private BaseDanmaku startSubItem;\n\n    private volatile AtomicInteger mSize = new AtomicInteger(0);\n\n    private int mSortType = ST_BY_TIME;\n\n    private BaseComparator mComparator;\n\n    private boolean mDuplicateMergingEnabled;\n    private Object mLockObject = new Object();\n\n    public Danmakus() {\n        this(ST_BY_TIME, false);\n    }\n\n    public Danmakus(int sortType) {\n        this(sortType, false);\n    }\n\n    public Danmakus(int sortType, boolean duplicateMergingEnabled) {\n        this(sortType, duplicateMergingEnabled, null);\n    }\n\n    public Danmakus(int sortType, boolean duplicateMergingEnabled, BaseComparator baseComparator) {\n        BaseComparator comparator = null;\n        if (sortType == ST_BY_TIME) {\n            comparator = baseComparator == null ? new TimeComparator(duplicateMergingEnabled) : baseComparator;\n        } else if (sortType == ST_BY_YPOS) {\n            comparator = new YPosComparator(duplicateMergingEnabled);\n        } else if (sortType == ST_BY_YPOS_DESC) {\n            comparator = new YPosDescComparator(duplicateMergingEnabled);\n        }\n        if (sortType == ST_BY_LIST) {\n            items = new LinkedList<>();\n        } else {\n            mDuplicateMergingEnabled = duplicateMergingEnabled;\n            comparator.setDuplicateMergingEnabled(duplicateMergingEnabled);\n            items = new TreeSet<>(comparator);\n            mComparator = comparator;\n        }\n        mSortType = sortType;\n        mSize.set(0);\n    }\n\n    public Danmakus(Collection<BaseDanmaku> items) {\n        setItems(items);\n    }\n\n    public Danmakus(boolean duplicateMergingEnabled) {\n        this(ST_BY_TIME, duplicateMergingEnabled);\n    }\n\n    public void setItems(Collection<BaseDanmaku> items) {\n        if (mDuplicateMergingEnabled && mSortType != ST_BY_LIST) {\n            synchronized (this.mLockObject) {\n                this.items.clear();\n                this.items.addAll(items);\n                items = this.items;\n            }\n        } else {\n            this.items = items;\n        }\n        if (items instanceof List) {\n            mSortType = ST_BY_LIST;\n        }\n        mSize.set(items == null ? 0 : items.size());\n    }\n\n    @Override\n    public boolean addItem(BaseDanmaku item) {\n        synchronized (this.mLockObject) {\n            if (items != null) {\n                try {\n                    if (items.add(item)) {\n                        mSize.incrementAndGet();\n                        return true;\n                    }\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n        return false;\n    }\n\n    @Override\n    public boolean removeItem(BaseDanmaku item) {\n        if (item == null) {\n            return false;\n        }\n        if (item.isOutside()) {\n            item.setVisibility(false);\n        }\n        synchronized (this.mLockObject) {\n            if (items.remove(item)) {\n                mSize.decrementAndGet();\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private Collection<BaseDanmaku> subset(long startTime, long endTime) {\n        if (mSortType == ST_BY_LIST || items == null || items.size() == 0) {\n            return null;\n        }\n        if (subItems == null) {\n            subItems = new Danmakus(mDuplicateMergingEnabled);\n            subItems.mLockObject = this.mLockObject;\n        }\n        if (startSubItem == null) {\n            startSubItem = createItem(\"start\");\n        }\n        if (endSubItem == null) {\n            endSubItem = createItem(\"end\");\n        }\n\n        startSubItem.setTime(startTime);\n        endSubItem.setTime(endTime);\n        return ((SortedSet<BaseDanmaku>) items).subSet(startSubItem, endSubItem);\n    }\n\n    @Override\n    public IDanmakus subnew(long startTime, long endTime) {\n        Collection<BaseDanmaku> subset = subset(startTime, endTime);\n        if (subset == null || subset.isEmpty()) {\n            return null;\n        }\n        LinkedList<BaseDanmaku> newSet = new LinkedList<BaseDanmaku>(subset);\n        return new Danmakus(newSet);\n    }\n\n    @Override\n    public IDanmakus sub(long startTime, long endTime) {\n        if (items == null || items.size() == 0) {\n            return null;\n        }\n        if (subItems == null) {\n            if(mSortType == ST_BY_LIST) {\n                subItems = new Danmakus(Danmakus.ST_BY_LIST);\n                subItems.mLockObject = this.mLockObject;\n                synchronized (this.mLockObject) {\n                    subItems.setItems(items);\n                }\n            } else {\n                subItems = new Danmakus(mDuplicateMergingEnabled);\n                subItems.mLockObject = this.mLockObject;\n            }\n        }\n        if (mSortType == ST_BY_LIST) {\n            return subItems;\n        }\n        if (startItem == null) {\n            startItem = createItem(\"start\");\n        }\n        if (endItem == null) {\n            endItem = createItem(\"end\");\n        }\n\n        if (subItems != null) {\n            long dtime = startTime - startItem.getActualTime();\n            if (dtime >= 0 && endTime <= endItem.getActualTime()) {\n                return subItems;\n            }\n        }\n\n        startItem.setTime(startTime);\n        endItem.setTime(endTime);\n        synchronized (this.mLockObject) {\n            subItems.setItems(((SortedSet<BaseDanmaku>) items).subSet(startItem, endItem));\n        }\n        return subItems;\n    }\n\n    private BaseDanmaku createItem(String text) {\n        return new Danmaku(text);\n    }\n\n    public int size() {\n        return mSize.get();\n    }\n\n    @Override\n    public void clear() {\n        synchronized (this.mLockObject) {\n            if (items != null) {\n                items.clear();\n                mSize.set(0);\n            }\n        }\n        if (subItems != null) {\n            subItems = null;\n            startItem = createItem(\"start\");\n            endItem = createItem(\"end\");\n        }\n    }\n\n    @Override\n    public BaseDanmaku first() {\n        if (items != null && !items.isEmpty()) {\n            if (mSortType == ST_BY_LIST) {\n                return ((LinkedList<BaseDanmaku>) items).peek();\n            }\n            return ((SortedSet<BaseDanmaku>) items).first();\n        }\n        return null;\n    }\n\n    @Override\n    public BaseDanmaku last() {\n        if (items != null && !items.isEmpty()) {\n            if (mSortType == ST_BY_LIST) {\n                return ((LinkedList<BaseDanmaku>) items).peekLast();\n            }\n            return ((SortedSet<BaseDanmaku>) items).last();\n        }\n        return null;\n    }\n\n    @Override\n    public boolean contains(BaseDanmaku item) {\n        return this.items != null && this.items.contains(item);\n    }\n\n    @Override\n    public boolean isEmpty() {\n        return this.items == null || this.items.isEmpty();\n    }\n\n    private void setDuplicateMergingEnabled(boolean enable) {\n        mComparator.setDuplicateMergingEnabled(enable);\n        mDuplicateMergingEnabled = enable;\n    }\n\n    @Override\n    public void setSubItemsDuplicateMergingEnabled(boolean enable) {\n        mDuplicateMergingEnabled = enable;\n        startItem = endItem = null;\n        if (subItems == null) {\n            subItems = new Danmakus(enable);\n            subItems.mLockObject = this.mLockObject;\n        }\n        subItems.setDuplicateMergingEnabled(enable);\n    }\n\n    @Override\n    public Collection<BaseDanmaku> getCollection() {\n        return this.items;\n    }\n\n    @Override\n    public void forEachSync(Consumer<? super BaseDanmaku, ?> consumer) {\n        synchronized (this.mLockObject) {\n            forEach(consumer);\n        }\n    }\n\n    @Override\n    public void forEach(Consumer<? super BaseDanmaku, ?> consumer) {\n        consumer.before();\n        Iterator<BaseDanmaku> it = items.iterator();\n        while (it.hasNext()) {\n            BaseDanmaku next = it.next();\n            if (next == null) {\n                continue;\n            }\n            int action = consumer.accept(next);\n            if (action == DefaultConsumer.ACTION_BREAK) {\n                break;\n            } else if (action == DefaultConsumer.ACTION_REMOVE) {\n                it.remove();\n                mSize.decrementAndGet();\n            } else if (action == DefaultConsumer.ACTION_REMOVE_AND_BREAK) {\n                it.remove();\n                mSize.decrementAndGet();\n                break;\n            }\n        }\n        consumer.after();\n    }\n\n    @Override\n    public Object obtainSynchronizer() {\n        return mLockObject;\n    }\n\n}"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/android/DrawingCache.java",
    "content": "\r\npackage master.flame.danmaku.danmaku.model.android;\r\n\r\nimport master.flame.danmaku.danmaku.model.IDrawingCache;\r\nimport master.flame.danmaku.danmaku.model.objectpool.Poolable;\r\n\r\npublic class DrawingCache implements IDrawingCache<DrawingCacheHolder>, Poolable<DrawingCache> {\r\n\r\n    private final DrawingCacheHolder mHolder;\r\n\r\n    private int mSize = 0;\r\n\r\n    private DrawingCache mNextElement;\r\n\r\n    private boolean mIsPooled;\r\n\r\n    private int referenceCount = 0;\r\n\r\n    public DrawingCache() {\r\n        mHolder = new DrawingCacheHolder();\r\n    }\r\n\r\n    @Override\r\n    public void build(int w, int h, int density, boolean checkSizeEquals, int bitsPerPixel) {\r\n        final DrawingCacheHolder holder = mHolder;\r\n        holder.buildCache(w, h, density, checkSizeEquals, bitsPerPixel);\r\n        mSize = mHolder.bitmap.getRowBytes() * mHolder.bitmap.getHeight();\r\n    }\r\n\r\n    @Override\r\n    public void erase() {\r\n        mHolder.erase();\r\n    }\r\n\r\n    @Override\r\n    public DrawingCacheHolder get() {\r\n        final DrawingCacheHolder holder = mHolder;\r\n        if (holder.bitmap == null) {\r\n            return null;\r\n        }\r\n        return mHolder;\r\n    }\r\n\r\n    @Override\r\n    public void destroy() {\r\n        if (mHolder != null) {\r\n            mHolder.recycle();\r\n        }\r\n        mSize = 0;\r\n        referenceCount = 0;\r\n    }\r\n\r\n    @Override\r\n    public int size() {\r\n        return mSize;\r\n    }\r\n\r\n    @Override\r\n    public void setNextPoolable(DrawingCache element) {\r\n        mNextElement = element;\r\n    }\r\n\r\n    @Override\r\n    public DrawingCache getNextPoolable() {\r\n        return mNextElement;\r\n    }\r\n\r\n    @Override\r\n    public boolean isPooled() {\r\n        return mIsPooled;\r\n    }\r\n\r\n    @Override\r\n    public void setPooled(boolean isPooled) {\r\n        mIsPooled = isPooled;\r\n    }\r\n\r\n    @Override\r\n    public synchronized boolean hasReferences() {\r\n        return referenceCount > 0;\r\n    }\r\n\r\n    @Override\r\n    public synchronized void increaseReference() {\r\n        referenceCount++;\r\n    }\r\n\r\n    @Override\r\n    public synchronized void decreaseReference() {\r\n        referenceCount--;\r\n    }\r\n\r\n    @Override\r\n    public int width() {\r\n        return mHolder.width;\r\n    }\r\n\r\n    @Override\r\n    public int height() {\r\n        return mHolder.height;\r\n    }\r\n\r\n}"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/android/DrawingCacheHolder.java",
    "content": "\r\npackage master.flame.danmaku.danmaku.model.android;\r\n\r\nimport android.annotation.SuppressLint;\r\nimport android.graphics.Bitmap;\r\nimport android.graphics.Canvas;\r\nimport android.graphics.Color;\r\nimport android.graphics.Paint;\r\nimport android.graphics.Rect;\r\n\r\nimport tv.cjump.jni.NativeBitmapFactory;\r\n\r\npublic class DrawingCacheHolder {\r\n\r\n    public Canvas canvas;\r\n\r\n    public Bitmap bitmap;\r\n    \r\n    public Bitmap[][] bitmapArray;\r\n\r\n    public Object extra;\r\n\r\n    public int width;\r\n\r\n    public int height;\r\n\r\n    public boolean drawn;\r\n\r\n    private int mDensity;\r\n\r\n    public DrawingCacheHolder() {\r\n\r\n    }\r\n\r\n    public void buildCache(int w, int h, int density, boolean checkSizeEquals, int bitsPerPixel) {\r\n        boolean reuse = checkSizeEquals ? (w == width && h == height) : (w <= width && h <= height);\r\n        if (reuse && bitmap != null) {\r\n//            canvas.drawColor(Color.TRANSPARENT);\r\n//            canvas.setBitmap(null);\r\n            bitmap.eraseColor(Color.TRANSPARENT);\r\n            canvas.setBitmap(bitmap);\r\n            recycleBitmapArray();\r\n            return;\r\n        }\r\n        if (bitmap != null) {\r\n            recycle();\r\n        }\r\n        width = w;\r\n        height = h;\r\n        Bitmap.Config config = Bitmap.Config.ARGB_4444;\r\n        if (bitsPerPixel == 32) {\r\n            config = Bitmap.Config.ARGB_8888;\r\n        }\r\n        bitmap = NativeBitmapFactory.createBitmap(w, h, config);\r\n        if (density > 0) {\r\n            mDensity = density;\r\n            bitmap.setDensity(density);\r\n        }\r\n        if (canvas == null){\r\n            canvas = new Canvas(bitmap);\r\n            canvas.setDensity(density);\r\n        }else\r\n            canvas.setBitmap(bitmap);\r\n    }\r\n\r\n    public void erase() {\r\n        eraseBitmap(bitmap);\r\n        eraseBitmapArray();\r\n    }\r\n\r\n    public synchronized void recycle() {\r\n        Bitmap bitmapReserve = bitmap;\r\n        bitmap = null;\r\n        width = height = 0;\r\n        if (bitmapReserve != null) {\r\n            bitmapReserve.recycle();\r\n        }\r\n        recycleBitmapArray();\r\n        extra = null;\r\n    }\r\n\r\n    @SuppressLint(\"NewApi\")\r\n    public void splitWith(int dispWidth, int dispHeight, int maximumCacheWidth, int maximumCacheHeight) {\r\n        recycleBitmapArray();\r\n        if (width <= 0 || height <= 0 || bitmap == null) {\r\n            return;\r\n        }\r\n        if (width <= maximumCacheWidth && height <= maximumCacheHeight) {\r\n            return;\r\n        }\r\n        maximumCacheWidth = Math.min(maximumCacheWidth, dispWidth);\r\n        maximumCacheHeight = Math.min(maximumCacheHeight, dispHeight);\r\n        int xCount = width / maximumCacheWidth + (width % maximumCacheWidth == 0 ? 0 : 1);\r\n        int yCount = height / maximumCacheHeight + (height % maximumCacheHeight == 0 ? 0 : 1);\r\n        int averageWidth = width / xCount;\r\n        int averageHeight = height / yCount;\r\n        final Bitmap[][] bmpArray = new Bitmap[yCount][xCount];\r\n        if (canvas == null){\r\n            canvas = new Canvas();\r\n            if (mDensity > 0) {\r\n                canvas.setDensity(mDensity);\r\n            }\r\n        }\r\n        Rect rectSrc = new Rect();\r\n        Rect rectDst = new Rect();\r\n        for (int yIndex = 0; yIndex < yCount; yIndex++) {\r\n            for (int xIndex = 0; xIndex < xCount; xIndex++) {\r\n                Bitmap bmp = bmpArray[yIndex][xIndex] = NativeBitmapFactory.createBitmap(\r\n                        averageWidth, averageHeight, Bitmap.Config.ARGB_8888);\r\n                if (mDensity > 0) {\r\n                    bmp.setDensity(mDensity);\r\n                }\r\n                canvas.setBitmap(bmp);\r\n                int left = xIndex * averageWidth, top = yIndex * averageHeight;\r\n                rectSrc.set(left, top, left + averageWidth, top + averageHeight);\r\n                rectDst.set(0, 0, bmp.getWidth(), bmp.getHeight());\r\n                canvas.drawBitmap(bitmap, rectSrc, rectDst, null);\r\n            }\r\n        }\r\n        canvas.setBitmap(bitmap);\r\n        bitmapArray = bmpArray;\r\n    }\r\n\r\n    private void eraseBitmap(Bitmap bmp) {\r\n        if (bmp != null) {\r\n            bmp.eraseColor(Color.TRANSPARENT);\r\n        }\r\n    }\r\n\r\n    private void eraseBitmapArray() {\r\n        if (bitmapArray != null) {\r\n            for (int i = 0; i < bitmapArray.length; i++) {\r\n                for (int j = 0; j < bitmapArray[i].length; j++) {\r\n                    eraseBitmap(bitmapArray[i][j]);\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    private void recycleBitmapArray() {\r\n        Bitmap[][] bitmapArrayReserve = bitmapArray;\r\n        bitmapArray = null;\r\n        if (bitmapArrayReserve != null) {\r\n            for (int i = 0; i < bitmapArrayReserve.length; i++) {\r\n                for (int j = 0; j < bitmapArrayReserve[i].length; j++) {\r\n                    if (bitmapArrayReserve[i][j] != null) {\r\n                        bitmapArrayReserve[i][j].recycle();\r\n                        bitmapArrayReserve[i][j] = null;\r\n                    }\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    public final synchronized boolean draw(Canvas canvas, float left, float top, Paint paint) {\r\n        if (bitmapArray != null) {\r\n            for (int i = 0; i < bitmapArray.length; i++) {\r\n                for (int j = 0; j < bitmapArray[i].length; j++) {\r\n                    Bitmap bmp = bitmapArray[i][j];\r\n                    if (bmp != null) {\r\n                        float dleft = left + j * bmp.getWidth();\r\n                        if (dleft > canvas.getWidth() || dleft + bmp.getWidth() < 0) {\r\n                            continue;\r\n                        }\r\n                        float dtop = top + i * bmp.getHeight();\r\n                        if (dtop > canvas.getHeight() || dtop + bmp.getHeight() < 0) {\r\n                            continue;\r\n                        }\r\n                        canvas.drawBitmap(bmp, dleft, dtop, paint);\r\n                    }\r\n                }\r\n            }\r\n            return true;\r\n        } else if (bitmap != null) {\r\n            canvas.drawBitmap(bitmap, left, top, paint);\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/android/DrawingCachePoolManager.java",
    "content": "\r\npackage master.flame.danmaku.danmaku.model.android;\r\n\r\nimport master.flame.danmaku.danmaku.model.objectpool.PoolableManager;\r\n\r\npublic class DrawingCachePoolManager implements PoolableManager<DrawingCache> {\r\n\r\n    @Override\r\n    public DrawingCache newInstance() {\r\n        return null;\r\n    }\r\n\r\n    @Override\r\n    public void onAcquired(DrawingCache element) {\r\n\r\n    }\r\n\r\n    @Override\r\n    public void onReleased(DrawingCache element) {\r\n\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/android/SimpleTextCacheStuffer.java",
    "content": "package master.flame.danmaku.danmaku.model.android;\n\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.text.TextPaint;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\nimport master.flame.danmaku.danmaku.model.SpecialDanmaku;\n\n/**\n * Created by ch on 15-7-16.\n */\npublic class SimpleTextCacheStuffer extends BaseCacheStuffer {\n\n    private final static Map<Float, Float> sTextHeightCache = new HashMap<Float, Float>();\n\n    protected Float getCacheHeight(BaseDanmaku danmaku, Paint paint) {\n        Float textSize = paint.getTextSize();\n        Float textHeight = sTextHeightCache.get(textSize);\n        if (textHeight == null) {\n            Paint.FontMetrics fontMetrics = paint.getFontMetrics();\n            textHeight = fontMetrics.descent - fontMetrics.ascent + fontMetrics.leading;\n            sTextHeightCache.put(textSize, textHeight);\n        }\n        return textHeight;\n    }\n\n    @Override\n    public void measure(BaseDanmaku danmaku, TextPaint paint, boolean fromWorkerThread) {\n        float w = 0;\n        Float textHeight = 0f;\n        if (danmaku.lines == null) {\n            if (danmaku.text == null) {\n                w = 0;\n            } else {\n                w = paint.measureText(danmaku.text.toString());\n                textHeight = getCacheHeight(danmaku, paint);\n            }\n            danmaku.paintWidth = w;\n            danmaku.paintHeight = textHeight;\n        } else {\n            textHeight = getCacheHeight(danmaku, paint);\n            for (String tempStr : danmaku.lines) {\n                if (tempStr.length() > 0) {\n                    float tr = paint.measureText(tempStr);\n                    w = Math.max(tr, w);\n                }\n            }\n            danmaku.paintWidth = w;\n            danmaku.paintHeight = danmaku.lines.length * textHeight;\n        }\n    }\n\n    protected void drawStroke(BaseDanmaku danmaku, String lineText, Canvas canvas, float left, float top, Paint paint) {\n        if (lineText != null) {\n            canvas.drawText(lineText, left, top, paint);\n        } else {\n            canvas.drawText(danmaku.text.toString(), left, top, paint);\n        }\n    }\n\n    protected void drawText(BaseDanmaku danmaku, String lineText, Canvas canvas, float left, float top, TextPaint paint, boolean fromWorkerThread) {\n        if (fromWorkerThread && danmaku instanceof SpecialDanmaku) {\n            paint.setAlpha(255);\n        }\n        if (lineText != null) {\n            canvas.drawText(lineText, left, top, paint);\n        } else {\n            canvas.drawText(danmaku.text.toString(), left, top, paint);\n        }\n    }\n\n    @Override\n    public void clearCaches() {\n        sTextHeightCache.clear();\n    }\n\n    protected void drawBackground(BaseDanmaku danmaku, Canvas canvas, float left, float top) {\n\n    }\n\n    @Override\n    public void drawDanmaku(BaseDanmaku danmaku, Canvas canvas, float left, float top, boolean fromWorkerThread, AndroidDisplayer.DisplayerConfig displayerConfig) {\n        float _left = left;\n        float _top = top;\n        left += danmaku.padding;\n        top += danmaku.padding;\n        if (danmaku.borderColor != 0) {\n            left += displayerConfig.BORDER_WIDTH;\n            top += displayerConfig.BORDER_WIDTH;\n        }\n\n        displayerConfig.definePaintParams(fromWorkerThread);\n        TextPaint paint = displayerConfig.getPaint(danmaku, fromWorkerThread);\n        drawBackground(danmaku, canvas, _left, _top);\n        if (danmaku.lines != null) {\n            String[] lines = danmaku.lines;\n            if (lines.length == 1) {\n                if (displayerConfig.hasStroke(danmaku)) {\n                    displayerConfig.applyPaintConfig(danmaku, paint, true);\n                    float strokeLeft = left;\n                    float strokeTop = top - paint.ascent();\n                    if (displayerConfig.HAS_PROJECTION) {\n                        strokeLeft += displayerConfig.sProjectionOffsetX;\n                        strokeTop += displayerConfig.sProjectionOffsetY;\n                    }\n                    drawStroke(danmaku, lines[0], canvas, strokeLeft, strokeTop, paint);\n                }\n                displayerConfig.applyPaintConfig(danmaku, paint, false);\n                drawText(danmaku, lines[0], canvas, left, top - paint.ascent(), paint, fromWorkerThread);\n            } else {\n                float textHeight = (danmaku.paintHeight - 2 * danmaku.padding) / lines.length;\n                for (int t = 0; t < lines.length; t++) {\n                    if (lines[t] == null || lines[t].length() == 0) {\n                        continue;\n                    }\n                    if (displayerConfig.hasStroke(danmaku)) {\n                        displayerConfig.applyPaintConfig(danmaku, paint, true);\n                        float strokeLeft = left;\n                        float strokeTop = t * textHeight + top - paint.ascent();\n                        if (displayerConfig.HAS_PROJECTION) {\n                            strokeLeft += displayerConfig.sProjectionOffsetX;\n                            strokeTop += displayerConfig.sProjectionOffsetY;\n                        }\n                        drawStroke(danmaku, lines[t], canvas, strokeLeft, strokeTop, paint);\n                    }\n                    displayerConfig.applyPaintConfig(danmaku, paint, false);\n                    drawText(danmaku, lines[t], canvas, left, t * textHeight + top - paint.ascent(), paint, fromWorkerThread);\n                }\n            }\n        } else {\n            if (displayerConfig.hasStroke(danmaku)) {\n                displayerConfig.applyPaintConfig(danmaku, paint, true);\n                float strokeLeft = left;\n                float strokeTop = top - paint.ascent();\n\n                if (displayerConfig.HAS_PROJECTION) {\n                    strokeLeft += displayerConfig.sProjectionOffsetX;\n                    strokeTop += displayerConfig.sProjectionOffsetY;\n                }\n                drawStroke(danmaku, null, canvas, strokeLeft, strokeTop, paint);\n            }\n\n            displayerConfig.applyPaintConfig(danmaku, paint, false);\n            drawText(danmaku, null, canvas, left, top - paint.ascent(), paint, fromWorkerThread);\n        }\n\n        // draw underline\n        if (danmaku.underlineColor != 0) {\n            Paint linePaint = displayerConfig.getUnderlinePaint(danmaku);\n            float bottom = _top + danmaku.paintHeight - displayerConfig.UNDERLINE_HEIGHT;\n            canvas.drawLine(_left, bottom, _left + danmaku.paintWidth, bottom, linePaint);\n        }\n\n        //draw border\n        if (danmaku.borderColor != 0) {\n            Paint borderPaint = displayerConfig.getBorderPaint(danmaku);\n            canvas.drawRect(_left, _top, _left + danmaku.paintWidth, _top + danmaku.paintHeight,\n                    borderPaint);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/android/SpannedCacheStuffer.java",
    "content": "package master.flame.danmaku.danmaku.model.android;\n\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.text.Layout;\nimport android.text.Spanned;\nimport android.text.StaticLayout;\nimport android.text.TextPaint;\n\nimport java.lang.ref.SoftReference;\n\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\n\n/**\n * Created by ch on 15-7-16.\n */\npublic class SpannedCacheStuffer extends SimpleTextCacheStuffer {\n\n    @Override\n    public void measure(BaseDanmaku danmaku, TextPaint paint, boolean fromWorkerThread) {\n        if (danmaku.text instanceof Spanned) {\n            CharSequence text = danmaku.text;\n            if (text != null) {\n                StaticLayout staticLayout = new StaticLayout(text, paint, (int) Math.ceil(StaticLayout.getDesiredWidth(danmaku.text, paint)), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true);\n                danmaku.paintWidth = staticLayout.getWidth();\n                danmaku.paintHeight = staticLayout.getHeight();\n                danmaku.obj = new SoftReference<>(staticLayout);\n                return;\n            }\n        }\n        super.measure(danmaku, paint, fromWorkerThread);\n    }\n\n    @Override\n    public void drawStroke(BaseDanmaku danmaku, String lineText, Canvas canvas, float left, float top, Paint paint) {\n        if (danmaku.obj == null) {\n            super.drawStroke(danmaku, lineText, canvas, left, top, paint);\n        }\n    }\n\n    @Override\n    public void drawText(BaseDanmaku danmaku, String lineText, Canvas canvas, float left, float top, TextPaint paint, boolean fromWorkerThread) {\n        if (danmaku.obj == null) {\n            super.drawText(danmaku, lineText, canvas, left, top, paint, fromWorkerThread);\n            return;\n        }\n        SoftReference<StaticLayout> reference = (SoftReference<StaticLayout>) danmaku.obj;\n        StaticLayout staticLayout = reference.get();\n        boolean requestRemeasure = 0 != (danmaku.requestFlags & BaseDanmaku.FLAG_REQUEST_REMEASURE);\n        boolean requestInvalidate = 0 != (danmaku.requestFlags & BaseDanmaku.FLAG_REQUEST_INVALIDATE);\n\n        if (requestInvalidate || staticLayout == null) {\n            if (requestInvalidate) {\n                danmaku.requestFlags &= ~BaseDanmaku.FLAG_REQUEST_INVALIDATE;\n            }\n            CharSequence text = danmaku.text;\n            if (text != null) {\n                if (requestRemeasure) {\n                    staticLayout = new StaticLayout(text, paint, (int) Math.ceil(StaticLayout.getDesiredWidth(danmaku.text, paint)), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true);\n                    danmaku.paintWidth = staticLayout.getWidth();\n                    danmaku.paintHeight = staticLayout.getHeight();\n                    danmaku.requestFlags &= ~BaseDanmaku.FLAG_REQUEST_REMEASURE;\n                } else {\n                    staticLayout = new StaticLayout(text, paint, (int) danmaku.paintWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true);\n                }\n                danmaku.obj = new SoftReference<>(staticLayout);\n            } else {\n                return;\n            }\n        }\n        boolean needRestore = false;\n        if (left != 0 && top != 0) {\n            canvas.save();\n            canvas.translate(left, top + paint.ascent());\n            needRestore = true;\n        }\n        staticLayout.draw(canvas);\n        if (needRestore) {\n            canvas.restore();\n        }\n    }\n\n    @Override\n    public void clearCaches() {\n        super.clearCaches();\n        System.gc();\n    }\n\n    @Override\n    public void clearCache(BaseDanmaku danmaku) {\n        super.clearCache(danmaku);\n        if (danmaku.obj instanceof SoftReference<?>) {\n            ((SoftReference<?>) danmaku.obj).clear();\n        }\n    }\n\n    @Override\n    public void releaseResource(BaseDanmaku danmaku) {\n        clearCache(danmaku);\n        super.releaseResource(danmaku);\n    }\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/android/ViewCacheStuffer.java",
    "content": "package master.flame.danmaku.danmaku.model.android;\n\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.text.TextPaint;\nimport android.util.SparseArray;\nimport android.view.View;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\n\npublic abstract class ViewCacheStuffer<VH extends ViewCacheStuffer.ViewHolder> extends BaseCacheStuffer {\n\n    public static abstract class ViewHolder {\n\n        protected final View itemView;\n\n        public ViewHolder(View itemView) {\n            if (itemView == null) {\n                throw new IllegalArgumentException(\"itemView may not be null\");\n            }\n            this.itemView = itemView;\n        }\n\n        public void measure(int widthMeasureSpec, int heightMeasureSpec) {\n            this.itemView.measure(widthMeasureSpec, heightMeasureSpec);\n        }\n\n        public int getMeasureWidth() {\n            return this.itemView.getMeasuredWidth();\n        }\n\n        public int getMeasureHeight() {\n            return this.itemView.getMeasuredHeight();\n        }\n\n        public void layout(int l, int t, int r, int b) {\n            this.itemView.layout(l, t, r, b);\n        }\n\n        public void draw(Canvas canvas, AndroidDisplayer.DisplayerConfig displayerConfig) {\n            this.itemView.draw(canvas);\n            //TODO: apply displayerConfig\n        }\n    }\n\n    public static final int INVALID_TYPE = -1;\n    public static final int MEASURE_VIEW_TYPE = -2;\n    public static final int DRAW_VIEW_TYPE = -3;\n    public static final int CACHE_VIEW_TYPE = -3;\n\n    private final int mMaximumWidthPixels;\n    private final int mMaximumHeightPixels;\n    private SparseArray<List<VH>> mViewHolderArray = new SparseArray();\n\n    public abstract VH onCreateViewHolder(int viewType);\n\n    public abstract void onBindViewHolder(int viewType, VH viewHolder, BaseDanmaku danmaku, AndroidDisplayer.DisplayerConfig displayerConfig, TextPaint paint);\n\n    public int getItemViewType(int position, BaseDanmaku danmaku) {\n        return 0;\n    }\n\n    public ViewCacheStuffer() {\n        mMaximumWidthPixels = -1;  // FIXME: get maximum of canvas\n        mMaximumHeightPixels = -1;\n    }\n\n    @Override\n    public void measure(BaseDanmaku danmaku, TextPaint paint, boolean fromWorkerThread) {\n        int viewType = getItemViewType(danmaku.index, danmaku);\n        List<VH> viewHolders = mViewHolderArray.get(viewType);\n        if (viewHolders == null) {\n            viewHolders = new ArrayList<>();\n            viewHolders.add(onCreateViewHolder(viewType));\n            viewHolders.add(onCreateViewHolder(viewType));\n            viewHolders.add(onCreateViewHolder(viewType));\n            mViewHolderArray.put(viewType, viewHolders);\n        }\n        VH viewHolder = viewHolders.get(0);\n        // TODO displayerConfig or TextPaint\n        onBindViewHolder(viewType, viewHolder, danmaku, null, paint);\n        viewHolder.measure(View.MeasureSpec.makeMeasureSpec(mMaximumWidthPixels, View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(mMaximumHeightPixels, View.MeasureSpec.AT_MOST));\n        viewHolder.layout(0, 0, viewHolder.getMeasureWidth(), viewHolder.getMeasureHeight());\n        danmaku.paintWidth = viewHolder.getMeasureWidth();\n        danmaku.paintHeight = viewHolder.getMeasureHeight();\n    }\n\n    @Override\n    public void clearCaches() {\n\n    }\n\n    @Override\n    public void releaseResource(BaseDanmaku danmaku) {\n        super.releaseResource(danmaku);\n        danmaku.tag = null;\n    }\n\n    @Override\n    public void drawDanmaku(BaseDanmaku danmaku, Canvas canvas, float left, float top, boolean fromWorkerThread, AndroidDisplayer.DisplayerConfig displayerConfig) {\n        int viewType = getItemViewType(danmaku.index, danmaku);\n        List<VH> viewHolders = mViewHolderArray.get(viewType);\n        VH viewHolder = null;\n        if (viewHolders != null) {\n            viewHolder = viewHolders.get(fromWorkerThread ? 1 : 2);\n        }\n        if (viewHolder == null) {\n            return;\n        }\n        //ignore danmaku.padding, apply it onBindViewHolder\n        displayerConfig.definePaintParams(fromWorkerThread);\n        TextPaint paint = displayerConfig.getPaint(danmaku, fromWorkerThread);\n        displayerConfig.applyPaintConfig(danmaku, paint, false);\n\n        onBindViewHolder(viewType, viewHolder, danmaku, displayerConfig, paint);\n        viewHolder.measure(View.MeasureSpec.makeMeasureSpec(Math.round(danmaku.paintWidth), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(Math.round(danmaku.paintHeight), View.MeasureSpec.EXACTLY));\n        boolean needRestore = false;\n        if (!fromWorkerThread) {\n            canvas.save();\n            canvas.translate(left, top);\n            needRestore = true;\n        }\n        // draw underline\n        if (danmaku.underlineColor != 0) {\n            Paint linePaint = displayerConfig.getUnderlinePaint(danmaku);\n            float bottom = top + danmaku.paintHeight - displayerConfig.UNDERLINE_HEIGHT;\n            canvas.drawLine(left, bottom, left + danmaku.paintWidth, bottom, linePaint);\n        }\n        //draw border\n        if (danmaku.borderColor != 0) {\n            Paint borderPaint = displayerConfig.getBorderPaint(danmaku);\n            canvas.drawRect(left, top, left + danmaku.paintWidth, top + danmaku.paintHeight,\n                    borderPaint);\n        }\n        //draw danmaku\n        viewHolder.layout(0, 0, (int) danmaku.paintWidth, (int) danmaku.paintHeight);\n        viewHolder.draw(canvas, displayerConfig); //FIXME: handle canvas.getMaximumBitmapWidth()\n        //TODO: stroke handle displayerConfig\n        if (needRestore) {\n            canvas.restore();\n        }\n    }\n\n}"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/objectpool/FinitePool.java",
    "content": "/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage master.flame.danmaku.danmaku.model.objectpool;\n\nclass FinitePool<T extends Poolable<T>> implements Pool<T> {\n    /** Factory used to create new pool objects */\n    private final PoolableManager<T> mManager;\n\n    /** Maximum number of objects in the pool */\n    private final int mLimit;\n\n    /** If true, mLimit is ignored */\n    private final boolean mInfinite;\n\n    /** Next object to acquire */\n    private T mRoot;\n\n    /** Number of objects in the pool */\n    private int mPoolCount;\n\n    FinitePool(PoolableManager<T> manager) {\n        mManager = manager;\n        mLimit = 0;\n        mInfinite = true;\n    }\n\n    FinitePool(PoolableManager<T> manager, int limit) {\n        if (limit <= 0) {\n            throw new IllegalArgumentException(\"The pool limit must be > 0\");\n        }\n\n        mManager = manager;\n        mLimit = limit;\n        mInfinite = false;\n    }\n\n    public T acquire() {\n        T element;\n\n        if (mRoot != null) {\n            element = mRoot;\n            mRoot = element.getNextPoolable();\n            mPoolCount--;\n        } else {\n            element = mManager.newInstance();\n        }\n\n        if (element != null) {\n            element.setNextPoolable(null);\n            element.setPooled(false);\n            mManager.onAcquired(element);\n        }\n\n        return element;\n    }\n\n    public void release(T element) {\n        if (!element.isPooled()) {\n            if (mInfinite || mPoolCount < mLimit) {\n                mPoolCount++;\n                element.setNextPoolable(mRoot);\n                element.setPooled(true);\n                mRoot = element;\n            }\n            mManager.onReleased(element);\n        } else {\n            System.out.print(\"[FinitePool] Element is already in pool: \" + element);\n        }\n    }\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/objectpool/Pool.java",
    "content": "/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage master.flame.danmaku.danmaku.model.objectpool;\n\npublic interface Pool<T extends Poolable<T>> {\n    T acquire();\n\n    void release(T element);\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/objectpool/Poolable.java",
    "content": "/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage master.flame.danmaku.danmaku.model.objectpool;\n\npublic interface Poolable<T> {\n    void setNextPoolable(T element);\n\n    T getNextPoolable();\n\n    boolean isPooled();\n\n    void setPooled(boolean isPooled);\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/objectpool/PoolableManager.java",
    "content": "/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage master.flame.danmaku.danmaku.model.objectpool;\n\npublic interface PoolableManager<T extends Poolable<T>> {\n    T newInstance();\n\n    void onAcquired(T element);\n\n    void onReleased(T element);\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/objectpool/Pools.java",
    "content": "/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage master.flame.danmaku.danmaku.model.objectpool;\n\npublic class Pools {\n    private Pools() {\n    }\n\n    public static <T extends Poolable<T>> Pool<T> simplePool(PoolableManager<T> manager) {\n        return new FinitePool<T>(manager);\n    }\n\n    public static <T extends Poolable<T>> Pool<T> finitePool(PoolableManager<T> manager, int limit) {\n        return new FinitePool<T>(manager, limit);\n    }\n\n    public static <T extends Poolable<T>> Pool<T> synchronizedPool(Pool<T> pool) {\n        return new SynchronizedPool<T>(pool);\n    }\n\n    public static <T extends Poolable<T>> Pool<T> synchronizedPool(Pool<T> pool, Object lock) {\n        return new SynchronizedPool<T>(pool, lock);\n    }\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/model/objectpool/SynchronizedPool.java",
    "content": "/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage master.flame.danmaku.danmaku.model.objectpool;\n\nclass SynchronizedPool<T extends Poolable<T>> implements Pool<T> {\n    private final Pool<T> mPool;\n\n    private final Object mLock;\n\n    public SynchronizedPool(Pool<T> pool) {\n        mPool = pool;\n        mLock = this;\n    }\n\n    public SynchronizedPool(Pool<T> pool, Object lock) {\n        mPool = pool;\n        mLock = lock;\n    }\n\n    public T acquire() {\n        synchronized (mLock) {\n            return mPool.acquire();\n        }\n    }\n\n    public void release(T element) {\n        synchronized (mLock) {\n            mPool.release(element);\n        }\n    }\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/parser/BaseDanmakuParser.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.parser;\r\n\r\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\r\nimport master.flame.danmaku.danmaku.model.DanmakuTimer;\r\nimport master.flame.danmaku.danmaku.model.IDanmakus;\r\nimport master.flame.danmaku.danmaku.model.IDisplayer;\r\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext;\r\n\r\n/**\r\n *\r\n */\r\npublic abstract class BaseDanmakuParser {\r\n\r\n    public interface Listener {\r\n        void onDanmakuAdd(BaseDanmaku danmaku);\r\n    }\r\n\r\n    protected IDataSource<?> mDataSource;\r\n\r\n    protected DanmakuTimer mTimer;\r\n    protected int mDispWidth;\r\n    protected int mDispHeight;\r\n    protected float mDispDensity;\r\n    protected float mScaledDensity;\r\n\r\n    private IDanmakus mDanmakus;\r\n\r\n    protected IDisplayer mDisp;\r\n    protected DanmakuContext mContext;\r\n    protected Listener mListener;\r\n\r\n    public BaseDanmakuParser setDisplayer(IDisplayer disp){\r\n        mDisp = disp;\r\n    \tmDispWidth = disp.getWidth();\r\n        mDispHeight = disp.getHeight();\r\n        mDispDensity = disp.getDensity();\r\n        mScaledDensity = disp.getScaledDensity();\r\n        mContext.mDanmakuFactory.updateViewportState(mDispWidth, mDispHeight, getViewportSizeFactor());\r\n        mContext.mDanmakuFactory.updateMaxDanmakuDuration();\r\n        return this;\r\n    }\r\n\r\n    public IDisplayer getDisplayer(){\r\n        return mDisp;\r\n    }\r\n\r\n    public BaseDanmakuParser setListener(Listener listener) {\r\n        mListener = listener;\r\n        return this;\r\n    }\r\n    \r\n    /**\r\n     * decide the speed of scroll-danmakus\r\n     * @return\r\n     */\r\n    protected float getViewportSizeFactor() {\r\n        return 1 / (mDispDensity - 0.6f);\r\n    }\r\n    \r\n    public BaseDanmakuParser load(IDataSource<?> source) {\r\n        mDataSource = source;\r\n        return this;\r\n    }\r\n    \r\n    public BaseDanmakuParser setTimer(DanmakuTimer timer) {\r\n        mTimer = timer;\r\n        return this;\r\n    }\r\n\r\n    public DanmakuTimer getTimer() {\r\n        return mTimer;\r\n    }\r\n    \r\n    public IDanmakus getDanmakus() {\r\n        if (mDanmakus != null)\r\n            return mDanmakus;\r\n        mContext.mDanmakuFactory.resetDurationsData();\r\n        mDanmakus = parse();\r\n        releaseDataSource();\r\n        mContext.mDanmakuFactory.updateMaxDanmakuDuration();\r\n        return mDanmakus;\r\n    }\r\n    \r\n    protected void releaseDataSource() {\r\n        if(mDataSource!=null)\r\n            mDataSource.release();\r\n        mDataSource = null;\r\n    }\r\n\r\n    protected abstract IDanmakus parse();\r\n\r\n    public void release() {\r\n        releaseDataSource();\r\n    }\r\n\r\n    public BaseDanmakuParser setConfig(DanmakuContext config) {\r\n        mContext = config;\r\n        return this;\r\n    }\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/parser/IDataSource.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.parser;\r\n\r\npublic interface IDataSource<T> {\r\n    String SCHEME_HTTP_TAG = \"http\";\r\n    String SCHEME_HTTPS_TAG = \"https\";\r\n    String SCHEME_FILE_TAG = \"file\";\r\n    \r\n\tpublic T data();\r\n\t\r\n    public void release();\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/parser/android/AndroidFileSource.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.parser.android;\r\n\r\nimport master.flame.danmaku.danmaku.parser.IDataSource;\r\nimport master.flame.danmaku.danmaku.util.IOUtils;\r\n\r\nimport android.net.Uri;\r\n\r\nimport java.io.*;\r\nimport java.net.MalformedURLException;\r\nimport java.net.URL;\r\n\r\npublic class AndroidFileSource implements IDataSource<InputStream> {\r\n\r\n    private InputStream inStream;\r\n\r\n    public AndroidFileSource(String filepath) {\r\n        fillStreamFromFile(new File(filepath));\r\n    }\r\n\r\n    public AndroidFileSource(Uri uri) {\r\n        fillStreamFromUri(uri);\r\n    }\r\n\r\n    public AndroidFileSource(File file) {\r\n        fillStreamFromFile(file);\r\n    }\r\n\r\n    public AndroidFileSource(InputStream stream) {\r\n        this.inStream = stream;\r\n    }\r\n\r\n    public void fillStreamFromFile(File file) {\r\n        try {\r\n            inStream = new BufferedInputStream(new FileInputStream(file));\r\n        } catch (FileNotFoundException e) {\r\n            e.printStackTrace();\r\n        }\r\n    }\r\n\r\n    public void fillStreamFromUri(Uri uri) {\r\n        String scheme = uri.getScheme();\r\n        if (SCHEME_HTTP_TAG.equalsIgnoreCase(scheme) || SCHEME_HTTPS_TAG.equalsIgnoreCase(scheme)) {\r\n            fillStreamFromHttpFile(uri);\r\n        } else if (SCHEME_FILE_TAG.equalsIgnoreCase(scheme)) {\r\n            fillStreamFromFile(new File(uri.getPath()));\r\n        }\r\n    }\r\n\r\n    public void fillStreamFromHttpFile(Uri uri) {\r\n        try {\r\n            URL url = new URL(uri.getPath());\r\n            url.openConnection();\r\n            inStream = new BufferedInputStream(url.openStream());\r\n\r\n        } catch (MalformedURLException e) {\r\n            e.printStackTrace();\r\n        } catch (IOException e) {\r\n            e.printStackTrace();\r\n        }\r\n\r\n    }\r\n\r\n    @Override\r\n    public void release() {\r\n        IOUtils.closeQuietly(inStream);\r\n        inStream = null;\r\n    }\r\n\r\n\t@Override\r\n\tpublic InputStream data() {\r\n\t\treturn inStream;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/parser/android/JSONSource.java",
    "content": "package master.flame.danmaku.danmaku.parser.android;\r\n\r\nimport java.io.File;\r\nimport java.io.FileInputStream;\r\nimport java.io.FileNotFoundException;\r\nimport java.io.IOException;\r\nimport java.io.InputStream;\r\nimport java.net.URL;\r\n\r\nimport master.flame.danmaku.danmaku.parser.IDataSource;\r\nimport master.flame.danmaku.danmaku.util.IOUtils;\r\n\r\nimport org.json.JSONArray;\r\nimport org.json.JSONException;\r\n\r\nimport android.net.Uri;\r\nimport android.text.TextUtils;\r\n\r\n/**\r\n * a json file source\r\n * @author yrom\r\n */\r\npublic class JSONSource implements IDataSource<JSONArray>{\r\n\tprivate JSONArray mJSONArray;\r\n\tprivate InputStream mInput;\r\n\tpublic JSONSource(String json) throws JSONException{\r\n\t\tinit(json);\r\n\t}\r\n\t\r\n\tpublic JSONSource(InputStream in) throws JSONException{\r\n\t\tinit(in);\r\n\t}\r\n\t\r\n\tprivate void init(InputStream in) throws JSONException {\r\n\t\tif(in == null)\r\n\t\t\tthrow new NullPointerException(\"input stream cannot be null!\");\r\n\t\tmInput = in;\r\n\t\tString json = IOUtils.getString(mInput);\r\n\t\tinit(json);\r\n\t}\r\n\t\r\n\tpublic JSONSource(URL url) throws JSONException, IOException{\r\n\t\tthis(url.openStream());\r\n\t}\r\n\t\r\n\tpublic JSONSource(File file) throws FileNotFoundException, JSONException{\r\n\t\tinit(new FileInputStream(file));\r\n\t}\r\n\t\r\n\tpublic JSONSource(Uri uri) throws IOException, JSONException {\r\n\t\tString scheme = uri.getScheme();\r\n        if (SCHEME_HTTP_TAG.equalsIgnoreCase(scheme) || SCHEME_HTTPS_TAG.equalsIgnoreCase(scheme)) {\r\n            init(new URL(uri.getPath()).openStream());\r\n        } else if (SCHEME_FILE_TAG.equalsIgnoreCase(scheme)) {\r\n            init(new FileInputStream(uri.getPath()));\r\n        }\r\n\t}\r\n\t\r\n\tprivate void init(String json) throws JSONException {\r\n\t\tif(!TextUtils.isEmpty(json)){\r\n\t\t\tmJSONArray = new JSONArray(json);\r\n\t\t}\r\n\t}\r\n    public JSONArray data(){\r\n    \treturn mJSONArray;\r\n    }\r\n\r\n\t@Override\r\n\tpublic void release() {\r\n\t\tIOUtils.closeQuietly(mInput);\r\n\t\tmInput = null;\r\n\t\tmJSONArray = null;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/renderer/IRenderer.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.renderer;\r\n\r\n\r\nimport master.flame.danmaku.danmaku.model.DanmakuTimer;\r\nimport master.flame.danmaku.danmaku.model.ICacheManager;\r\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\r\nimport master.flame.danmaku.danmaku.model.IDanmakus;\r\nimport master.flame.danmaku.danmaku.model.IDisplayer;\r\nimport master.flame.danmaku.danmaku.model.android.Danmakus;\r\n\r\npublic interface IRenderer {\r\n    \r\n    int NOTHING_RENDERING = 0;\r\n    int CACHE_RENDERING = 1;\r\n    int TEXT_RENDERING = 2;\r\n\r\n    interface OnDanmakuShownListener {\r\n        void onDanmakuShown(BaseDanmaku danmaku);\r\n    }\r\n\r\n    class Area {\r\n\r\n        public final float[] mRefreshRect = new float[4];\r\n        private int mMaxHeight;\r\n        private int mMaxWidth;\r\n\r\n        public void setEdge(int maxWidth, int maxHeight) {\r\n            mMaxWidth = maxWidth;\r\n            mMaxHeight = maxHeight;\r\n        }\r\n\r\n        public void reset() {\r\n            set(mMaxWidth, mMaxHeight, 0, 0);\r\n        }\r\n\r\n        public void resizeToMax() {\r\n            set(0, 0, mMaxWidth, mMaxHeight);\r\n        }\r\n\r\n        public void set(float left, float top, float right, float bottom) {\r\n            mRefreshRect[0] = left;\r\n            mRefreshRect[1] = top;\r\n            mRefreshRect[2] = right;\r\n            mRefreshRect[3] = bottom;\r\n        }\r\n\r\n    }\r\n\r\n    public class RenderingState {\r\n        public final static int UNKNOWN_TIME = -1;\r\n\r\n        public boolean isRunningDanmakus;\r\n        public DanmakuTimer timer = new DanmakuTimer();\r\n        public int indexInScreen;\r\n        public int totalSizeInScreen;\r\n        public BaseDanmaku lastDanmaku;\r\n\r\n        public int r2lDanmakuCount;\r\n        public int l2rDanmakuCount;\r\n        public int ftDanmakuCount;\r\n        public int fbDanmakuCount;\r\n        public int specialDanmakuCount;\r\n        public int totalDanmakuCount;\r\n        public int lastTotalDanmakuCount;\r\n        public long consumingTime;\r\n        public long beginTime;\r\n        public long endTime;\r\n        public boolean nothingRendered;\r\n        public long sysTime;\r\n        public long cacheHitCount;\r\n        public long cacheMissCount;\r\n\r\n        private IDanmakus runningDanmakus = new Danmakus(Danmakus.ST_BY_LIST);\r\n        private boolean mIsObtaining;\r\n\r\n        public int addTotalCount(int count) {\r\n            totalDanmakuCount += count;\r\n            return totalDanmakuCount;\r\n        }\r\n\r\n        public int addCount(int type, int count) {\r\n            switch (type) {\r\n                case BaseDanmaku.TYPE_SCROLL_RL:\r\n                    r2lDanmakuCount += count;\r\n                    return r2lDanmakuCount;\r\n                case BaseDanmaku.TYPE_SCROLL_LR:\r\n                    l2rDanmakuCount += count;\r\n                    return l2rDanmakuCount;\r\n                case BaseDanmaku.TYPE_FIX_TOP:\r\n                    ftDanmakuCount += count;\r\n                    return ftDanmakuCount;\r\n                case BaseDanmaku.TYPE_FIX_BOTTOM:\r\n                    fbDanmakuCount += count;\r\n                    return fbDanmakuCount;\r\n                case BaseDanmaku.TYPE_SPECIAL:\r\n                    specialDanmakuCount += count;\r\n                    return specialDanmakuCount;\r\n            }\r\n            return 0;\r\n        }\r\n\r\n        public void reset() {\r\n            lastTotalDanmakuCount = totalDanmakuCount;\r\n            r2lDanmakuCount = l2rDanmakuCount = ftDanmakuCount = fbDanmakuCount = specialDanmakuCount = totalDanmakuCount = 0;\r\n            sysTime = beginTime = endTime = consumingTime = 0;\r\n            nothingRendered = false;\r\n            synchronized (this) {\r\n                runningDanmakus.clear();\r\n            }\r\n        }\r\n\r\n        public void set(RenderingState other) {\r\n            if(other == null)\r\n                return;\r\n            lastTotalDanmakuCount = other.lastTotalDanmakuCount;\r\n            r2lDanmakuCount = other.r2lDanmakuCount;\r\n            l2rDanmakuCount = other.l2rDanmakuCount;\r\n            ftDanmakuCount = other.ftDanmakuCount;\r\n            fbDanmakuCount = other.fbDanmakuCount;\r\n            specialDanmakuCount = other.specialDanmakuCount;\r\n            totalDanmakuCount = other.totalDanmakuCount;\r\n            consumingTime = other.consumingTime;\r\n            beginTime = other.beginTime;\r\n            endTime = other.endTime;\r\n            nothingRendered = other.nothingRendered;\r\n            sysTime = other.sysTime;\r\n            cacheHitCount = other.cacheHitCount;\r\n            cacheMissCount = other.cacheMissCount;\r\n        }\r\n\r\n        public void appendToRunningDanmakus(BaseDanmaku danmaku) {\r\n            if (!mIsObtaining) {\r\n                runningDanmakus.addItem(danmaku);\r\n            }\r\n        }\r\n\r\n        public IDanmakus obtainRunningDanmakus() {\r\n            mIsObtaining = true;\r\n            IDanmakus danmakus;\r\n            synchronized (this) {\r\n                danmakus = runningDanmakus;\r\n                runningDanmakus = new Danmakus(Danmakus.ST_BY_LIST);\r\n            }\r\n            mIsObtaining = false;\r\n            return danmakus;\r\n        }\r\n\r\n    }\r\n\r\n    void draw(IDisplayer disp, IDanmakus danmakus, long startRenderTime, RenderingState renderingState);\r\n\r\n    void clear();\r\n\r\n    void clearRetainer();\r\n\r\n    void release();\r\n\r\n    void setVerifierEnabled(boolean enabled);\r\n\r\n    void setCacheManager(ICacheManager cacheManager);\r\n\r\n    void setOnDanmakuShownListener(OnDanmakuShownListener onDanmakuShownListener);\r\n\r\n    void removeOnDanmakuShownListener();\r\n\r\n    void alignBottom(boolean enable);\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/renderer/Renderer.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.renderer;\r\n\r\npublic abstract class Renderer implements IRenderer {\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/renderer/android/DanmakuRenderer.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.renderer.android;\r\n\r\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\r\nimport master.flame.danmaku.danmaku.model.DanmakuTimer;\r\nimport master.flame.danmaku.danmaku.model.ICacheManager;\r\nimport master.flame.danmaku.danmaku.model.IDanmakus;\r\nimport master.flame.danmaku.danmaku.model.IDisplayer;\r\nimport master.flame.danmaku.danmaku.model.IDrawingCache;\r\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext;\r\nimport master.flame.danmaku.danmaku.renderer.IRenderer;\r\nimport master.flame.danmaku.danmaku.renderer.Renderer;\r\n\r\n\r\npublic class DanmakuRenderer extends Renderer {\r\n\r\n    private class Consumer extends IDanmakus.DefaultConsumer<BaseDanmaku> {\r\n        private BaseDanmaku lastItem;\r\n        public IDisplayer disp;\r\n        public RenderingState renderingState;\r\n        public long startRenderTime;\r\n\r\n        @Override\r\n        public int accept(BaseDanmaku drawItem) {\r\n            lastItem = drawItem;\r\n            if (drawItem.isTimeOut()) {\r\n                disp.recycle(drawItem);\r\n                return renderingState.isRunningDanmakus ? ACTION_REMOVE : ACTION_CONTINUE;\r\n            }\r\n\r\n            if (!renderingState.isRunningDanmakus && drawItem.isOffset()) {\r\n                return ACTION_CONTINUE;\r\n            }\r\n\r\n            if (!drawItem.hasPassedFilter()) {\r\n                mContext.mDanmakuFilters.filter(drawItem, renderingState.indexInScreen, renderingState.totalSizeInScreen, renderingState.timer, false, mContext);\r\n            }\r\n            if (drawItem.getActualTime() < startRenderTime\r\n                    || (drawItem.priority == 0 && drawItem.isFiltered())) {\r\n                return ACTION_CONTINUE;\r\n            }\r\n\r\n            if (drawItem.isLate()) {\r\n                IDrawingCache<?> cache = drawItem.getDrawingCache();\r\n                if (mCacheManager != null && (cache == null || cache.get() == null)) {\r\n                    mCacheManager.addDanmaku(drawItem);\r\n                }\r\n                return ACTION_BREAK;\r\n            }\r\n\r\n            if (drawItem.getType() == BaseDanmaku.TYPE_SCROLL_RL) {\r\n                // 同屏弹幕密度只对滚动弹幕有效\r\n                renderingState.indexInScreen++;\r\n            }\r\n\r\n            // measure\r\n            if (!drawItem.isMeasured()) {\r\n                drawItem.measure(disp, false);\r\n            }\r\n\r\n            // notify prepare drawing\r\n            if (!drawItem.isPrepared()) {\r\n                drawItem.prepare(disp, false);\r\n            }\r\n\r\n            // layout\r\n            mDanmakusRetainer.fix(drawItem, disp, mVerifier);\r\n\r\n            // draw\r\n            if (drawItem.isShown()) {\r\n                if (drawItem.lines == null && drawItem.getBottom() > disp.getHeight()) {\r\n                    return ACTION_CONTINUE;    // skip bottom outside danmaku\r\n                }\r\n                int renderingType = drawItem.draw(disp);\r\n                if (renderingType == IRenderer.CACHE_RENDERING) {\r\n                    renderingState.cacheHitCount++;\r\n                } else if (renderingType == IRenderer.TEXT_RENDERING) {\r\n                    renderingState.cacheMissCount++;\r\n                    if (mCacheManager != null) {\r\n                        mCacheManager.addDanmaku(drawItem);\r\n                    }\r\n                }\r\n                renderingState.addCount(drawItem.getType(), 1);\r\n                renderingState.addTotalCount(1);\r\n                renderingState.appendToRunningDanmakus(drawItem);\r\n\r\n                if (mOnDanmakuShownListener != null\r\n                        && drawItem.firstShownFlag != mContext.mGlobalFlagValues.FIRST_SHOWN_RESET_FLAG) {\r\n                    drawItem.firstShownFlag = mContext.mGlobalFlagValues.FIRST_SHOWN_RESET_FLAG;\r\n                    mOnDanmakuShownListener.onDanmakuShown(drawItem);\r\n                }\r\n            }\r\n            return ACTION_CONTINUE;\r\n        }\r\n\r\n        @Override\r\n        public void after() {\r\n            renderingState.lastDanmaku = lastItem;\r\n            super.after();\r\n        }\r\n    }\r\n\r\n    private DanmakuTimer mStartTimer;\r\n    private final DanmakuContext mContext;\r\n    private DanmakusRetainer.Verifier mVerifier;\r\n    private final DanmakusRetainer.Verifier verifier = new DanmakusRetainer.Verifier() {\r\n        @Override\r\n        public boolean skipLayout(BaseDanmaku danmaku, float fixedTop, int lines, boolean willHit) {\r\n            if (danmaku.priority == 0 && mContext.mDanmakuFilters.filterSecondary(danmaku, lines, 0, mStartTimer, willHit, mContext)) {\r\n                danmaku.setVisibility(false);\r\n                return true;\r\n            }\r\n            return false;\r\n        }\r\n    };\r\n    private final DanmakusRetainer mDanmakusRetainer;\r\n    private ICacheManager mCacheManager;\r\n    private OnDanmakuShownListener mOnDanmakuShownListener;\r\n    private Consumer mConsumer = new Consumer();\r\n\r\n    public DanmakuRenderer(DanmakuContext config) {\r\n        mContext = config;\r\n        mDanmakusRetainer = new DanmakusRetainer(config.isAlignBottom());\r\n    }\r\n\r\n    @Override\r\n    public void clear() {\r\n        clearRetainer();\r\n        mContext.mDanmakuFilters.clear();\r\n    }\r\n\r\n    @Override\r\n    public void clearRetainer() {\r\n        mDanmakusRetainer.clear();\r\n    }\r\n\r\n    @Override\r\n    public void release() {\r\n        mDanmakusRetainer.release();\r\n        mContext.mDanmakuFilters.clear();\r\n    }\r\n\r\n    @Override\r\n    public void setVerifierEnabled(boolean enabled) {\r\n        mVerifier = (enabled ? verifier : null);\r\n    }\r\n\r\n    @Override\r\n    public void draw(final IDisplayer disp, IDanmakus danmakus, long startRenderTime, final RenderingState renderingState) {\r\n        mStartTimer = renderingState.timer;\r\n        mConsumer.disp = disp;\r\n        mConsumer.renderingState = renderingState;\r\n        mConsumer.startRenderTime = startRenderTime;\r\n        danmakus.forEachSync(mConsumer);\r\n    }\r\n\r\n    public void setCacheManager(ICacheManager cacheManager) {\r\n        mCacheManager = cacheManager;\r\n    }\r\n\r\n    @Override\r\n    public void setOnDanmakuShownListener(OnDanmakuShownListener onDanmakuShownListener) {\r\n        mOnDanmakuShownListener = onDanmakuShownListener;\r\n    }\r\n\r\n    @Override\r\n    public void removeOnDanmakuShownListener() {\r\n        mOnDanmakuShownListener = null;\r\n    }\r\n\r\n    public void alignBottom(boolean enable) {\r\n        if (mDanmakusRetainer != null) {\r\n            mDanmakusRetainer.alignBottom(enable);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/renderer/android/DanmakusRetainer.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.renderer.android;\r\n\r\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\r\nimport master.flame.danmaku.danmaku.model.IDanmakus;\r\nimport master.flame.danmaku.danmaku.model.IDisplayer;\r\nimport master.flame.danmaku.danmaku.model.android.Danmakus;\r\nimport master.flame.danmaku.danmaku.util.DanmakuUtils;\r\n\r\npublic class DanmakusRetainer {\r\n\r\n    private IDanmakusRetainer rldrInstance = null;\r\n\r\n    private IDanmakusRetainer lrdrInstance = null;\r\n\r\n    private IDanmakusRetainer ftdrInstance = null;\r\n\r\n    private IDanmakusRetainer fbdrInstance = null;\r\n\r\n    public DanmakusRetainer(boolean alignBottom) {\r\n        alignBottom(alignBottom);\r\n    }\r\n\r\n    public void alignBottom(boolean alignBottom) {\r\n        rldrInstance = alignBottom ? new AlignBottomRetainer() : new AlignTopRetainer();\r\n        lrdrInstance = alignBottom ? new AlignBottomRetainer() : new AlignTopRetainer();\r\n        if (ftdrInstance == null) {\r\n            ftdrInstance = new FTDanmakusRetainer();\r\n        }\r\n        if (fbdrInstance == null) {\r\n            fbdrInstance = new AlignBottomRetainer();\r\n        }\r\n    }\r\n\r\n    public void fix(BaseDanmaku danmaku, IDisplayer disp, Verifier verifier) {\r\n\r\n        int type = danmaku.getType();\r\n        switch (type) {\r\n            case BaseDanmaku.TYPE_SCROLL_RL:\r\n                rldrInstance.fix(danmaku, disp, verifier);\r\n                break;\r\n            case BaseDanmaku.TYPE_SCROLL_LR:\r\n                lrdrInstance.fix(danmaku, disp, verifier);\r\n                break;\r\n            case BaseDanmaku.TYPE_FIX_TOP:\r\n                ftdrInstance.fix(danmaku, disp, verifier);\r\n                break;\r\n            case BaseDanmaku.TYPE_FIX_BOTTOM:\r\n                fbdrInstance.fix(danmaku, disp, verifier);\r\n                break;\r\n            case BaseDanmaku.TYPE_SPECIAL:\r\n                danmaku.layout(disp, 0, 0);\r\n                break;\r\n        }\r\n\r\n    }\r\n\r\n    public void clear() {\r\n        if (rldrInstance != null) {\r\n            rldrInstance.clear();\r\n        }\r\n        if (lrdrInstance != null) {\r\n            lrdrInstance.clear();\r\n        }\r\n        if (ftdrInstance != null) {\r\n            ftdrInstance.clear();\r\n        }\r\n        if (fbdrInstance != null) {\r\n            fbdrInstance.clear();\r\n        }\r\n    }\r\n    \r\n    public void release(){\r\n        clear();\r\n    }\r\n\r\n    public interface Verifier {\r\n\r\n        public boolean skipLayout(BaseDanmaku danmaku, float fixedTop, int lines, boolean willHit);\r\n\r\n    }\r\n\r\n    public interface IDanmakusRetainer {\r\n\r\n        public void fix(BaseDanmaku drawItem, IDisplayer disp, Verifier verifier);\r\n\r\n        public void clear();\r\n\r\n    }\r\n\r\n    private static class RetainerState {\r\n        public int lines = 0;\r\n        public BaseDanmaku insertItem = null, firstItem = null, lastItem = null, minRightRow = null, removeItem = null;\r\n        public boolean overwriteInsert = false;\r\n        public boolean shown = false;\r\n        public boolean willHit = false;\r\n    }\r\n\r\n\r\n    private static class AlignTopRetainer implements IDanmakusRetainer {\r\n        protected class RetainerConsumer extends IDanmakus.Consumer<BaseDanmaku, RetainerState> {\r\n            public IDisplayer disp;\r\n            int lines = 0;\r\n            public BaseDanmaku insertItem = null, firstItem = null, lastItem = null, minRightRow = null, drawItem = null;\r\n            boolean overwriteInsert = false;\r\n            boolean shown = false;\r\n            boolean willHit = false;\r\n\r\n            @Override\r\n            public void before() {\r\n                lines = 0;\r\n                insertItem = firstItem = lastItem = minRightRow = null;\r\n                overwriteInsert = shown = willHit = false;\r\n            }\r\n\r\n            @Override\r\n            public int accept(BaseDanmaku item) {\r\n                if (mCancelFixingFlag) {\r\n                    return ACTION_BREAK;\r\n                }\r\n                lines++;\r\n                if(item == drawItem){\r\n                    insertItem = item;\r\n                    lastItem = null;\r\n                    shown = true;\r\n                    willHit = false;\r\n                    return ACTION_BREAK;\r\n                }\r\n\r\n                if (firstItem == null)\r\n                    firstItem = item;\r\n\r\n                if (drawItem.paintHeight + item.getTop() > disp.getHeight()) {\r\n                    overwriteInsert = true;\r\n                    return ACTION_BREAK;\r\n                }\r\n\r\n                if (minRightRow == null) {\r\n                    minRightRow = item;\r\n                } else {\r\n                    if (minRightRow.getRight() >= item.getRight()) {\r\n                        minRightRow = item;\r\n                    }\r\n                }\r\n\r\n                // 检查碰撞\r\n                willHit = DanmakuUtils.willHitInDuration(disp, item, drawItem,\r\n                        drawItem.getDuration(), drawItem.getTimer().currMillisecond);\r\n                if (!willHit) {\r\n                    insertItem = item;\r\n                    return ACTION_BREAK;\r\n                }\r\n\r\n                lastItem = item;\r\n                return ACTION_CONTINUE;\r\n            }\r\n\r\n            @Override\r\n            public RetainerState result() {\r\n                RetainerState retainerState = new RetainerState();\r\n                retainerState.lines = this.lines;\r\n                retainerState.firstItem = this.firstItem;\r\n                retainerState.insertItem = this.insertItem;\r\n                retainerState.lastItem = this.lastItem;\r\n                retainerState.minRightRow = this.minRightRow;\r\n                retainerState.overwriteInsert = this.overwriteInsert;\r\n                retainerState.shown = this.shown;\r\n                retainerState.willHit = this.willHit;\r\n                return retainerState;\r\n            }\r\n        }\r\n\r\n        protected Danmakus mVisibleDanmakus = new Danmakus(Danmakus.ST_BY_YPOS);\r\n        protected boolean mCancelFixingFlag = false;\r\n        protected RetainerConsumer mConsumer = new RetainerConsumer();\r\n\r\n        @Override\r\n        public void fix(BaseDanmaku drawItem, IDisplayer disp, Verifier verifier) {\r\n            if (drawItem.isOutside())\r\n                return;\r\n            float topPos = disp.getAllMarginTop();\r\n            int lines = 0;\r\n            boolean shown = drawItem.isShown();\r\n            boolean willHit = !shown && !mVisibleDanmakus.isEmpty();\r\n            boolean isOutOfVertialEdge = false;\r\n            BaseDanmaku removeItem = null;\r\n            int margin = disp.getMargin();\r\n            if (!shown) {\r\n                mCancelFixingFlag = false;\r\n                // 确定弹幕位置\r\n                BaseDanmaku insertItem = null, firstItem = null, lastItem = null, minRightRow = null;\r\n                boolean overwriteInsert = false;\r\n                mConsumer.disp = disp;\r\n                mConsumer.drawItem = drawItem;\r\n                mVisibleDanmakus.forEachSync(mConsumer);\r\n                RetainerState retainerState = mConsumer.result();\r\n                if (retainerState != null) {\r\n                    lines = retainerState.lines;\r\n                    insertItem = retainerState.insertItem;\r\n                    firstItem = retainerState.firstItem;\r\n                    lastItem = retainerState.lastItem;\r\n                    minRightRow = retainerState.minRightRow;\r\n                    overwriteInsert = retainerState.overwriteInsert;\r\n                    shown = retainerState.shown;\r\n                    willHit = retainerState.willHit;\r\n                }\r\n                boolean checkEdge = true;\r\n                if (insertItem != null) {\r\n                    if (lastItem != null)\r\n                        topPos = lastItem.getBottom() + margin;\r\n                    else\r\n                        topPos = insertItem.getTop();\r\n                    if (insertItem != drawItem){\r\n                        removeItem = insertItem;\r\n                        shown = false;\r\n                    }\r\n                } else if (overwriteInsert && minRightRow != null) {\r\n                    topPos = minRightRow.getTop();\r\n                    checkEdge = false;\r\n                    shown = false;\r\n                } else if (lastItem != null) {\r\n                    topPos = lastItem.getBottom() + margin;\r\n                    willHit = false;\r\n                } else if (firstItem != null) {\r\n                    topPos = firstItem.getTop();\r\n                    removeItem = firstItem;\r\n                    shown = false;\r\n                } else {\r\n                    topPos = disp.getAllMarginTop();\r\n                }\r\n                if (checkEdge) {\r\n                    isOutOfVertialEdge = isOutVerticalEdge(overwriteInsert, drawItem, disp, topPos, firstItem,\r\n                            lastItem);\r\n                }\r\n                if (isOutOfVertialEdge) {\r\n                    topPos = disp.getAllMarginTop();\r\n                    willHit = true;\r\n                    lines = 1;\r\n                } else if (removeItem != null) {\r\n                    lines--;\r\n                }\r\n                if (topPos == disp.getAllMarginTop()) {\r\n                    shown = false;\r\n                }\r\n            }\r\n\r\n            if (verifier != null && verifier.skipLayout(drawItem, topPos, lines, willHit)) {\r\n                return;\r\n            }\r\n\r\n            if (isOutOfVertialEdge) {\r\n                clear();\r\n            }\r\n\r\n            drawItem.layout(disp, drawItem.getLeft(), topPos);\r\n\r\n            if (!shown) {\r\n                mVisibleDanmakus.removeItem(removeItem);\r\n                mVisibleDanmakus.addItem(drawItem);\r\n            }\r\n\r\n        }\r\n\r\n        protected boolean isOutVerticalEdge(boolean overwriteInsert, BaseDanmaku drawItem,\r\n                                            IDisplayer disp, float topPos, BaseDanmaku firstItem, BaseDanmaku lastItem) {\r\n            if (topPos < disp.getAllMarginTop() || (firstItem != null && firstItem.getTop() > 0) || topPos + drawItem.paintHeight > disp.getHeight()) {\r\n                return true;\r\n            }\r\n            return false;\r\n        }\r\n\r\n        @Override\r\n        public void clear() {\r\n            mCancelFixingFlag = true;\r\n            mVisibleDanmakus.clear();\r\n        }\r\n\r\n    }\r\n\r\n    private static class FTDanmakusRetainer extends AlignTopRetainer {\r\n\r\n        @Override\r\n        protected boolean isOutVerticalEdge(boolean overwriteInsert, BaseDanmaku drawItem,\r\n                                            IDisplayer disp, float topPos, BaseDanmaku firstItem, BaseDanmaku lastItem) {\r\n            if (topPos + drawItem.paintHeight > disp.getHeight()) {\r\n                return true;\r\n            }\r\n            return false;\r\n        }\r\n\r\n    }\r\n\r\n    private static class AlignBottomRetainer extends FTDanmakusRetainer {\r\n        protected class RetainerConsumer extends IDanmakus.Consumer<BaseDanmaku, RetainerState> {\r\n            public IDisplayer disp;\r\n            int lines = 0;\r\n            public BaseDanmaku removeItem = null, firstItem = null, drawItem = null;\r\n            boolean willHit = false;\r\n            float topPos;\r\n\r\n            @Override\r\n            public void before() {\r\n                lines = 0;\r\n                removeItem = firstItem = null;\r\n                willHit = false;\r\n            }\r\n\r\n            @Override\r\n            public int accept(BaseDanmaku item) {\r\n                if (mCancelFixingFlag) {\r\n                    return ACTION_BREAK;\r\n                }\r\n                lines++;\r\n                if (item == drawItem) {\r\n                    removeItem = null;\r\n                    willHit = false;\r\n                    return ACTION_BREAK;\r\n                }\r\n\r\n                if (firstItem == null) {\r\n                    firstItem = item;\r\n                    if (firstItem.getBottom() != disp.getHeight()) {\r\n                        return ACTION_BREAK;\r\n                    }\r\n                }\r\n\r\n                if (topPos < disp.getAllMarginTop()) {\r\n                    removeItem = null;\r\n                    return ACTION_BREAK;\r\n                }\r\n\r\n                // 检查碰撞\r\n                willHit = DanmakuUtils.willHitInDuration(disp, item, drawItem,\r\n                        drawItem.getDuration(), drawItem.getTimer().currMillisecond);\r\n                if (!willHit) {\r\n                    removeItem = item;\r\n                    // topPos = item.getBottom() - drawItem.paintHeight;\r\n                    return ACTION_BREAK;\r\n                }\r\n\r\n                topPos = item.getTop() - disp.getMargin() - drawItem.paintHeight;\r\n                return ACTION_CONTINUE;\r\n            }\r\n\r\n            @Override\r\n            public RetainerState result() {\r\n                RetainerState retainerState = new RetainerState();\r\n                retainerState.lines = this.lines;\r\n                retainerState.firstItem = this.firstItem;\r\n                retainerState.removeItem = this.removeItem;\r\n                retainerState.willHit = this.willHit;\r\n                return retainerState;\r\n            }\r\n        }\r\n\r\n        protected RetainerConsumer mConsumer = new RetainerConsumer();\r\n        protected Danmakus mVisibleDanmakus = new Danmakus(Danmakus.ST_BY_YPOS_DESC);\r\n\r\n        @Override\r\n        public void fix(BaseDanmaku drawItem, IDisplayer disp, Verifier verifier) {\r\n            if (drawItem.isOutside())\r\n                return;\r\n            boolean shown = drawItem.isShown();\r\n            float topPos = shown ? drawItem.getTop() : -1;\r\n            int lines = 0;\r\n            boolean willHit = !shown && !mVisibleDanmakus.isEmpty();\r\n            boolean isOutOfVerticalEdge = false;\r\n            if (topPos < disp.getAllMarginTop()) {\r\n                topPos = disp.getHeight() - drawItem.paintHeight;\r\n            }\r\n            BaseDanmaku removeItem = null, firstItem = null;\r\n            if (!shown) {\r\n                mCancelFixingFlag = false;\r\n                mConsumer.topPos = topPos;\r\n                mConsumer.disp = disp;\r\n                mConsumer.drawItem = drawItem;\r\n                mVisibleDanmakus.forEachSync(mConsumer);\r\n                RetainerState retainerState = mConsumer.result();\r\n                topPos = mConsumer.topPos;\r\n                if (retainerState != null) {\r\n                    lines = retainerState.lines;\r\n                    firstItem = retainerState.firstItem;\r\n                    removeItem = retainerState.removeItem;\r\n                    shown = retainerState.shown;\r\n                    willHit = retainerState.willHit;\r\n                }\r\n\r\n                isOutOfVerticalEdge = isOutVerticalEdge(false, drawItem, disp, topPos, firstItem, null);\r\n                if (isOutOfVerticalEdge) {\r\n                    topPos = disp.getHeight() - drawItem.paintHeight;\r\n                    willHit = true;\r\n                    lines = 1;\r\n                } else {\r\n                    if (topPos >= disp.getAllMarginTop()) {\r\n                        willHit = false;\r\n                    }\r\n                    if (removeItem != null) {\r\n                        lines--;\r\n                    }\r\n                }\r\n\r\n            }\r\n\r\n            if (verifier != null && verifier.skipLayout(drawItem, topPos, lines, willHit)) {\r\n                return;\r\n            }\r\n\r\n            if (isOutOfVerticalEdge) {\r\n                clear();\r\n            }\r\n\r\n            drawItem.layout(disp, drawItem.getLeft(), topPos);\r\n\r\n            if (!shown) {\r\n                mVisibleDanmakus.removeItem(removeItem);\r\n                mVisibleDanmakus.addItem(drawItem);\r\n            }\r\n\r\n        }\r\n\r\n        protected boolean isOutVerticalEdge(boolean overwriteInsert, BaseDanmaku drawItem,\r\n                                            IDisplayer disp, float topPos, BaseDanmaku firstItem, BaseDanmaku lastItem) {\r\n            if (topPos < disp.getAllMarginTop() || (firstItem != null && firstItem.getBottom() != disp.getHeight())) {\r\n                return true;\r\n            }\r\n            return false;\r\n        }\r\n\r\n        @Override\r\n        public void clear() {\r\n            mCancelFixingFlag = true;\r\n            mVisibleDanmakus.clear();\r\n        }\r\n\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/util/DanmakuUtils.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.danmaku.util;\r\n\r\nimport android.text.TextUtils;\r\n\r\nimport master.flame.danmaku.danmaku.model.AbsDisplayer;\r\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\r\nimport master.flame.danmaku.danmaku.model.IDisplayer;\r\nimport master.flame.danmaku.danmaku.model.android.DrawingCache;\r\nimport master.flame.danmaku.danmaku.model.android.DrawingCacheHolder;\r\n\r\npublic class DanmakuUtils {\r\n\r\n    /**\r\n     * 检测两个弹幕是否会碰撞\r\n     * 允许不同类型弹幕的碰撞\r\n     * @param d1\r\n     * @param d2\r\n     * @return\r\n     */\r\n    public static boolean willHitInDuration(IDisplayer disp, BaseDanmaku d1, BaseDanmaku d2,\r\n            long duration, long currTime) {\r\n        final int type1 = d1.getType();\r\n        final int type2 = d2.getType();\r\n        // allow hit if different type\r\n        if(type1 != type2)\r\n            return false;\r\n        \r\n        if(d1.isOutside()){\r\n            return false;\r\n        }\r\n        long dTime = d2.getActualTime() - d1.getActualTime();\r\n        if (dTime <= 0)\r\n            return true;\r\n        if (Math.abs(dTime) >= duration || d1.isTimeOut() || d2.isTimeOut()) {\r\n            return false;\r\n        }\r\n\r\n        if (type1 == BaseDanmaku.TYPE_FIX_TOP || type1 == BaseDanmaku.TYPE_FIX_BOTTOM) {\r\n            return true;\r\n        }\r\n\r\n        return checkHitAtTime(disp, d1, d2, currTime) \r\n                || checkHitAtTime(disp, d1, d2,  d1.getActualTime() + d1.getDuration());\r\n    }\r\n    \r\n    private static boolean checkHitAtTime(IDisplayer disp, BaseDanmaku d1, BaseDanmaku d2, long time){\r\n        final float[] rectArr1 = d1.getRectAtTime(disp, time);\r\n        final float[] rectArr2 = d2.getRectAtTime(disp, time);\r\n        if (rectArr1 == null || rectArr2 == null)\r\n            return false;\r\n        return checkHit(d1.getType(), d2.getType(), rectArr1, rectArr2);\r\n    }\r\n    \r\n    private static boolean checkHit(int type1, int type2, float[] rectArr1,\r\n            float[] rectArr2) {\r\n        if(type1 != type2)\r\n            return false;\r\n        if (type1 == BaseDanmaku.TYPE_SCROLL_RL) {\r\n            // hit if left2 < right1\r\n            return rectArr2[0] < rectArr1[2];\r\n        }\r\n        \r\n        if (type1 == BaseDanmaku.TYPE_SCROLL_LR){\r\n            // hit if right2 > left1\r\n            return rectArr2[2] > rectArr1[0];\r\n        }\r\n        \r\n        return false;\r\n    }\r\n\r\n    public static DrawingCache buildDanmakuDrawingCache(BaseDanmaku danmaku, IDisplayer disp,\r\n            DrawingCache cache, int bitsPerPixel) {\r\n        if (cache == null)\r\n            cache = new DrawingCache();\r\n\r\n        cache.build((int) Math.ceil(danmaku.paintWidth), (int) Math.ceil(danmaku.paintHeight), disp.getDensityDpi(), false, bitsPerPixel);\r\n        DrawingCacheHolder holder = cache.get();\r\n        if (holder != null) {\r\n            ((AbsDisplayer) disp).drawDanmaku(danmaku, holder.canvas, 0, 0, true);\r\n            if(disp.isHardwareAccelerated()) {\r\n                holder.splitWith(disp.getWidth(), disp.getHeight(), disp.getMaximumCacheWidth(),\r\n                        disp.getMaximumCacheHeight());\r\n            }\r\n        }\r\n        return cache;\r\n    }\r\n\r\n    public static int getCacheSize(int w, int h, int bytesPerPixel) {\r\n        return (w) * (h) * bytesPerPixel;\r\n    }\r\n    \r\n    public final static boolean isDuplicate(BaseDanmaku obj1, BaseDanmaku obj2) {\r\n        if(obj1 == obj2) {\r\n            return false;\r\n        }\r\n//        if(obj1.isTimeOut() || obj2.isTimeOut()) {\r\n//            return false;\r\n//        }\r\n//        long dtime = Math.abs(obj1.time - obj2.time);\r\n//        if(dtime > obj1.getDuration()) {\r\n//            return false;\r\n//        }\r\n        if (obj1.text == obj2.text) {\r\n            return true;\r\n        }\r\n        if (obj1.text != null && obj1.text.equals(obj2.text)) {\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    public final static int compare(BaseDanmaku obj1, BaseDanmaku obj2) {\r\n        \r\n        if (obj1 == obj2) {\r\n            return 0;\r\n        }\r\n\r\n        if (obj1 == null) {\r\n            return -1;\r\n        }\r\n\r\n        if (obj2 == null) {\r\n            return 1;\r\n        }\r\n\r\n        long val = obj1.getTime() - obj2.getTime();\r\n        if (val > 0) {\r\n            return 1;\r\n        } else if (val < 0) {\r\n            return -1;\r\n        }\r\n        int r = obj1.index - obj2.index;\r\n        if (r != 0)\r\n            return r < 0 ? -1 : 1;\r\n\r\n        r = obj1.hashCode() - obj1.hashCode();\r\n        return r;\r\n    }\r\n\r\n    public final static boolean isOverSize(IDisplayer disp, BaseDanmaku item) {\r\n        return disp.isHardwareAccelerated() && (item.paintWidth > disp.getMaximumCacheWidth() || item.paintHeight > disp.getMaximumCacheHeight());\r\n    }\r\n\r\n    public static void fillText(BaseDanmaku danmaku, CharSequence text) {\r\n        danmaku.text = text;\r\n        if (TextUtils.isEmpty(text) || !text.toString().contains(BaseDanmaku.DANMAKU_BR_CHAR)) {\r\n            return;\r\n        }\r\n\r\n        String[] lines = String.valueOf(danmaku.text).split(BaseDanmaku.DANMAKU_BR_CHAR, -1);\r\n        if (lines.length > 1) {\r\n            danmaku.lines = lines;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/util/IOUtils.java",
    "content": "package master.flame.danmaku.danmaku.util;\r\n\r\nimport java.io.ByteArrayOutputStream;\r\nimport java.io.IOException;\r\nimport java.io.InputStream;\r\nimport java.io.OutputStream;\r\n\r\n/**\r\n * Created by MoiTempete.\r\n */\r\npublic class IOUtils {\r\n    public static String getString(InputStream in){\r\n        byte[] data = getBytes(in);\r\n        return data == null? null:new String(data);\r\n    }\r\n    public static byte[] getBytes(InputStream in){\r\n\r\n        try {\r\n            ByteArrayOutputStream baos = new ByteArrayOutputStream();\r\n            byte[] buffer = new byte[8192];\r\n            int len = 0;\r\n            while ((len = in.read(buffer)) != -1)\r\n                baos.write(buffer, 0, len);\r\n            in.close();\r\n            return baos.toByteArray();\r\n        } catch (IOException e) {\r\n            return null;\r\n        }\r\n    }\r\n    public static void closeQuietly(InputStream in){\r\n    \ttry {\r\n    \t\tif(in != null)\r\n    \t\t\tin.close();\r\n\t\t} catch (IOException ignore) {}\r\n    }\r\n    public static void closeQuietly(OutputStream out){\r\n    \ttry {\r\n    \t\tif(out != null)\r\n    \t\t\tout.close();\r\n    \t} catch (IOException ignore) {}\r\n    }\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/danmaku/util/SystemClock.java",
    "content": "package master.flame.danmaku.danmaku.util;\n\n/**\n * Created by ch on 15-12-9.\n */\npublic class SystemClock {\n    public static final long uptimeMillis() {\n        return android.os.SystemClock.elapsedRealtime();\n    }\n\n    public static final void sleep(long mills) {\n        android.os.SystemClock.sleep(mills);\n    }\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/ui/widget/DanmakuSurfaceView.java",
    "content": "/*\r\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage master.flame.danmaku.ui.widget;\r\n\r\nimport android.content.Context;\r\nimport android.graphics.Canvas;\r\nimport android.graphics.PixelFormat;\r\nimport android.os.HandlerThread;\r\nimport android.os.Looper;\r\nimport android.util.AttributeSet;\r\nimport android.view.MotionEvent;\r\nimport android.view.SurfaceHolder;\r\nimport android.view.SurfaceView;\r\nimport android.view.View;\r\n\r\nimport java.util.LinkedList;\r\nimport java.util.Locale;\r\n\r\nimport master.flame.danmaku.controller.DrawHandler;\r\nimport master.flame.danmaku.controller.DrawHandler.Callback;\r\nimport master.flame.danmaku.controller.DrawHelper;\r\nimport master.flame.danmaku.controller.IDanmakuView;\r\nimport master.flame.danmaku.controller.IDanmakuViewController;\r\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\r\nimport master.flame.danmaku.danmaku.model.IDanmakus;\r\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext;\r\nimport master.flame.danmaku.danmaku.parser.BaseDanmakuParser;\r\nimport master.flame.danmaku.danmaku.renderer.IRenderer.RenderingState;\r\nimport master.flame.danmaku.danmaku.util.SystemClock;\r\n\r\nimport static android.R.attr.x;\r\n\r\npublic class DanmakuSurfaceView extends SurfaceView implements IDanmakuView, IDanmakuViewController, SurfaceHolder.Callback {\r\n\r\n    public static final String TAG = \"DanmakuSurfaceView\";\r\n\r\n    private Callback mCallback;\r\n\r\n    private SurfaceHolder mSurfaceHolder;\r\n\r\n    private HandlerThread mHandlerThread;\r\n\r\n    private DrawHandler handler;\r\n    \r\n    private boolean isSurfaceCreated;\r\n\r\n    private boolean mEnableDanmakuDrwaingCache = true;\r\n\r\n\tprivate OnDanmakuClickListener mOnDanmakuClickListener;\r\n\r\n    private float mXOff;\r\n\r\n    private float mYOff;\r\n\r\n    private DanmakuTouchHelper mTouchHelper;\r\n    \r\n    private boolean mShowFps;\r\n\r\n    private boolean mDanmakuVisible = true;\r\n\r\n    protected int mDrawingThreadType = THREAD_TYPE_NORMAL_PRIORITY;\r\n\r\n    public DanmakuSurfaceView(Context context) {\r\n        super(context);\r\n        init();\r\n    }\r\n\r\n    private void init() {\r\n        setZOrderMediaOverlay(true);\r\n        setWillNotCacheDrawing(true);\r\n        setDrawingCacheEnabled(false);\r\n        setWillNotDraw(true);\r\n        mSurfaceHolder = getHolder();\r\n        mSurfaceHolder.addCallback(this);\r\n        mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);\r\n        DrawHelper.useDrawColorToClearCanvas(true, true);\r\n\t\tmTouchHelper = DanmakuTouchHelper.instance(this);\r\n    }\r\n\r\n    public DanmakuSurfaceView(Context context, AttributeSet attrs) {\r\n        super(context, attrs);\r\n        init();\r\n    }\r\n\r\n    public DanmakuSurfaceView(Context context, AttributeSet attrs, int defStyle) {\r\n        super(context, attrs, defStyle);\r\n        init();\r\n    }\r\n\r\n    public void addDanmaku(BaseDanmaku item) {\r\n        if (handler != null) {\r\n            handler.addDanmaku(item);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void invalidateDanmaku(BaseDanmaku item, boolean remeasure) {\r\n        if (handler != null) {\r\n            handler.invalidateDanmaku(item, remeasure);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void removeAllDanmakus(boolean isClearDanmakusOnScreen) {\r\n        if (handler != null) {\r\n            handler.removeAllDanmakus(isClearDanmakusOnScreen);\r\n        }\r\n    }\r\n    \r\n    @Override\r\n    public void removeAllLiveDanmakus() {\r\n        if (handler != null) {\r\n            handler.removeAllLiveDanmakus();\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public IDanmakus getCurrentVisibleDanmakus() {\r\n        if (handler != null) {\r\n            return handler.getCurrentVisibleDanmakus();\r\n        }\r\n\r\n        return null;\r\n    }\r\n\r\n    public void setCallback(Callback callback) {\r\n        mCallback = callback;\r\n        if (handler != null) {\r\n            handler.setCallback(callback);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void surfaceCreated(SurfaceHolder surfaceHolder) {\r\n        isSurfaceCreated = true;\r\n        Canvas canvas = surfaceHolder.lockCanvas();\r\n        if (canvas != null) {\r\n            DrawHelper.clearCanvas(canvas);\r\n            surfaceHolder.unlockCanvasAndPost(canvas);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {\r\n        if (handler != null) {\r\n            handler.notifyDispSizeChanged(width, height);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {\r\n        isSurfaceCreated = false;\r\n    }\r\n\r\n    @Override\r\n    public void release() {\r\n        stop();\r\n        if(mDrawTimes!= null) mDrawTimes.clear();\r\n    }\r\n\r\n    @Override\r\n    public void stop() {\r\n        stopDraw();\r\n    }\r\n\r\n    private synchronized void stopDraw() {\r\n        if (handler != null) {\r\n            handler.quit();\r\n            handler = null;\r\n        }\r\n        HandlerThread handlerThread = this.mHandlerThread;\r\n        mHandlerThread = null;\r\n        if (handlerThread != null) {\r\n            try {\r\n                handlerThread.join();\r\n            } catch (InterruptedException e) {\r\n                e.printStackTrace();\r\n            }\r\n            handlerThread.quit();\r\n        }\r\n    }\r\n    \r\n    protected synchronized Looper getLooper(int type){\r\n        if (mHandlerThread != null) {\r\n            mHandlerThread.quit();\r\n            mHandlerThread = null;\r\n        }\r\n        \r\n        int priority;\r\n        switch (type) {\r\n            case THREAD_TYPE_MAIN_THREAD:\r\n                return Looper.getMainLooper();\r\n            case THREAD_TYPE_HIGH_PRIORITY:\r\n                priority = android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY;\r\n                break;\r\n            case THREAD_TYPE_LOW_PRIORITY:\r\n                priority = android.os.Process.THREAD_PRIORITY_LOWEST;\r\n                break;\r\n            case THREAD_TYPE_NORMAL_PRIORITY:\r\n            default:\r\n                priority = android.os.Process.THREAD_PRIORITY_DEFAULT;\r\n                break;\r\n        }\r\n        String threadName = \"DFM Handler Thread #\"+priority;\r\n        mHandlerThread = new HandlerThread(threadName, priority);\r\n        mHandlerThread.start();\r\n        return mHandlerThread.getLooper();\r\n    }\r\n\r\n    private void prepare() {\r\n        if (handler == null)\r\n            handler = new DrawHandler(getLooper(mDrawingThreadType), this, mDanmakuVisible);\r\n    }\r\n\r\n    @Override\r\n    public void prepare(BaseDanmakuParser parser, DanmakuContext config) {\r\n    \tprepare();\r\n        handler.setConfig(config);\r\n        handler.setParser(parser);\r\n        handler.setCallback(mCallback);\r\n        handler.prepare();\r\n    }\r\n\r\n    @Override\r\n    public boolean isPrepared() {\r\n        return handler != null && handler.isPrepared();\r\n    }\r\n\r\n    @Override\r\n    public DanmakuContext getConfig() {\r\n        if (handler == null) {\r\n            return null;\r\n        }\r\n        return handler.getConfig();\r\n    }\r\n\r\n    @Override\r\n    public void showFPS(boolean show){\r\n        mShowFps = show;\r\n    }\r\n    private static final int MAX_RECORD_SIZE = 50;\r\n    private static final int ONE_SECOND = 1000;\r\n    private LinkedList<Long> mDrawTimes;\r\n    private float fps() {\r\n        long lastTime = SystemClock.uptimeMillis();\r\n        mDrawTimes.addLast(lastTime);\r\n        Long first = mDrawTimes.peekFirst();\r\n        if (first == null) {\r\n            return 0.0f;\r\n        }\r\n        float dtime = lastTime - first;\r\n        int frames = mDrawTimes.size();\r\n        if (frames > MAX_RECORD_SIZE) {\r\n            mDrawTimes.removeFirst();\r\n        }\r\n        return dtime > 0 ? mDrawTimes.size() * ONE_SECOND / dtime : 0.0f;\r\n    }\r\n    @Override\r\n    public long drawDanmakus() {\r\n        if (!isSurfaceCreated)\r\n            return 0;\r\n        if (!isShown())\r\n            return -1;\r\n        long stime = SystemClock.uptimeMillis();\r\n        long dtime = 0;\r\n        Canvas canvas = mSurfaceHolder.lockCanvas();\r\n        if (canvas != null){\r\n            if (handler != null) {\r\n                RenderingState rs = handler.draw(canvas);\r\n                if (mShowFps) {\r\n                    if (mDrawTimes == null)\r\n                        mDrawTimes = new LinkedList<Long>();\r\n                    dtime = SystemClock.uptimeMillis() - stime;\r\n                    String fps = String.format(Locale.getDefault(),\r\n                            \"fps %.2f,time:%d s,cache:%d,miss:%d\", fps(), getCurrentTime() / 1000,\r\n                            rs.cacheHitCount, rs.cacheMissCount);\r\n                    DrawHelper.drawFPS(canvas, fps);\r\n                }\r\n            }\r\n            if (isSurfaceCreated)\r\n                mSurfaceHolder.unlockCanvasAndPost(canvas);\r\n        }\r\n        dtime = SystemClock.uptimeMillis() - stime;\r\n        return dtime;\r\n    }\r\n\r\n    public void toggle() {\r\n        if (isSurfaceCreated) {\r\n            if (handler == null)\r\n                start();\r\n            else if (handler.isStop()) {\r\n                resume();\r\n            } else\r\n                pause();\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void pause() {\r\n        if (handler != null)\r\n            handler.pause();\r\n    }\r\n\r\n    @Override\r\n    public void resume() {\r\n        if (handler != null && handler.isPrepared())\r\n            handler.resume();\r\n        else if (handler == null) {\r\n            restart();\r\n        }\r\n    }\r\n    \r\n    @Override\r\n    public boolean isPaused() {\r\n        if(handler != null) {\r\n            return handler.isStop();\r\n        }\r\n        return false;\r\n    }\r\n\r\n    public void restart() {\r\n        stop();\r\n        start();\r\n    }\r\n\r\n    @Override\r\n    public void start() {\r\n        start(0);\r\n    }\r\n\r\n    @Override\r\n    public void start(long postion) {\r\n        if (handler == null) {\r\n            prepare();\r\n        }else{\r\n            handler.removeCallbacksAndMessages(null);\r\n        }\r\n        handler.obtainMessage(DrawHandler.START, postion).sendToTarget();\r\n    }\r\n\r\n\t@Override\r\n    public boolean onTouchEvent(MotionEvent event) {\r\n        boolean isEventConsumed = mTouchHelper.onTouchEvent(event);\r\n        if (!isEventConsumed) {\r\n            return super.onTouchEvent(event);\r\n        }\r\n        return isEventConsumed;\r\n    }\r\n\r\n    public void seekTo(Long ms) {\r\n        if(handler != null){\r\n            handler.seekTo(ms);\r\n        }\r\n    }\r\n\r\n    public void enableDanmakuDrawingCache(boolean enable) {\r\n        mEnableDanmakuDrwaingCache = enable;\r\n    }\r\n\r\n    @Override\r\n    public boolean isDanmakuDrawingCacheEnabled() {\r\n        return mEnableDanmakuDrwaingCache;\r\n    }\r\n\r\n    @Override\r\n    public boolean isViewReady() {\r\n        return isSurfaceCreated;\r\n    }\r\n\r\n    @Override\r\n    public int getViewWidth() {\r\n        return super.getWidth();\r\n    }\r\n\r\n    @Override\r\n    public int getViewHeight() {\r\n        return super.getHeight();\r\n    }\r\n\r\n    @Override\r\n    public View getView() {\r\n        return this;\r\n    }\r\n      \r\n    @Override\r\n    public void show() {\r\n        showAndResumeDrawTask(null);\r\n    }\r\n    \r\n    @Override\r\n    public void showAndResumeDrawTask(Long position) {\r\n        mDanmakuVisible = true;\r\n        if (handler == null) {\r\n            return;\r\n        }\r\n        handler.showDanmakus(position);\r\n    }\r\n\r\n    @Override\r\n    public void hide() {\r\n        mDanmakuVisible = false;\r\n        if (handler == null) {\r\n            return;\r\n        }\r\n        handler.hideDanmakus(false);\r\n    }\r\n    \r\n    @Override\r\n    public long hideAndPauseDrawTask() {\r\n        mDanmakuVisible = false;\r\n        if (handler == null) {\r\n            return 0;\r\n        }\r\n        return handler.hideDanmakus(true);\r\n    }\r\n\r\n    @Override\r\n    public void setOnDanmakuClickListener(OnDanmakuClickListener listener) {\r\n        mOnDanmakuClickListener = listener;\r\n    }\r\n\r\n    @Override\r\n    public void setOnDanmakuClickListener(OnDanmakuClickListener listener, float xOff, float yOff) {\r\n        mOnDanmakuClickListener = listener;\r\n        mXOff = xOff;\r\n        mYOff = yOff;\r\n    }\r\n\r\n    @Override\r\n    public OnDanmakuClickListener getOnDanmakuClickListener() {\r\n        return mOnDanmakuClickListener;\r\n    }\r\n\r\n    @Override\r\n    public float getXOff() {\r\n        return mXOff;\r\n    }\r\n\r\n    @Override\r\n    public float getYOff() {\r\n        return mYOff;\r\n    }\r\n\r\n    @Override\r\n    public void forceRender() {\r\n\r\n    }\r\n\r\n    @Override\r\n    public void clear() {\r\n        if (!isViewReady()) {\r\n            return;\r\n        }\r\n        Canvas canvas = mSurfaceHolder.lockCanvas();\r\n        if (canvas != null) {\r\n            DrawHelper.clearCanvas(canvas);\r\n            mSurfaceHolder.unlockCanvasAndPost(canvas);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public boolean isShown() {\r\n        return mDanmakuVisible && super.isShown();\r\n    }\r\n\r\n    @Override\r\n    public void setDrawingThreadType(int type) {\r\n        mDrawingThreadType  = type;\r\n    }\r\n\r\n    @Override\r\n    public long getCurrentTime() {\r\n        if (handler != null) {\r\n            return handler.getCurrentTime();\r\n        }\r\n        return 0;\r\n    }\r\n\r\n    @Override\r\n    public boolean isHardwareAccelerated() {\r\n        return false;\r\n    }\r\n\r\n    @Override\r\n    public void clearDanmakusOnScreen() {\r\n        if (handler != null) {\r\n            handler.clearDanmakusOnScreen();\r\n        }\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/ui/widget/DanmakuTextureView.java",
    "content": "/*\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage master.flame.danmaku.ui.widget;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.SurfaceTexture;\nimport android.os.Build;\nimport android.os.HandlerThread;\nimport android.os.Looper;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.TextureView;\nimport android.view.View;\n\nimport java.util.LinkedList;\nimport java.util.Locale;\n\nimport master.flame.danmaku.controller.DrawHandler;\nimport master.flame.danmaku.controller.DrawHandler.Callback;\nimport master.flame.danmaku.controller.DrawHelper;\nimport master.flame.danmaku.controller.IDanmakuView;\nimport master.flame.danmaku.controller.IDanmakuViewController;\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\nimport master.flame.danmaku.danmaku.model.IDanmakus;\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext;\nimport master.flame.danmaku.danmaku.parser.BaseDanmakuParser;\nimport master.flame.danmaku.danmaku.renderer.IRenderer.RenderingState;\nimport master.flame.danmaku.danmaku.util.SystemClock;\n\n/**\n * DanmakuTextureView  目前使用lockCanvas, 没有使用opengl硬件加速\n * @author ch\n *\n */\n@SuppressLint(\"NewApi\")\npublic class DanmakuTextureView extends TextureView implements IDanmakuView, IDanmakuViewController,\n        TextureView.SurfaceTextureListener {\n\n    public static final String TAG = \"DanmakuTextureView\";\n\n    private Callback mCallback;\n\n    private HandlerThread mHandlerThread;\n\n    private DrawHandler handler;\n\n    private boolean isSurfaceCreated;\n\n    private boolean mEnableDanmakuDrwaingCache = true;\n\n\tprivate OnDanmakuClickListener mOnDanmakuClickListener;\n\n    private float mXOff;\n\n    private float mYOff;\n\n    private DanmakuTouchHelper mTouchHelper;\n\n    private boolean mShowFps;\n\n    private boolean mDanmakuVisible = true;\n    \n    protected int mDrawingThreadType = THREAD_TYPE_NORMAL_PRIORITY;\n\n    public DanmakuTextureView(Context context) {\n        super(context);\n        init();\n    }\n\n    @TargetApi(Build.VERSION_CODES.HONEYCOMB)\n    private void init() {\n        setLayerType(View.LAYER_TYPE_HARDWARE, null);\n        setOpaque(false);\n        setWillNotCacheDrawing(true);\n        setDrawingCacheEnabled(false);\n        setWillNotDraw(true);\n        setSurfaceTextureListener(this);\n        DrawHelper.useDrawColorToClearCanvas(true, true);\n\t\tmTouchHelper = DanmakuTouchHelper.instance(this);    \n\t}\n\n    public DanmakuTextureView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public DanmakuTextureView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init();\n    }\n\n    public void addDanmaku(BaseDanmaku item) {\n        if (handler != null) {\n            handler.addDanmaku(item);\n        }\n    }\n\n    @Override\n    public void invalidateDanmaku(BaseDanmaku item, boolean remeasure) {\n        if (handler != null) {\n            handler.invalidateDanmaku(item, remeasure);\n        }\n    }\n\n    @Override\n    public void removeAllDanmakus(boolean isClearDanmakusOnScreen) {\n        if (handler != null) {\n            handler.removeAllDanmakus(isClearDanmakusOnScreen);\n        }\n    }\n    \n    @Override\n    public void removeAllLiveDanmakus() {\n        if (handler != null) {\n            handler.removeAllLiveDanmakus();\n        }\n    }\n\n    @Override\n    public IDanmakus getCurrentVisibleDanmakus() {\n        if (handler != null) {\n            return handler.getCurrentVisibleDanmakus();\n        }\n\n        return null;\n    }\n\n    public void setCallback(Callback callback) {\n        mCallback = callback;\n        if (handler != null) {\n            handler.setCallback(callback);\n        }\n    }\n\n    @Override\n    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {\n        isSurfaceCreated = true;\n    }\n\n    @Override\n    public synchronized boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {\n        isSurfaceCreated = false;\n        return true;\n    }\n\n    @Override\n    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {\n        if (handler != null) {\n            handler.notifyDispSizeChanged(width, height);\n        }\n    }\n\n    @Override\n    public void onSurfaceTextureUpdated(SurfaceTexture surface) {\n\n    }\n\n    @Override\n    public void release() {\n        stop();\n        if(mDrawTimes!= null) mDrawTimes.clear();\n    }\n\n    @Override\n    public void stop() {\n        stopDraw();\n    }\n\n    private synchronized void stopDraw() {\n        if (handler != null) {\n            handler.quit();\n            handler = null;\n        }\n        HandlerThread handlerThread = this.mHandlerThread;\n        mHandlerThread = null;\n        if (handlerThread != null) {\n            try {\n                handlerThread.join();\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n            handlerThread.quit();\n        }\n    }\n    \n    protected synchronized Looper getLooper(int type){\n        if (mHandlerThread != null) {\n            mHandlerThread.quit();\n            mHandlerThread = null;\n        }\n        \n        int priority;\n        switch (type) {\n            case THREAD_TYPE_MAIN_THREAD:\n                return Looper.getMainLooper();\n            case THREAD_TYPE_HIGH_PRIORITY:\n                priority = android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY;\n                break;\n            case THREAD_TYPE_LOW_PRIORITY:\n                priority = android.os.Process.THREAD_PRIORITY_LOWEST;\n                break;\n            case THREAD_TYPE_NORMAL_PRIORITY:\n            default:\n                priority = android.os.Process.THREAD_PRIORITY_DEFAULT;\n                break;\n        }\n        String threadName = \"DFM Handler Thread #\"+priority;\n        mHandlerThread = new HandlerThread(threadName, priority);\n        mHandlerThread.start();\n        return mHandlerThread.getLooper();\n    }\n\n    private void prepare() {\n        if (handler == null)\n            handler = new DrawHandler(getLooper(mDrawingThreadType), this, mDanmakuVisible);\n    }\n\n    @Override\n    public void prepare(BaseDanmakuParser parser, DanmakuContext config) {\n        prepare();\n        handler.setConfig(config);\n        handler.setParser(parser);\n        handler.setCallback(mCallback);\n        handler.prepare();\n    }\n\n    @Override\n    public boolean isPrepared() {\n        return handler != null && handler.isPrepared();\n    }\n\n    @Override\n    public DanmakuContext getConfig() {\n        if (handler == null) {\n            return null;\n        }\n        return handler.getConfig();\n    }\n\n    @Override\n    public void showFPS(boolean show) {\n        mShowFps = show;\n    }\n    private static final int MAX_RECORD_SIZE = 50;\n    private static final int ONE_SECOND = 1000;\n    private LinkedList<Long> mDrawTimes;\n    private float fps() {\n        long lastTime = SystemClock.uptimeMillis();\n        mDrawTimes.addLast(lastTime);\n        Long first = mDrawTimes.peekFirst();\n        if (first == null) {\n            return 0.0f;\n        }\n        float dtime = lastTime - first;\n        int frames = mDrawTimes.size();\n        if (frames > MAX_RECORD_SIZE) {\n            mDrawTimes.removeFirst();\n        }\n        return dtime > 0 ? mDrawTimes.size() * ONE_SECOND / dtime : 0.0f;\n    }\n    \n    @Override\n    public synchronized long drawDanmakus() {\n        if (!isSurfaceCreated)\n            return 0;\n        long stime = SystemClock.uptimeMillis();\n        if (!isShown())\n            return -1;\n        long dtime = 0;\n        Canvas canvas = lockCanvas();\n        if (canvas != null) {\n            if (handler != null) {\n                RenderingState rs = handler.draw(canvas);\n                if (mShowFps) {\n                    if (mDrawTimes == null)\n                        mDrawTimes = new LinkedList<Long>();\n                    dtime = SystemClock.uptimeMillis() - stime;\n                    String fps = String.format(Locale.getDefault(),\n                            \"fps %.2f,time:%d s,cache:%d,miss:%d\", fps(), getCurrentTime() / 1000,\n                            rs.cacheHitCount, rs.cacheMissCount);\n                    DrawHelper.drawFPS(canvas, fps);\n                }\n            }\n            if (isSurfaceCreated)\n                unlockCanvasAndPost(canvas);\n        }\n        dtime = SystemClock.uptimeMillis() - stime;\n        return dtime;\n    }\n\n    public void toggle() {\n        if (isSurfaceCreated) {\n            if (handler == null)\n                start();\n            else if (handler.isStop()) {\n                resume();\n            } else\n                pause();\n        }\n    }\n\n    @Override\n    public void pause() {\n        if (handler != null)\n            handler.pause();\n    }\n\n    @Override\n    public void resume() {\n        if (handler != null && handler.isPrepared())\n            handler.resume();\n        else if (handler == null) {\n            restart();\n        }\n    }\n    \n    @Override\n    public boolean isPaused() {\n        if(handler != null) {\n            return handler.isStop();\n        }\n        return false;\n    }\n\n    public void restart() {\n        stop();\n        start();\n    }\n\n    @Override\n    public void start() {\n        start(0);\n    }\n\n    @Override\n    public void start(long postion) {\n        if (handler == null) {\n            prepare();\n        } else {\n            handler.removeCallbacksAndMessages(null);\n        }\n        handler.obtainMessage(DrawHandler.START, postion).sendToTarget();\n    }\n\n\t@Override\n    public boolean onTouchEvent(MotionEvent event) {\n        boolean isEventConsumed = mTouchHelper.onTouchEvent(event);\n        if (!isEventConsumed) {\n            return super.onTouchEvent(event);\n        }\n        return isEventConsumed;\n    }\n\n    public void seekTo(Long ms) {\n        if (handler != null) {\n            handler.seekTo(ms);\n        }\n    }\n\n    public void enableDanmakuDrawingCache(boolean enable) {\n        mEnableDanmakuDrwaingCache = enable;\n    }\n\n    @Override\n    public boolean isDanmakuDrawingCacheEnabled() {\n        return mEnableDanmakuDrwaingCache;\n    }\n\n    @Override\n    public boolean isViewReady() {\n        return isSurfaceCreated;\n    }\n\n    @Override\n    public int getViewWidth() {\n        return super.getWidth();\n    }\n\n    @Override\n    public int getViewHeight() {\n        return super.getHeight();\n    }\n\n    @Override\n    public View getView() {\n        return this;\n    }\n\n    @Override\n    public void show() {\n        showAndResumeDrawTask(null);\n    }\n    \n    @Override\n    public void showAndResumeDrawTask(Long position) {\n        mDanmakuVisible = true;\n        if (handler == null) {\n            return;\n        }\n        handler.showDanmakus(position);\n    }\n\n    @Override\n    public void hide() {\n        mDanmakuVisible = false;\n        if (handler == null) {\n            return;\n        }\n        handler.hideDanmakus(false);\n    }\n    \n    @Override\n    public long hideAndPauseDrawTask() {\n        mDanmakuVisible = false;\n        if (handler == null) {\n            return 0;\n        }\n        return handler.hideDanmakus(true);\n    }\n\n    @Override\n    public void setOnDanmakuClickListener(OnDanmakuClickListener listener) {\n        mOnDanmakuClickListener = listener;\n    }\n\n    @Override\n    public void setOnDanmakuClickListener(OnDanmakuClickListener listener, float xOff, float yOff) {\n        mOnDanmakuClickListener = listener;\n        mXOff = xOff;\n        mYOff = yOff;\n    }\n\n    @Override\n    public OnDanmakuClickListener getOnDanmakuClickListener() {\n        return mOnDanmakuClickListener;\n    }\n\n    @Override\n    public float getXOff() {\n        return mXOff;\n    }\n\n    @Override\n    public float getYOff() {\n        return mYOff;\n    }\n\n    @Override\n    public void forceRender() {\n\n    }\n\n    @Override\n    public synchronized void clear() {\n        if (!isViewReady()) {\n            return;\n        }        \n        Canvas canvas = lockCanvas();\n        if (canvas != null) {\n            DrawHelper.clearCanvas(canvas);\n            unlockCanvasAndPost(canvas);\n        }\n\n    }\n\n    @Override\n    public boolean isShown() {\n        return mDanmakuVisible && super.isShown();\n    }\n\n    @Override\n    public void setDrawingThreadType(int type) {\n        mDrawingThreadType = type;\n    }\n\n    @Override\n    public long getCurrentTime() {\n        if (handler != null) {\n            return handler.getCurrentTime();\n        }\n        return 0;\n    }\n\n    @Override\n    public boolean isHardwareAccelerated() {\n        return false;\n    }\n\n    @Override\n    public void clearDanmakusOnScreen() {\n        if (handler != null) {\n            handler.clearDanmakusOnScreen();\n        }\n    }\n\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/ui/widget/DanmakuTouchHelper.java",
    "content": "package master.flame.danmaku.ui.widget;\n\nimport android.graphics.RectF;\nimport android.nfc.Tag;\nimport android.util.Log;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\nimport android.view.View;\n\nimport master.flame.danmaku.controller.IDanmakuView;\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\nimport master.flame.danmaku.danmaku.model.IDanmakus;\nimport master.flame.danmaku.danmaku.model.android.Danmakus;\n\n/**\n * Created by kmfish on 2015/1/25.\n */\npublic class DanmakuTouchHelper {\n    private final GestureDetector mTouchDelegate;\n    private IDanmakuView danmakuView;\n    private RectF mDanmakuBounds;\n    private float mXOff;\n    private float mYOff;\n\n    private final android.view.GestureDetector.OnGestureListener mOnGestureListener = new GestureDetector.SimpleOnGestureListener() {\n        @Override\n        public boolean onDown(MotionEvent event) {\n            if (danmakuView != null) {\n                IDanmakuView.OnDanmakuClickListener onDanmakuClickListener = danmakuView.getOnDanmakuClickListener();\n                if (onDanmakuClickListener != null) {\n                    mXOff = danmakuView.getXOff();\n                    mYOff = danmakuView.getYOff();\n                    return true;\n                }\n            }\n            return false;\n        }\n\n        @Override\n        public boolean onSingleTapConfirmed(MotionEvent event) {\n            IDanmakus clickDanmakus = touchHitDanmaku(event.getX(), event.getY());\n            boolean isEventConsumed = false;\n            if (null != clickDanmakus && !clickDanmakus.isEmpty()) {\n                isEventConsumed = performDanmakuClick(clickDanmakus, false);\n            }\n            if (!isEventConsumed) {\n                isEventConsumed = performViewClick();\n            }\n            return isEventConsumed;\n        }\n\n        @Override\n        public void onLongPress(MotionEvent event) {\n            IDanmakuView.OnDanmakuClickListener onDanmakuClickListener = danmakuView.getOnDanmakuClickListener();\n            if (onDanmakuClickListener == null) {\n                return;\n            }\n            mXOff = danmakuView.getXOff();\n            mYOff = danmakuView.getYOff();\n            IDanmakus clickDanmakus = touchHitDanmaku(event.getX(), event.getY());\n            if (null != clickDanmakus && !clickDanmakus.isEmpty()) {\n                performDanmakuClick(clickDanmakus, true);\n            }\n        }\n    };\n\n    private DanmakuTouchHelper(IDanmakuView danmakuView) {\n        this.danmakuView = danmakuView;\n        this.mDanmakuBounds = new RectF();\n        this.mTouchDelegate = new GestureDetector(((View) danmakuView).getContext(), mOnGestureListener);\n    }\n\n    public static synchronized DanmakuTouchHelper instance(IDanmakuView danmakuView) {\n        return new DanmakuTouchHelper(danmakuView);\n    }\n\n    public boolean onTouchEvent(MotionEvent event) {\n        return mTouchDelegate.onTouchEvent(event);\n    }\n\n    private boolean performDanmakuClick(IDanmakus danmakus, boolean isLongClick) {\n        IDanmakuView.OnDanmakuClickListener onDanmakuClickListener = danmakuView.getOnDanmakuClickListener();\n        if (onDanmakuClickListener != null) {\n            if (isLongClick) {\n                return onDanmakuClickListener.onDanmakuLongClick(danmakus);\n            } else {\n                return onDanmakuClickListener.onDanmakuClick(danmakus);\n            }\n        }\n        return false;\n    }\n\n    private boolean performViewClick() {\n        IDanmakuView.OnDanmakuClickListener onDanmakuClickListener = danmakuView.getOnDanmakuClickListener();\n        if (onDanmakuClickListener != null) {\n            return onDanmakuClickListener.onViewClick(danmakuView);\n        }\n        return false;\n    }\n\n    private IDanmakus touchHitDanmaku(final float x, final float y) {\n        final IDanmakus hitDanmakus = new Danmakus();\n        mDanmakuBounds.setEmpty();\n\n        IDanmakus danmakus = danmakuView.getCurrentVisibleDanmakus();\n        if (null != danmakus && !danmakus.isEmpty()) {\n            danmakus.forEachSync(new IDanmakus.DefaultConsumer<BaseDanmaku>() {\n                @Override\n                public int accept(BaseDanmaku danmaku) {\n                    if (null != danmaku) {\n                        mDanmakuBounds.set(danmaku.getLeft(), danmaku.getTop(), danmaku.getRight(), danmaku.getBottom());\n                        if (mDanmakuBounds.intersect(x - mXOff, y - mYOff, x + mXOff, y + mYOff)) {\n                            hitDanmakus.addItem(danmaku);\n                        }\n                    }\n                    return ACTION_CONTINUE;\n                }\n            });\n        }\n\n        return hitDanmakus;\n    }\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/ui/widget/DanmakuView.java",
    "content": "/*\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage master.flame.danmaku.ui.widget;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.os.Build;\nimport android.os.Handler;\nimport android.os.HandlerThread;\nimport android.os.Looper;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\n\nimport java.util.LinkedList;\nimport java.util.Locale;\n\nimport master.flame.danmaku.controller.DrawHandler;\nimport master.flame.danmaku.controller.DrawHandler.Callback;\nimport master.flame.danmaku.controller.DrawHelper;\nimport master.flame.danmaku.controller.IDanmakuView;\nimport master.flame.danmaku.controller.IDanmakuViewController;\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\nimport master.flame.danmaku.danmaku.model.IDanmakus;\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext;\nimport master.flame.danmaku.danmaku.parser.BaseDanmakuParser;\nimport master.flame.danmaku.danmaku.renderer.IRenderer.RenderingState;\nimport master.flame.danmaku.danmaku.util.SystemClock;\n\npublic class DanmakuView extends View implements IDanmakuView, IDanmakuViewController {\n\n    public static final String TAG = \"DanmakuView\";\n\n    private Callback mCallback;\n\n    private HandlerThread mHandlerThread;\n\n    protected volatile DrawHandler handler;\n\n    private boolean isSurfaceCreated;\n\n    private boolean mEnableDanmakuDrwaingCache = true;\n\n    private OnDanmakuClickListener mOnDanmakuClickListener;\n\n    private float mXOff;\n\n    private float mYOff;\n\n    private OnClickListener mOnClickListener;\n\n    private DanmakuTouchHelper mTouchHelper;\n    \n    private boolean mShowFps;\n\n    private boolean mDanmakuVisible = true;\n\n    protected int mDrawingThreadType = THREAD_TYPE_NORMAL_PRIORITY;\n\n    private Object mDrawMonitor = new Object();\n\n    private boolean mDrawFinished = false;\n\n    protected boolean mRequestRender = false;\n\n    private long mUiThreadId;\n\n    public DanmakuView(Context context) {\n        super(context);\n        init();\n    }\n\n    private void init() {\n        mUiThreadId = Thread.currentThread().getId();\n        setBackgroundColor(Color.TRANSPARENT);\n        setDrawingCacheBackgroundColor(Color.TRANSPARENT);\n        DrawHelper.useDrawColorToClearCanvas(true, false);\n        mTouchHelper = DanmakuTouchHelper.instance(this);\n    }\n\n    public DanmakuView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public DanmakuView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init();\n    }\n\n    public void addDanmaku(BaseDanmaku item) {\n        if (handler != null) {\n            handler.addDanmaku(item);\n        }\n    }\n\n    @Override\n    public void invalidateDanmaku(BaseDanmaku item, boolean remeasure) {\n        if (handler != null) {\n            handler.invalidateDanmaku(item, remeasure);\n        }\n    }\n\n    @Override\n    public void removeAllDanmakus(boolean isClearDanmakusOnScreen) {\n        if (handler != null) {\n            handler.removeAllDanmakus(isClearDanmakusOnScreen);\n        }\n    }\n    \n    @Override\n    public void removeAllLiveDanmakus() {\n        if (handler != null) {\n            handler.removeAllLiveDanmakus();\n        }\n    }\n\n    @Override\n    public IDanmakus getCurrentVisibleDanmakus() {\n        if (handler != null) {\n            return handler.getCurrentVisibleDanmakus();\n        }\n\n        return null;\n    }\n\n    public void setCallback(Callback callback) {\n        mCallback = callback;\n        if (handler != null) {\n            handler.setCallback(callback);\n        }\n    }\n\n    @Override\n    public void release() {\n        stop();\n        if(mDrawTimes!= null) mDrawTimes.clear();\n    }\n\n    @Override\n    public void stop() {\n        stopDraw();\n    }\n\n    private synchronized void stopDraw() {\n        if (this.handler == null) {\n            return;\n        }\n        DrawHandler handler = this.handler;\n        this.handler = null;\n        unlockCanvasAndPost();\n        if (handler != null) {\n            handler.quit();\n        }\n        HandlerThread handlerThread = this.mHandlerThread;\n        mHandlerThread = null;\n        if (handlerThread != null) {\n            try {\n                handlerThread.join();\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n            handlerThread.quit();\n        }\n    }\n\n    protected synchronized Looper getLooper(int type) {\n        if (mHandlerThread != null) {\n            mHandlerThread.quit();\n            mHandlerThread = null;\n        }\n\n        int priority;\n        switch (type) {\n            case THREAD_TYPE_MAIN_THREAD:\n                return Looper.getMainLooper();\n            case THREAD_TYPE_HIGH_PRIORITY:\n                priority = android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY;\n                break;\n            case THREAD_TYPE_LOW_PRIORITY:\n                priority = android.os.Process.THREAD_PRIORITY_LOWEST;\n                break;\n            case THREAD_TYPE_NORMAL_PRIORITY:\n            default:\n                priority = android.os.Process.THREAD_PRIORITY_DEFAULT;\n                break;\n        }\n        String threadName = \"DFM Handler Thread #\" + priority;\n        mHandlerThread = new HandlerThread(threadName, priority);\n        mHandlerThread.start();\n        return mHandlerThread.getLooper();\n    }\n\n    private void prepare() {\n        if (handler == null)\n            handler = new DrawHandler(getLooper(mDrawingThreadType), this, mDanmakuVisible);\n    }\n\n    @Override\n    public void prepare(BaseDanmakuParser parser, DanmakuContext config) {\n        prepare();\n        handler.setConfig(config);\n        handler.setParser(parser);\n        handler.setCallback(mCallback);\n        handler.prepare();\n    }\n\n    @Override\n    public boolean isPrepared() {\n        return handler != null && handler.isPrepared();\n    }\n\n    @Override\n    public DanmakuContext getConfig() {\n        if (handler == null) {\n            return null;\n        }\n        return handler.getConfig();\n    }\n\n    @Override\n    public void showFPS(boolean show){\n        mShowFps = show;\n    }\n    private static final int MAX_RECORD_SIZE = 50;\n    private static final int ONE_SECOND = 1000;\n    private LinkedList<Long> mDrawTimes;\n\n    protected boolean mClearFlag;\n    private float fps() {\n        long lastTime = SystemClock.uptimeMillis();\n        mDrawTimes.addLast(lastTime);\n        Long first = mDrawTimes.peekFirst();\n        if (first == null) {\n            return 0.0f;\n        }\n        float dtime = lastTime - first;\n        int frames = mDrawTimes.size();\n        if (frames > MAX_RECORD_SIZE) {\n            mDrawTimes.removeFirst();\n        }\n        return dtime > 0 ? mDrawTimes.size() * ONE_SECOND / dtime : 0.0f;\n    }\n    @Override\n    public long drawDanmakus() {\n        if (!isSurfaceCreated)\n            return 0;\n        if (!isShown())\n            return -1;\n        long stime = SystemClock.uptimeMillis();\n        lockCanvas();\n        return SystemClock.uptimeMillis() - stime;\n    }\n    \n    @SuppressLint(\"NewApi\")\n    private void postInvalidateCompat() {\n        mRequestRender = true;\n        if(Build.VERSION.SDK_INT >= 16) {\n            this.postInvalidateOnAnimation();\n        } else {\n            this.postInvalidate();\n        }\n    }\n\n    protected void lockCanvas() {\n        if(mDanmakuVisible == false) {\n            return;\n        }\n        postInvalidateCompat();\n        synchronized (mDrawMonitor) {\n            while ((!mDrawFinished) && (handler != null)) {\n                try {\n                    mDrawMonitor.wait(200);\n                } catch (InterruptedException e) {\n                    if (mDanmakuVisible == false || handler == null || handler.isStop()) {\n                        break;\n                    } else {\n                        Thread.currentThread().interrupt();\n                    }\n                }\n            }\n            mDrawFinished = false;\n        }\n    }\n    \n    private void lockCanvasAndClear() {\n        mClearFlag = true;\n        lockCanvas();\n    }\n    \n    private void unlockCanvasAndPost() {\n        synchronized (mDrawMonitor) {\n            mDrawFinished = true;\n            mDrawMonitor.notifyAll();\n        }\n    }\n    \n    @Override\n    protected void onDraw(Canvas canvas) {\n        if ((!mDanmakuVisible) && (!mRequestRender)) {\n            super.onDraw(canvas);\n            return;\n        }\n        if (mClearFlag) {\n            DrawHelper.clearCanvas(canvas);\n            mClearFlag = false;\n        } else {\n            if (handler != null) {\n                RenderingState rs = handler.draw(canvas);\n                if (mShowFps) {\n                    if (mDrawTimes == null)\n                        mDrawTimes = new LinkedList<Long>();\n                    String fps = String.format(Locale.getDefault(),\n                            \"fps %.2f,time:%d s,cache:%d,miss:%d\", fps(), getCurrentTime() / 1000,\n                            rs.cacheHitCount, rs.cacheMissCount);\n                    DrawHelper.drawFPS(canvas, fps);\n                }\n            }\n        }\n        mRequestRender = false;\n        unlockCanvasAndPost();\n    }\n    \n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        super.onLayout(changed, left, top, right, bottom);\n        if (handler != null) {\n            handler.notifyDispSizeChanged(right - left, bottom - top);\n        }\n        isSurfaceCreated = true;\n    }\n\n    public void toggle() {\n        if (isSurfaceCreated) {\n            if (handler == null)\n                start();\n            else if (handler.isStop()) {\n                resume();\n            } else\n                pause();\n        }\n    }\n\n    @Override\n    public void pause() {\n        if (handler != null) {\n            handler.removeCallbacks(mResumeRunnable);\n            handler.pause();\n        }\n    }\n\n    private int mResumeTryCount = 0;\n\n    private Runnable mResumeRunnable = new Runnable() {\n        @Override\n        public void run() {\n            DrawHandler drawHandler = handler;\n            if (drawHandler == null) {\n                return;\n            }\n            mResumeTryCount++;\n            if (mResumeTryCount > 4 || DanmakuView.super.isShown()) {\n                drawHandler.resume();\n            } else {\n                drawHandler.postDelayed(this, 100 * mResumeTryCount);\n            }\n        }\n    };\n\n    @Override\n    public void resume() {\n        if (handler != null && handler.isPrepared()) {\n            mResumeTryCount = 0;\n            handler.post(mResumeRunnable);\n        }  else if (handler == null) {\n            restart();\n        }\n    }\n    \n    @Override\n    public boolean isPaused() {\n        if(handler != null) {\n            return handler.isStop();\n        }\n        return false;\n    }\n\n    public void restart() {\n        stop();\n        start();\n    }\n\n    @Override\n    public void start() {\n        start(0);\n    }\n\n    @Override\n    public void start(long position) {\n        Handler handler = this.handler;\n        if (handler == null) {\n            prepare();\n            handler = this.handler;\n        } else {\n            handler.removeCallbacksAndMessages(null);\n        }\n        if (handler != null) {\n            handler.obtainMessage(DrawHandler.START, position).sendToTarget();\n        }\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        boolean isEventConsumed = mTouchHelper.onTouchEvent(event);\n        if (!isEventConsumed) {\n            return super.onTouchEvent(event);\n        }\n        return isEventConsumed;\n    }\n\n    public void seekTo(Long ms) {\n        if(handler != null){\n            handler.seekTo(ms);\n        }\n    }\n\n\n    public void enableDanmakuDrawingCache(boolean enable) {\n        mEnableDanmakuDrwaingCache = enable;\n    }\n\n    @Override\n    public boolean isDanmakuDrawingCacheEnabled() {\n        return mEnableDanmakuDrwaingCache;\n    }\n\n    @Override\n    public boolean isViewReady() {\n        return isSurfaceCreated;\n    }\n\n    @Override\n    public int getViewWidth() {\n        return super.getWidth();\n    }\n\n    @Override\n    public int getViewHeight() {\n        return super.getHeight();\n    }\n\n    @Override\n    public View getView() {\n        return this;\n    }\n      \n    @Override\n    public void show() {\n        showAndResumeDrawTask(null);\n    }\n    \n    @Override\n    public void showAndResumeDrawTask(Long position) {\n        mDanmakuVisible = true;\n        mClearFlag = false;\n        if (handler == null) {\n            return;\n        }\n        handler.showDanmakus(position);\n    }\n\n    @Override\n    public void hide() {\n        mDanmakuVisible = false;\n        if (handler == null) {\n            return;\n        }\n        handler.hideDanmakus(false);\n    }\n    \n    @Override\n    public long hideAndPauseDrawTask() {\n        mDanmakuVisible = false;\n        if (handler == null) {\n            return 0;\n        }\n        return handler.hideDanmakus(true);\n    }\n\n    @Override\n    public void clear() {\n        if (!isViewReady()) {\n            return;\n        }\n        if (!mDanmakuVisible || Thread.currentThread().getId() == mUiThreadId) {\n            mClearFlag = true;\n            postInvalidateCompat();\n        } else {\n            lockCanvasAndClear();\n        }\n    }\n\n    @Override\n    public boolean isShown() {\n        return mDanmakuVisible && super.isShown();\n    }\n\n    @Override\n    public void setDrawingThreadType(int type) {\n        mDrawingThreadType  = type;\n    }\n\n    @Override\n    public long getCurrentTime() {\n        if (handler != null) {\n            return handler.getCurrentTime();\n        }\n        return 0;\n    }\n\n    @Override\n    @SuppressLint(\"NewApi\")\n    public boolean isHardwareAccelerated() {\n        // >= 3.0\n        if (Build.VERSION.SDK_INT >= 11) {\n            return super.isHardwareAccelerated();\n        } else {\n            return false;\n        }\n    }\n\n    @Override\n    public void clearDanmakusOnScreen() {\n        if (handler != null) {\n            handler.clearDanmakusOnScreen();\n        }\n    }\n\n    @Override\n    public void setOnDanmakuClickListener(OnDanmakuClickListener listener) {\n        mOnDanmakuClickListener = listener;\n    }\n\n    @Override\n    public void setOnDanmakuClickListener(OnDanmakuClickListener listener, float xOff, float yOff) {\n        mOnDanmakuClickListener = listener;\n        mXOff = xOff;\n        mYOff = yOff;\n    }\n\n    @Override\n    public OnDanmakuClickListener getOnDanmakuClickListener() {\n        return mOnDanmakuClickListener;\n    }\n\n    @Override\n    public float getXOff() {\n        return mXOff;\n    }\n\n    @Override\n    public float getYOff() {\n        return mYOff;\n    }\n\n    public void forceRender() {\n        mRequestRender = true;\n        handler.forceRender();\n    }\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/master/flame/danmaku/ui/widget/FakeDanmakuView.java",
    "content": "package master.flame.danmaku.ui.widget;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\n\nimport master.flame.danmaku.controller.DrawHandler;\nimport master.flame.danmaku.controller.DrawHelper;\nimport master.flame.danmaku.danmaku.model.AlphaValue;\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\nimport master.flame.danmaku.danmaku.model.DanmakuTimer;\nimport master.flame.danmaku.danmaku.model.Duration;\nimport master.flame.danmaku.danmaku.model.IDanmakus;\nimport master.flame.danmaku.danmaku.model.IDisplayer;\nimport master.flame.danmaku.danmaku.model.SpecialDanmaku;\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext;\nimport master.flame.danmaku.danmaku.model.android.DanmakuFactory;\nimport master.flame.danmaku.danmaku.model.android.Danmakus;\nimport master.flame.danmaku.danmaku.parser.BaseDanmakuParser;\nimport master.flame.danmaku.danmaku.util.DanmakuUtils;\n\n/**\n * Created by ch on 17/8/18.\n */\n\npublic class FakeDanmakuView extends DanmakuView implements DrawHandler.Callback {\n\n    private class CustomParser extends BaseDanmakuParser {\n        private final BaseDanmakuParser mBaseParser;\n        private final long stTime;\n        private final long edTime;\n        private float mDispScaleX, mDispScaleY;\n        private int mViewWidth;\n\n        public CustomParser(BaseDanmakuParser baseParser, long stTime, long edTime) {\n            this.mBaseParser = baseParser;\n            this.stTime = stTime;\n            this.edTime = edTime;\n        }\n\n        @Override\n        protected IDanmakus parse() {\n            final IDanmakus danmakus = new Danmakus();\n            IDanmakus subnew;\n            try {\n                subnew = this.mBaseParser.getDanmakus().subnew(this.stTime, this.edTime);\n            } catch (Exception e) {\n                subnew = this.mBaseParser.getDanmakus();\n            }\n            if (subnew == null) {\n                return danmakus;\n            }\n            subnew.forEach(new IDanmakus.Consumer<BaseDanmaku, Object>() {\n                @Override\n                public int accept(BaseDanmaku danmaku) {\n                    long time = danmaku.getTime();\n                    if (time < stTime) {\n                        return IDanmakus.Consumer.ACTION_CONTINUE;\n                    } else if (time > edTime) {\n                        return IDanmakus.Consumer.ACTION_BREAK;\n                    }\n                    BaseDanmaku item = mContext.mDanmakuFactory.createDanmaku(danmaku.getType(), mContext);\n                    if (item != null) {\n                        item.setTime(danmaku.getTime());\n                        DanmakuUtils.fillText(item, danmaku.text);\n                        item.textSize = danmaku.textSize;\n                        item.textColor = danmaku.textColor;\n                        item.textShadowColor = danmaku.textShadowColor;\n\n                        if (danmaku instanceof SpecialDanmaku) {\n                            SpecialDanmaku sdanmaku = (SpecialDanmaku) danmaku;\n                            item.index = danmaku.index;\n                            item.duration = new Duration(sdanmaku.getDuration());\n                            item.rotationZ = sdanmaku.rotateZ;\n                            item.rotationY = sdanmaku.rotationY;\n                            ((SpecialDanmaku) item).isQuadraticEaseOut = sdanmaku.isQuadraticEaseOut;\n\n                            mContext.mDanmakuFactory.fillTranslationData(item, sdanmaku.beginX,\n                                    sdanmaku.beginY, sdanmaku.endX, sdanmaku.endY, sdanmaku.translationDuration, sdanmaku.translationStartDelay, mDispScaleX, mDispScaleY);\n                            mContext.mDanmakuFactory.fillAlphaData(item, sdanmaku.beginAlpha, sdanmaku.endAlpha, item.getDuration());\n\n//                            mContext.mDanmakuFactory.fillLinePathData(item, points, mDispScaleX,\n//                                    mDispScaleY);  // FIXME\n                            return 0;  // FIXME skip special danmakus\n                        }\n\n                        item.setTimer(mTimer);\n                        item.mFilterParam = danmaku.mFilterParam;\n                        item.filterResetFlag = danmaku.filterResetFlag;\n                        item.flags = mContext.mGlobalFlagValues;\n                        Object lock = danmakus.obtainSynchronizer();\n                        synchronized (lock) {\n                            danmakus.addItem(item);\n                        }\n                    }\n                    return 0;\n                }\n            });\n            return danmakus;\n        }\n\n        @Override\n        public BaseDanmakuParser setDisplayer(IDisplayer disp) {\n            super.setDisplayer(disp);\n            if (mBaseParser == null || mBaseParser.getDisplayer() == null) {\n                return this;\n            }\n            mDispScaleX = mDispWidth / (float) mBaseParser.getDisplayer().getWidth();\n            mDispScaleY = mDispHeight / (float) mBaseParser.getDisplayer().getHeight();\n            if (mViewWidth <= 1) {\n                mViewWidth = disp.getWidth();\n            }\n            return this;\n        }\n\n        @Override\n        protected float getViewportSizeFactor() {\n            float scale = DanmakuFactory.COMMON_DANMAKU_DURATION * mViewWidth / DanmakuFactory.BILI_PLAYER_WIDTH;\n            float factor = 1.1f;\n            return mContext.mDanmakuFactory.MAX_DANMAKU_DURATION * factor / scale;\n        }\n    }\n\n    private DanmakuTimer mTimer;\n\n    public interface OnFrameAvailableListener {\n\n        void onConfig(DanmakuContext config);\n\n        void onFrameAvailable(long timeMills, Bitmap bitmap);\n\n        void onFramesFinished(long timeMills);\n\n        void onFailed(int errorCode, String msg);\n    }\n\n    private boolean mIsRelease;\n    private OnFrameAvailableListener mOnFrameAvailableListener;\n    private int mWidth = 0;\n    private int mHeight = 0;\n    private float mScale = 1f;\n    private DanmakuTimer mOuterTimer;\n    private long mBeginTimeMills;\n    private long mFrameIntervalMills = 16L;\n    private long mEndTimeMills;\n    private Bitmap mBufferBitmap;\n    private Canvas mBufferCanvas;\n\n    private int mRetryCount = 0;\n    private long mExpectBeginMills = 0;\n\n    public FakeDanmakuView(Context context) {\n        super(context);\n    }\n\n    public FakeDanmakuView(Context context, int width, int height, float scale) {\n        super(context);\n        mWidth = width;\n        mHeight = height;\n        mScale = scale;\n        initBufferCanvas(width, height);\n    }\n\n    public void initBufferCanvas(int width, int height) {\n        mBufferBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);\n        mBufferCanvas = new Canvas(mBufferBitmap);\n    }\n\n    @Override\n    public long drawDanmakus() {\n        if (mIsRelease) {\n            return 0;\n        }\n        Canvas canvas = mBufferCanvas;\n        if (canvas == null) {\n            return 0;\n        }\n        Bitmap bufferBitmap = this.mBufferBitmap;\n        if (bufferBitmap == null || bufferBitmap.isRecycled()) {\n            return 0;\n        }\n        bufferBitmap.eraseColor(Color.TRANSPARENT);\n        if (mClearFlag) {\n            DrawHelper.clearCanvas(canvas);\n            mClearFlag = false;\n        } else {\n            if (handler != null) {\n                handler.draw(canvas);\n            }\n        }\n        OnFrameAvailableListener onFrameAvailableListener = this.mOnFrameAvailableListener;\n        if (onFrameAvailableListener != null) {\n            long curr = mOuterTimer.currMillisecond;\n            try {\n                if (curr >= mExpectBeginMills - mFrameIntervalMills) {\n                    Bitmap bitmap;\n                    boolean recycle = false;\n                    if (mScale == 1f) {\n                        bitmap = bufferBitmap;\n                    } else {\n                        bitmap = Bitmap.createScaledBitmap(bufferBitmap, (int) (mWidth * mScale), (int) (mHeight * mScale), true);\n                        recycle = true;\n                    }\n                    onFrameAvailableListener.onFrameAvailable(curr, bitmap);\n                    if (recycle) {\n                        bitmap.recycle();\n                    }\n                }\n            } catch (Exception e) {\n                release();\n                onFrameAvailableListener.onFailed(101, e.getMessage());\n            } finally {\n                if (curr >= mEndTimeMills) {\n                    release();\n                    if (mTimer != null) {\n                        mTimer.update(mEndTimeMills);\n                    }\n                    onFrameAvailableListener.onFramesFinished(curr);\n                }\n            }\n        }\n        mRequestRender = false;\n        return 2;  // 固定频率\n    }\n\n    @Override\n    public void release() {\n        mIsRelease = true;\n        super.release();\n        mBufferBitmap = null;\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n\n    }\n\n    @Override\n    public boolean isShown() {\n        return true;\n    }\n\n    @Override\n    public boolean isViewReady() {\n        return true;\n    }\n\n    @Override\n    public int getViewWidth() {\n        return mWidth;\n    }\n\n    @Override\n    public int getViewHeight() {\n        return mHeight;\n    }\n\n    @Override\n    public void prepare(BaseDanmakuParser parser, DanmakuContext config) {\n        CustomParser newParser = new CustomParser(parser, mBeginTimeMills, mEndTimeMills);\n        DanmakuContext configCopy;\n        try {\n            configCopy = (DanmakuContext) config.clone();\n            configCopy.resetContext();\n            configCopy.transparency = AlphaValue.MAX;\n            configCopy.setDanmakuTransparency(config.transparency / (float) AlphaValue.MAX);\n            configCopy.mGlobalFlagValues.FILTER_RESET_FLAG = config.mGlobalFlagValues.FILTER_RESET_FLAG;\n            configCopy.setDanmakuSync(null);\n            configCopy.unregisterAllConfigChangedCallbacks();\n            configCopy.mGlobalFlagValues.updateAll();\n        } catch (CloneNotSupportedException e) {\n            e.printStackTrace();\n            configCopy = config;\n        }\n        configCopy.updateMethod = 1;\n        if (mOnFrameAvailableListener != null) {\n            mOnFrameAvailableListener.onConfig(configCopy);\n        }\n        super.prepare(newParser, configCopy);\n        handler.setIdleSleep(false);\n        handler.enableNonBlockMode(true);\n    }\n\n    public void setTimeRange(final long beginMills, final long endMills) {\n        mExpectBeginMills = beginMills;\n        mBeginTimeMills = Math.max(0, beginMills - 30000L); // FIXME: 17/8/23 magic code 30000L\n        mEndTimeMills = endMills;\n    }\n\n    public void setOnFrameAvailableListener(OnFrameAvailableListener onFrameAvailableListener) {\n        mOnFrameAvailableListener = onFrameAvailableListener;\n    }\n\n    public void getFrameAtTime(final int frameRate) {\n        if (mRetryCount++ > 5) {\n            release();\n            if (mOnFrameAvailableListener != null) {\n                mOnFrameAvailableListener.onFailed(100, \"not prepared\");\n            }\n            return;\n        }\n        if (!isPrepared()) {\n            DrawHandler handler = this.handler;\n            if (handler == null) {\n                return;\n            }\n            handler.postDelayed(new Runnable() {\n                @Override\n                public void run() {\n                    getFrameAtTime(frameRate);\n                }\n            }, 1000L);\n            return;\n        }\n        mFrameIntervalMills = 1000 / frameRate;\n        setCallback(this);\n        long beginMills = Math.max(0, mExpectBeginMills - getConfig().mDanmakuFactory.MAX_DANMAKU_DURATION * 3 / 2);\n        mOuterTimer = new DanmakuTimer(beginMills);\n        start(beginMills);\n    }\n\n    @Override\n    public void prepared() {\n\n    }\n\n    @Override\n    public void updateTimer(DanmakuTimer timer) {\n        mTimer = timer;\n        timer.update(mOuterTimer.currMillisecond);\n        mOuterTimer.add(mFrameIntervalMills);\n        timer.add(mFrameIntervalMills);\n    }\n\n    @Override\n    public void danmakuShown(BaseDanmaku danmaku) {\n\n    }\n\n    @Override\n    public void drawingFinished() {\n\n    }\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/tv/cjump/jni/DeviceUtils.java",
    "content": "\npackage tv.cjump.jni;\n\nimport android.os.Build;\nimport android.os.Environment;\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.RandomAccessFile;\nimport java.lang.reflect.Field;\n\npublic class DeviceUtils {\n\n    public static final String ABI_X86 = \"x86\";\n\n    public static final String ABI_MIPS = \"mips\";\n\n    public static enum ARCH {\n        Unknown, ARM, X86, MIPS, ARM64,\n    }\n\n    private static ARCH sArch = ARCH.Unknown;\n\n    // see include/​uapi/​linux/​elf-em.h\n    private static final int EM_ARM = 40;\n    private static final int EM_386 = 3;\n    private static final int EM_MIPS = 8;\n    private static final int EM_AARCH64 = 183;\n\n    // /system/lib/libc.so\n    // XXX: need a runtime check\n    public static synchronized ARCH getMyCpuArch() {\n        byte[] data = new byte[20];\n        File libc = new File(Environment.getRootDirectory(), \"lib/libc.so\");\n        if (libc.canRead()) {\n            RandomAccessFile fp = null;\n            try {\n                fp = new RandomAccessFile(libc, \"r\");\n                fp.readFully(data);\n                int machine = (data[19] << 8) | data[18];\n                switch (machine) {\n                    case EM_ARM:\n                        sArch = ARCH.ARM;\n                        break;\n                    case EM_386:\n                        sArch = ARCH.X86;\n                        break;\n                    case EM_MIPS:\n                        sArch = ARCH.MIPS;\n                        break;\n                    case EM_AARCH64:\n                        sArch = ARCH.ARM64;\n                        break;\n                    default:\n                        Log.e(\"NativeBitmapFactory\", \"libc.so is unknown arch: \" + Integer.toHexString(machine));\n                        break;\n                }\n            } catch (FileNotFoundException e) {\n                e.printStackTrace();\n            } catch (IOException e) {\n                e.printStackTrace();\n            } finally {\n                if (fp != null) {\n                    try {\n                        fp.close();\n                    } catch (IOException e) {\n                        e.printStackTrace();\n                    }\n                }\n            }\n        }\n        return sArch;\n    }\n\n    public static String get_CPU_ABI() {\n        return Build.CPU_ABI;\n    }\n\n    public static String get_CPU_ABI2() {\n        try {\n            Field field = Build.class.getDeclaredField(\"CPU_ABI2\");\n            if (field == null)\n                return null;\n\n            Object fieldValue = field.get(null);\n            if (!(fieldValue instanceof String)) {\n                return null;\n            }\n\n            return (String) fieldValue;\n        } catch (Exception e) {\n\n        }\n\n        return null;\n    }\n\n    public static boolean supportABI(String requestAbi) {\n        String abi = get_CPU_ABI();\n        if (!TextUtils.isEmpty(abi) && abi.equalsIgnoreCase(requestAbi))\n            return true;\n\n        String abi2 = get_CPU_ABI2();\n        return !TextUtils.isEmpty(abi2) && abi.equalsIgnoreCase(requestAbi);\n\n    }\n\n    public static boolean supportX86() {\n        return supportABI(ABI_X86);\n    }\n\n    public static boolean supportMips() {\n        return supportABI(ABI_MIPS);\n    }\n\n    public static boolean isARMSimulatedByX86() {\n        ARCH arch = getMyCpuArch();\n        return !supportX86() && ARCH.X86.equals(arch);\n    }\n\n    public static boolean isMiBox2Device() {\n        String manufacturer = Build.MANUFACTURER;\n        String productName = Build.PRODUCT;\n        return manufacturer.equalsIgnoreCase(\"Xiaomi\")\n            && productName.equalsIgnoreCase(\"dredd\");\n    }\n\n    public static boolean isMagicBoxDevice() {\n        String manufacturer = Build.MANUFACTURER;\n        String productName = Build.PRODUCT;\n        return manufacturer.equalsIgnoreCase(\"MagicBox\")\n            && productName.equalsIgnoreCase(\"MagicBox\");\n    }\n\n    public static boolean isProblemBoxDevice() {\n        return isMiBox2Device() || isMagicBoxDevice();\n    }\n\n    public static boolean isRealARMArch() {\n        ARCH arch = getMyCpuArch();\n        return (supportABI(\"armeabi-v7a\") || supportABI(\"armeabi\")) && ARCH.ARM.equals(arch);\n    }\n\n    public static boolean isRealX86Arch() {\n        ARCH arch = getMyCpuArch();\n        return supportABI(ABI_X86) || ARCH.X86.equals(arch);\n    }\n\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/java/tv/cjump/jni/NativeBitmapFactory.java",
    "content": "\npackage tv.cjump.jni;\n\nimport android.annotation.SuppressLint;\nimport android.graphics.Bitmap;\nimport android.graphics.Bitmap.Config;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.util.Log;\n\nimport java.lang.reflect.Field;\n\npublic class NativeBitmapFactory {\n\n    static Field nativeIntField = null;\n\n    static boolean nativeLibLoaded = false;\n    static boolean notLoadAgain = false;\n    \n    public static boolean isInNativeAlloc() {\n        return android.os.Build.VERSION.SDK_INT < 11 || (nativeLibLoaded && nativeIntField != null);\n    }\n\n    public static void loadLibs() {\n        if (notLoadAgain) {\n            return;\n        }\n        if (!(DeviceUtils.isRealARMArch() || DeviceUtils.isRealX86Arch())) {\n            notLoadAgain = true;\n            nativeLibLoaded = false;\n            return;\n        }\n        if (nativeLibLoaded) {\n            return;\n        }\n        try {\n            if (android.os.Build.VERSION.SDK_INT >= 11 && android.os.Build.VERSION.SDK_INT < 23) {\n                System.loadLibrary(\"ndkbitmap\");\n                nativeLibLoaded = true;\n            } else {\n                notLoadAgain = true;\n                nativeLibLoaded = false;\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n            notLoadAgain = true;\n            nativeLibLoaded = false;\n        } catch (Error e) {\n            e.printStackTrace();\n            notLoadAgain = true;\n            nativeLibLoaded = false;\n        }\n        if (nativeLibLoaded) {\n            boolean libInit = init();\n            if (!libInit) {\n                release();\n                notLoadAgain = true;\n                nativeLibLoaded = false;\n            } else {\n                initField();\n                boolean confirm = testLib();\n                if (!confirm) {\n                    // 测试so文件函数是否调用失败\n                    release();\n                    notLoadAgain = true;\n                    nativeLibLoaded = false;\n                }\n            }\n        }\n\n        Log.e(\"NativeBitmapFactory\", \"loaded\" + nativeLibLoaded);\n    }\n\n    public static synchronized void releaseLibs() {\n        boolean loaded =  nativeLibLoaded;\n        nativeIntField = null;\n        nativeLibLoaded = false;\n        if (loaded) {\n            release();\n        }\n        // Log.e(\"NativeBitmapFactory\", \"released\");\n    }\n\n    static void initField() {\n        try {\n            nativeIntField = Bitmap.Config.class.getDeclaredField(\"nativeInt\");\n            nativeIntField.setAccessible(true);\n        } catch (NoSuchFieldException e) {\n            nativeIntField = null;\n            e.printStackTrace();\n        }\n    }\n\n    @SuppressLint(\"NewApi\")\n    private static boolean testLib() {\n        if (nativeIntField == null) {\n            return false;\n        }\n        Bitmap bitmap = null;\n        Canvas canvas = null;\n        try {\n            bitmap = createNativeBitmap(2, 2, Bitmap.Config.ARGB_8888, true);\n            boolean result = (bitmap != null && bitmap.getWidth() == 2 && bitmap.getHeight() == 2);\n            if (result) {\n                if (android.os.Build.VERSION.SDK_INT >= 17 && !bitmap.isPremultiplied()) {\n                    bitmap.setPremultiplied(true);\n                }\n                canvas = new Canvas(bitmap);\n                Paint paint = new Paint();\n                paint.setColor(Color.RED);\n                paint.setTextSize(20f);\n                canvas.drawRect(0f, 0f, (float) bitmap.getWidth(), (float) bitmap.getHeight(),\n                        paint);\n                canvas.drawText(\"TestLib\", 0, 0, paint);\n                if (android.os.Build.VERSION.SDK_INT >= 17) {\n                    result = bitmap.isPremultiplied();\n                }\n            }\n            return result;\n        } catch (Exception e) {\n            Log.e(\"NativeBitmapFactory\", \"exception:\" + e.toString());\n            return false;\n        } catch (Error e) {\n            return false;\n        } finally {\n            if (bitmap != null) {\n                bitmap.recycle();\n                bitmap = null;\n            }\n        }\n    }\n\n    public static int getNativeConfig(Bitmap.Config config) {\n        try {\n            if (nativeIntField == null) {\n                return 0;\n            }\n            return nativeIntField.getInt(config);\n        } catch (IllegalArgumentException e) {\n            e.printStackTrace();\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        }\n        return 0;\n    }\n\n    public static Bitmap createBitmap(int width, int height, Bitmap.Config config) {\n        return createBitmap(width, height, config, config.equals(Bitmap.Config.ARGB_4444) || config.equals(Bitmap.Config.ARGB_8888));\n    }\n\n    public static void recycle(Bitmap bitmap) {\n        bitmap.recycle();\n    }\n\n    public static synchronized Bitmap createBitmap(int width, int height, Bitmap.Config config, boolean hasAlpha) {\n        if (!nativeLibLoaded || nativeIntField == null) {\n            // Log.e(\"NativeBitmapFactory\", \"ndk bitmap create failed\");\n            return Bitmap.createBitmap(width, height, config);\n        }\n        return createNativeBitmap(width, height, config, hasAlpha);\n    }\n\n    private static Bitmap createNativeBitmap(int width, int height, Config config, boolean hasAlpha) {\n        int nativeConfig = getNativeConfig(config);\n        // Log.e(\"NativeBitmapFactory\", \"nativeConfig:\" + nativeConfig);\n        // Log.e(\"NativeBitmapFactory\", \"create bitmap:\" + bitmap);\n        return android.os.Build.VERSION.SDK_INT == 19 ? createBitmap19(width, height,\n                nativeConfig, hasAlpha) : createBitmap(width, height, nativeConfig, hasAlpha);\n    }\n\n    // ///////////native methods//////////\n\n    private static native boolean init();\n\n    private static native boolean release();\n\n    private static native Bitmap createBitmap(int width, int height, int nativeConfig,\n            boolean hasAlpha);\n\n    private static native Bitmap createBitmap19(int width, int height, int nativeConfig,\n            boolean hasAlpha);\n\n}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/lint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<lint>\n</lint>"
  },
  {
    "path": "DanmakuFlameMaster/src/main/proguard-project.txt",
    "content": "# To enable ProGuard in your project, edit project.properties\n# to define the proguard.config property as described in that file.\n#\n# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in ${sdk.dir}/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the ProGuard\n# include property in project.properties.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "DanmakuFlameMaster/src/main/project.properties",
    "content": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# This file must be checked in Version Control Systems.\n#\n# To customize properties used by the Ant build system edit\n# \"ant.properties\", and override values to adapt the script to your\n# project structure.\n#\n# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):\n#proguard.config=${sdk.dir}\\tools\\proguard\\proguard-android.txt:proguard-project.txt\n\n# Project target.\ntarget=android-19\nandroid.library=true\nsource.dir=java\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\r\nVersion 2.0, January 2004\r\nhttp://www.apache.org/licenses/\r\n\r\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\r\n\r\n1. Definitions.\r\n\r\n\"License\" shall mean the terms and conditions for use, reproduction, and\r\ndistribution as defined by Sections 1 through 9 of this document.\r\n\r\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright\r\nowner that is granting the License.\r\n\r\n\"Legal Entity\" shall mean the union of the acting entity and all other entities\r\nthat control, are controlled by, or are under common control with that entity.\r\nFor the purposes of this definition, \"control\" means (i) the power, direct or\r\nindirect, to cause the direction or management of such entity, whether by\r\ncontract or otherwise, or (ii) ownership of fifty percent (50%) or more of the\r\noutstanding shares, or (iii) beneficial ownership of such entity.\r\n\r\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising\r\npermissions granted by this License.\r\n\r\n\"Source\" form shall mean the preferred form for making modifications, including\r\nbut not limited to software source code, documentation source, and configuration\r\nfiles.\r\n\r\n\"Object\" form shall mean any form resulting from mechanical transformation or\r\ntranslation of a Source form, including but not limited to compiled object code,\r\ngenerated documentation, and conversions to other media types.\r\n\r\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made\r\navailable under the License, as indicated by a copyright notice that is included\r\nin or attached to the work (an example is provided in the Appendix below).\r\n\r\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that\r\nis based on (or derived from) the Work and for which the editorial revisions,\r\nannotations, elaborations, or other modifications represent, as a whole, an\r\noriginal work of authorship. For the purposes of this License, Derivative Works\r\nshall not include works that remain separable from, or merely link (or bind by\r\nname) to the interfaces of, the Work and Derivative Works thereof.\r\n\r\n\"Contribution\" shall mean any work of authorship, including the original version\r\nof the Work and any modifications or additions to that Work or Derivative Works\r\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\r\nby the copyright owner or by an individual or Legal Entity authorized to submit\r\non behalf of the copyright owner. For the purposes of this definition,\r\n\"submitted\" means any form of electronic, verbal, or written communication sent\r\nto the Licensor or its representatives, including but not limited to\r\ncommunication on electronic mailing lists, source code control systems, and\r\nissue tracking systems that are managed by, or on behalf of, the Licensor for\r\nthe purpose of discussing and improving the Work, but excluding communication\r\nthat is conspicuously marked or otherwise designated in writing by the copyright\r\nowner as \"Not a Contribution.\"\r\n\r\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf\r\nof whom a Contribution has been received by Licensor and subsequently\r\nincorporated within the Work.\r\n\r\n2. Grant of Copyright License.\r\n\r\nSubject to the terms and conditions of this License, each Contributor hereby\r\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\r\nirrevocable copyright license to reproduce, prepare Derivative Works of,\r\npublicly display, publicly perform, sublicense, and distribute the Work and such\r\nDerivative Works in Source or Object form.\r\n\r\n3. Grant of Patent License.\r\n\r\nSubject to the terms and conditions of this License, each Contributor hereby\r\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\r\nirrevocable (except as stated in this section) patent license to make, have\r\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\r\nsuch license applies only to those patent claims licensable by such Contributor\r\nthat are necessarily infringed by their Contribution(s) alone or by combination\r\nof their Contribution(s) with the Work to which such Contribution(s) was\r\nsubmitted. If You institute patent litigation against any entity (including a\r\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\r\nContribution incorporated within the Work constitutes direct or contributory\r\npatent infringement, then any patent licenses granted to You under this License\r\nfor that Work shall terminate as of the date such litigation is filed.\r\n\r\n4. Redistribution.\r\n\r\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\r\nin any medium, with or without modifications, and in Source or Object form,\r\nprovided that You meet the following conditions:\r\n\r\nYou must give any other recipients of the Work or Derivative Works a copy of\r\nthis License; and\r\nYou must cause any modified files to carry prominent notices stating that You\r\nchanged the files; and\r\nYou must retain, in the Source form of any Derivative Works that You distribute,\r\nall copyright, patent, trademark, and attribution notices from the Source form\r\nof the Work, excluding those notices that do not pertain to any part of the\r\nDerivative Works; and\r\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any\r\nDerivative Works that You distribute must include a readable copy of the\r\nattribution notices contained within such NOTICE file, excluding those notices\r\nthat do not pertain to any part of the Derivative Works, in at least one of the\r\nfollowing places: within a NOTICE text file distributed as part of the\r\nDerivative Works; within the Source form or documentation, if provided along\r\nwith the Derivative Works; or, within a display generated by the Derivative\r\nWorks, if and wherever such third-party notices normally appear. The contents of\r\nthe NOTICE file are for informational purposes only and do not modify the\r\nLicense. You may add Your own attribution notices within Derivative Works that\r\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\r\nprovided that such additional attribution notices cannot be construed as\r\nmodifying the License.\r\nYou may add Your own copyright statement to Your modifications and may provide\r\nadditional or different license terms and conditions for use, reproduction, or\r\ndistribution of Your modifications, or for any such Derivative Works as a whole,\r\nprovided Your use, reproduction, and distribution of the Work otherwise complies\r\nwith the conditions stated in this License.\r\n\r\n5. Submission of Contributions.\r\n\r\nUnless You explicitly state otherwise, any Contribution intentionally submitted\r\nfor inclusion in the Work by You to the Licensor shall be under the terms and\r\nconditions of this License, without any additional terms or conditions.\r\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\r\nany separate license agreement you may have executed with Licensor regarding\r\nsuch Contributions.\r\n\r\n6. Trademarks.\r\n\r\nThis License does not grant permission to use the trade names, trademarks,\r\nservice marks, or product names of the Licensor, except as required for\r\nreasonable and customary use in describing the origin of the Work and\r\nreproducing the content of the NOTICE file.\r\n\r\n7. Disclaimer of Warranty.\r\n\r\nUnless required by applicable law or agreed to in writing, Licensor provides the\r\nWork (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\r\nincluding, without limitation, any warranties or conditions of TITLE,\r\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\r\nsolely responsible for determining the appropriateness of using or\r\nredistributing the Work and assume any risks associated with Your exercise of\r\npermissions under this License.\r\n\r\n8. Limitation of Liability.\r\n\r\nIn no event and under no legal theory, whether in tort (including negligence),\r\ncontract, or otherwise, unless required by applicable law (such as deliberate\r\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\r\nliable to You for damages, including any direct, indirect, special, incidental,\r\nor consequential damages of any character arising as a result of this License or\r\nout of the use or inability to use the Work (including but not limited to\r\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\r\nany and all other commercial damages or losses), even if such Contributor has\r\nbeen advised of the possibility of such damages.\r\n\r\n9. Accepting Warranty or Additional Liability.\r\n\r\nWhile redistributing the Work or Derivative Works thereof, You may choose to\r\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\r\nother liability obligations and/or rights consistent with this License. However,\r\nin accepting such obligations, You may act only on Your own behalf and on Your\r\nsole responsibility, not on behalf of any other Contributor, and only if You\r\nagree to indemnify, defend, and hold each Contributor harmless for any liability\r\nincurred by, or claims asserted against, such Contributor by reason of your\r\naccepting any such warranty or additional liability.\r\n\r\nEND OF TERMS AND CONDITIONS\r\n\r\nAPPENDIX: How to apply the Apache License to your work\r\n\r\nTo apply the Apache License to your work, attach the following boilerplate\r\nnotice, with the fields enclosed by brackets \"[]\" replaced with your own\r\nidentifying information. (Don't include the brackets!) The text should be\r\nenclosed in the appropriate comment syntax for the file format. We also\r\nrecommend that a file or class name and description of purpose be included on\r\nthe same \"printed page\" as the copyright notice for easier identification within\r\nthird-party archives.\r\n\r\n   Copyright [yyyy] [name of copyright owner]\r\n\r\n   Licensed under the Apache License, Version 2.0 (the \"License\");\r\n   you may not use this file except in compliance with the License.\r\n   You may obtain a copy of the License at\r\n\r\n     http://www.apache.org/licenses/LICENSE-2.0\r\n\r\n   Unless required by applicable law or agreed to in writing, software\r\n   distributed under the License is distributed on an \"AS IS\" BASIS,\r\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n   See the License for the specific language governing permissions and\r\n   limitations under the License.\r\n"
  },
  {
    "path": "README.md",
    "content": "DanmakuFlameMaster\n==================\n\nandroid上开源弹幕解析绘制引擎项目。[![Build Status](https://travis-ci.org/Bilibili/DanmakuFlameMaster.png?branch=master)](https://travis-ci.org/Bilibili/DanmakuFlameMaster)\n\n### DFM Inside: \n[![bili](https://raw.github.com/ctiao/ctiao.github.io/master/images/apps/bili.png?raw=true)](https://play.google.com/store/apps/details?id=tv.danmaku.bili)\n\n- libndkbitmap.so(ndk)源码：https://github.com/Bilibili/NativeBitmapFactory\n- 开发交流群：314468823 (加入请注明DFM开发交流)\n\n### Features\n\n- 使用多种方式(View/SurfaceView/TextureView)实现高效绘制\n\n- B站xml弹幕格式解析\n\n- 基础弹幕精确还原绘制\n\n- 支持mode7特殊弹幕\n\n- 多核机型优化，高效的预缓存机制\n\n- 支持多种显示效果选项实时切换\n\n- 实时弹幕显示支持\n\n- 换行弹幕支持/运动弹幕支持\n\n- 支持自定义字体\n\n- 支持多种弹幕参数设置\n\n- 支持多种方式的弹幕屏蔽\n\n### TODO:\n\n- 增加OpenGL ES绘制方式\n\n\n### Download\nDownload the [latest version][1] or grab via Maven:\n\n```xml\n<dependency>\n  <groupId>com.github.ctiao</groupId>\n  <artifactId>dfm</artifactId>\n  <version>0.9.25</version>\n</dependency>\n```\n\nor Gradle:\n```groovy\nrepositories {\n    jcenter()\n}\n\ndependencies {\n    compile 'com.github.ctiao:DanmakuFlameMaster:0.9.25'\n    compile 'com.github.ctiao:ndkbitmap-armv7a:0.9.21'\n\n    # Other ABIs: optional\n    compile 'com.github.ctiao:ndkbitmap-armv5:0.9.21'\n    compile 'com.github.ctiao:ndkbitmap-x86:0.9.21'\n}\n```\nSnapshots of the development version are available in [Sonatype's snapshots repository][2].\n\n\n### License\n    Copyright (C) 2013-2015 Chen Hui <calmer91@gmail.com>\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n\n\n[1]:https://oss.sonatype.org/#nexus-search;gav~com.github.ctiao~dfm~~~\n[2]:https://oss.sonatype.org/content/repositories/snapshots/\n"
  },
  {
    "path": "Sample/build.gradle",
    "content": "/*\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\napply plugin: 'com.android.application'\n\ndependencies {\n    compile project(':ndkbitmap-armv5')\n    compile project(':ndkbitmap-armv7a')\n    compile project(':ndkbitmap-x86')\n    compile project(':DanmakuFlameMaster')\n\n    compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'\n//    compile 'tv.danmaku.ijk.media:ijkplayer-arm64:0.6.2'\n}\nandroid {\n    compileSdkVersion 25\n    buildToolsVersion \"26.0.2\"\n\n    defaultConfig {\n        minSdkVersion 9\n        targetSdkVersion 25\n        versionCode 1\n        versionName \"1.0\"\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_7\n        targetCompatibility JavaVersion.VERSION_1_7\n    }\n}\n"
  },
  {
    "path": "Sample/src/main/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<classpath>\r\n\t<classpathentry kind=\"src\" path=\"java\"/>\r\n\t<classpathentry kind=\"src\" path=\"gen\"/>\r\n\t<classpathentry kind=\"con\" path=\"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK\"/>\r\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.LIBRARIES\"/>\r\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.DEPENDENCIES\"/>\r\n\t<classpathentry kind=\"output\" path=\"bin/classes\"/>\r\n</classpath>\r\n"
  },
  {
    "path": "Sample/src/main/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<projectDescription>\r\n\t<name>Sample</name>\r\n\t<comment></comment>\r\n\t<projects>\r\n\t</projects>\r\n\t<buildSpec>\r\n\t\t<buildCommand>\r\n\t\t\t<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>\r\n\t\t\t<arguments>\r\n\t\t\t</arguments>\r\n\t\t</buildCommand>\r\n\t\t<buildCommand>\r\n\t\t\t<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>\r\n\t\t\t<arguments>\r\n\t\t\t</arguments>\r\n\t\t</buildCommand>\r\n\t\t<buildCommand>\r\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\r\n\t\t\t<arguments>\r\n\t\t\t</arguments>\r\n\t\t</buildCommand>\r\n\t\t<buildCommand>\r\n\t\t\t<name>com.android.ide.eclipse.adt.ApkBuilder</name>\r\n\t\t\t<arguments>\r\n\t\t\t</arguments>\r\n\t\t</buildCommand>\r\n\t</buildSpec>\r\n\t<natures>\r\n\t\t<nature>com.android.ide.eclipse.adt.AndroidNature</nature>\r\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\r\n\t</natures>\r\n</projectDescription>\r\n"
  },
  {
    "path": "Sample/src/main/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6\norg.eclipse.jdt.core.compiler.compliance=1.6\norg.eclipse.jdt.core.compiler.source=1.6\n"
  },
  {
    "path": "Sample/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    package=\"com.sample\"\r\n    android:versionCode=\"1\"\r\n    android:versionName=\"1.0\" >\r\n\r\n    <uses-sdk\r\n        android:minSdkVersion=\"9\"\r\n        android:targetSdkVersion=\"22\" />\r\n\r\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>\r\n    <uses-permission android:name=\"android.permission.INTERNET\" />\r\n\r\n    <application\r\n        android:allowBackup=\"true\"\r\n        android:hardwareAccelerated=\"true\"\r\n        android:icon=\"@drawable/ic_launcher\"\r\n        android:label=\"@string/app_name\"\r\n        android:theme=\"@style/AppTheme\" >\r\n        <activity\r\n            android:name=\".MainActivity\"\r\n            android:configChanges=\"keyboardHidden|orientation|screenSize\"\r\n            android:label=\"@string/app_name\"\r\n            android:screenOrientation=\"landscape\"\r\n            android:theme=\"@android:style/Theme.NoTitleBar.Fullscreen\" >\r\n            <intent-filter>\r\n                <action android:name=\"android.intent.action.MAIN\" />\r\n\r\n                <category android:name=\"android.intent.category.LAUNCHER\" />\r\n            </intent-filter>\r\n        </activity>\r\n    </application>\r\n\r\n</manifest>"
  },
  {
    "path": "Sample/src/main/java/com/sample/BiliDanmukuParser.java",
    "content": "/*\n * Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.sample;\n\nimport android.graphics.Color;\nimport android.text.TextUtils;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.xml.sax.Attributes;\nimport org.xml.sax.InputSource;\nimport org.xml.sax.SAXException;\nimport org.xml.sax.XMLReader;\nimport org.xml.sax.helpers.DefaultHandler;\nimport org.xml.sax.helpers.XMLReaderFactory;\n\nimport java.io.IOException;\nimport java.util.Locale;\n\nimport master.flame.danmaku.danmaku.model.AlphaValue;\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\nimport master.flame.danmaku.danmaku.model.Duration;\nimport master.flame.danmaku.danmaku.model.IDisplayer;\nimport master.flame.danmaku.danmaku.model.SpecialDanmaku;\nimport master.flame.danmaku.danmaku.model.android.Danmakus;\nimport master.flame.danmaku.danmaku.parser.BaseDanmakuParser;\nimport master.flame.danmaku.danmaku.model.android.DanmakuFactory;\nimport master.flame.danmaku.danmaku.parser.android.AndroidFileSource;\nimport master.flame.danmaku.danmaku.util.DanmakuUtils;\n\nimport static master.flame.danmaku.danmaku.model.IDanmakus.ST_BY_TIME;\n\npublic class BiliDanmukuParser extends BaseDanmakuParser {\n\n    static {\n        System.setProperty(\"org.xml.sax.driver\", \"org.xmlpull.v1.sax2.Driver\");\n    }\n\n    protected float mDispScaleX;\n    protected float mDispScaleY;\n\n    @Override\n    public Danmakus parse() {\n\n        if (mDataSource != null) {\n            AndroidFileSource source = (AndroidFileSource) mDataSource;\n            try {\n                XMLReader xmlReader = XMLReaderFactory.createXMLReader();\n                XmlContentHandler contentHandler = new XmlContentHandler();\n                xmlReader.setContentHandler(contentHandler);\n                xmlReader.parse(new InputSource(source.data()));\n                return contentHandler.getResult();\n            } catch (SAXException e) {\n                e.printStackTrace();\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n\n        }\n\n        return null;\n    }\n\n    public class XmlContentHandler extends DefaultHandler {\n\n        private static final String TRUE_STRING = \"true\";\n\n        public Danmakus result;\n\n        public BaseDanmaku item = null;\n\n        public boolean completed = false;\n\n        public int index = 0;\n\n        public Danmakus getResult() {\n            return result;\n        }\n\n        @Override\n        public void startDocument() throws SAXException {\n            result = new Danmakus(ST_BY_TIME, false, mContext.getBaseComparator());\n        }\n\n        @Override\n        public void endDocument() throws SAXException {\n            completed = true;\n        }\n\n        @Override\n        public void startElement(String uri, String localName, String qName, Attributes attributes)\n                throws SAXException {\n            String tagName = localName.length() != 0 ? localName : qName;\n            tagName = tagName.toLowerCase(Locale.getDefault()).trim();\n            if (tagName.equals(\"d\")) {\n                // <d p=\"23.826000213623,1,25,16777215,1422201084,0,057075e9,757076900\">我从未见过如此厚颜无耻之猴</d>\n                // 0:时间(弹幕出现时间)\n                // 1:类型(1从右至左滚动弹幕|6从左至右滚动弹幕|5顶端固定弹幕|4底端固定弹幕|7高级弹幕|8脚本弹幕)\n                // 2:字号\n                // 3:颜色\n                // 4:时间戳 ?\n                // 5:弹幕池id\n                // 6:用户hash\n                // 7:弹幕id\n                String pValue = attributes.getValue(\"p\");\n                // parse p value to danmaku\n                String[] values = pValue.split(\",\");\n                if (values.length > 0) {\n                    long time = (long) (parseFloat(values[0]) * 1000); // 出现时间\n                    int type = parseInteger(values[1]); // 弹幕类型\n                    float textSize = parseFloat(values[2]); // 字体大小\n                    int color = (int) ((0x00000000ff000000 | parseLong(values[3])) & 0x00000000ffffffff); // 颜色\n                    // int poolType = parseInteger(values[5]); // 弹幕池类型（忽略\n                    item = mContext.mDanmakuFactory.createDanmaku(type, mContext);\n                    if (item != null) {\n                        item.setTime(time);\n                        item.textSize = textSize * (mDispDensity - 0.6f);\n                        item.textColor = color;\n                        item.textShadowColor = color <= Color.BLACK ? Color.WHITE : Color.BLACK;\n                    }\n                }\n            }\n        }\n\n        @Override\n        public void endElement(String uri, String localName, String qName) throws SAXException {\n            if (item != null && item.text != null) {\n                if (item.duration != null) {\n                    String tagName = localName.length() != 0 ? localName : qName;\n                    if (tagName.equalsIgnoreCase(\"d\")) {\n                        item.setTimer(mTimer);\n                        item.flags = mContext.mGlobalFlagValues;\n                        Object lock = result.obtainSynchronizer();\n                        synchronized (lock) {\n                            result.addItem(item);\n                        }\n                    }\n                }\n                item = null;\n            }\n        }\n\n        @Override\n        public void characters(char[] ch, int start, int length) {\n            if (item != null) {\n                DanmakuUtils.fillText(item, decodeXmlString(new String(ch, start, length)));\n                item.index = index++;\n\n                // initial specail danmaku data\n                String text = String.valueOf(item.text).trim();\n                if (item.getType() == BaseDanmaku.TYPE_SPECIAL && text.startsWith(\"[\")\n                        && text.endsWith(\"]\")) {\n                    //text = text.substring(1, text.length() - 1);\n                    String[] textArr = null;//text.split(\",\", -1);\n                    try {\n                        JSONArray jsonArray = new JSONArray(text);\n                        textArr = new String[jsonArray.length()];\n                        for (int i = 0; i < textArr.length; i++) {\n                            textArr[i] = jsonArray.getString(i);\n                        }\n                    } catch (JSONException e) {\n                        e.printStackTrace();\n                    }\n\n                    if (textArr == null || textArr.length < 5 || TextUtils.isEmpty(textArr[4])) {\n                        item = null;\n                        return;\n                    }\n                    DanmakuUtils.fillText(item, textArr[4]);\n                    float beginX = parseFloat(textArr[0]);\n                    float beginY = parseFloat(textArr[1]);\n                    float endX = beginX;\n                    float endY = beginY;\n                    String[] alphaArr = textArr[2].split(\"-\");\n                    int beginAlpha = (int) (AlphaValue.MAX * parseFloat(alphaArr[0]));\n                    int endAlpha = beginAlpha;\n                    if (alphaArr.length > 1) {\n                        endAlpha = (int) (AlphaValue.MAX * parseFloat(alphaArr[1]));\n                    }\n                    long alphaDuraion = (long) (parseFloat(textArr[3]) * 1000);\n                    long translationDuration = alphaDuraion;\n                    long translationStartDelay = 0;\n                    float rotateY = 0, rotateZ = 0;\n                    if (textArr.length >= 7) {\n                        rotateZ = parseFloat(textArr[5]);\n                        rotateY = parseFloat(textArr[6]);\n                    }\n                    if (textArr.length >= 11) {\n                        endX = parseFloat(textArr[7]);\n                        endY = parseFloat(textArr[8]);\n                        if (!\"\".equals(textArr[9])) {\n                            translationDuration = parseInteger(textArr[9]);\n                        }\n                        if (!\"\".equals(textArr[10])) {\n                            translationStartDelay = (long) (parseFloat(textArr[10]));\n                        }\n                    }\n                    if (isPercentageNumber(textArr[0])) {\n                        beginX *= DanmakuFactory.BILI_PLAYER_WIDTH;\n                    }\n                    if (isPercentageNumber(textArr[1])) {\n                        beginY *= DanmakuFactory.BILI_PLAYER_HEIGHT;\n                    }\n                    if (textArr.length >= 8 && isPercentageNumber(textArr[7])) {\n                        endX *= DanmakuFactory.BILI_PLAYER_WIDTH;\n                    }\n                    if (textArr.length >= 9 && isPercentageNumber(textArr[8])) {\n                        endY *= DanmakuFactory.BILI_PLAYER_HEIGHT;\n                    }\n                    item.duration = new Duration(alphaDuraion);\n                    item.rotationZ = rotateZ;\n                    item.rotationY = rotateY;\n                    mContext.mDanmakuFactory.fillTranslationData(item, beginX,\n                            beginY, endX, endY, translationDuration, translationStartDelay, mDispScaleX, mDispScaleY);\n                    mContext.mDanmakuFactory.fillAlphaData(item, beginAlpha, endAlpha, alphaDuraion);\n\n                    if (textArr.length >= 12) {\n                        // 是否有描边\n                        if (!TextUtils.isEmpty(textArr[11]) && TRUE_STRING.equalsIgnoreCase(textArr[11])) {\n                            item.textShadowColor = Color.TRANSPARENT;\n                        }\n                    }\n                    if (textArr.length >= 13) {\n                        //TODO 字体 textArr[12]\n                    }\n                    if (textArr.length >= 14) {\n                        // Linear.easeIn or Quadratic.easeOut\n                        ((SpecialDanmaku) item).isQuadraticEaseOut = (\"0\".equals(textArr[13]));\n                    }\n                    if (textArr.length >= 15) {\n                        // 路径数据\n                        if (!\"\".equals(textArr[14])) {\n                            String motionPathString = textArr[14].substring(1);\n                            if (!TextUtils.isEmpty(motionPathString)) {\n                                String[] pointStrArray = motionPathString.split(\"L\");\n                                if (pointStrArray.length > 0) {\n                                    float[][] points = new float[pointStrArray.length][2];\n                                    for (int i = 0; i < pointStrArray.length; i++) {\n                                        String[] pointArray = pointStrArray[i].split(\",\");\n                                        if (pointArray.length >= 2) {\n                                            points[i][0] = parseFloat(pointArray[0]);\n                                            points[i][1] = parseFloat(pointArray[1]);\n                                        }\n                                    }\n                                    mContext.mDanmakuFactory.fillLinePathData(item, points, mDispScaleX,\n                                            mDispScaleY);\n                                }\n                            }\n                        }\n                    }\n                }\n\n            }\n        }\n\n        private String decodeXmlString(String title) {\n            if (title.contains(\"&amp;\")) {\n                title = title.replace(\"&amp;\", \"&\");\n            }\n            if (title.contains(\"&quot;\")) {\n                title = title.replace(\"&quot;\", \"\\\"\");\n            }\n            if (title.contains(\"&gt;\")) {\n                title = title.replace(\"&gt;\", \">\");\n            }\n            if (title.contains(\"&lt;\")) {\n                title = title.replace(\"&lt;\", \"<\");\n            }\n            return title;\n        }\n\n    }\n\n    private boolean isPercentageNumber(String number) {\n        //return number >= 0f && number <= 1f;\n        return number != null && number.contains(\".\");\n    }\n\n    private float parseFloat(String floatStr) {\n        try {\n            return Float.parseFloat(floatStr);\n        } catch (NumberFormatException e) {\n            return 0.0f;\n        }\n    }\n\n    private int parseInteger(String intStr) {\n        try {\n            return Integer.parseInt(intStr);\n        } catch (NumberFormatException e) {\n            return 0;\n        }\n    }\n    \n    private long parseLong(String longStr) {\n        try {\n            return Long.parseLong(longStr);\n        } catch (NumberFormatException e) {\n            return 0;\n        }\n    }\n\n    @Override\n    public BaseDanmakuParser setDisplayer(IDisplayer disp) {\n        super.setDisplayer(disp);\n        mDispScaleX = mDispWidth / DanmakuFactory.BILI_PLAYER_WIDTH;\n        mDispScaleY = mDispHeight / DanmakuFactory.BILI_PLAYER_HEIGHT;\n        return this;\n    }\n}\n"
  },
  {
    "path": "Sample/src/main/java/com/sample/MainActivity.java",
    "content": "\r\npackage com.sample;\r\n\r\nimport android.app.Activity;\r\nimport android.content.pm.ActivityInfo;\r\nimport android.content.res.Configuration;\r\nimport android.graphics.Canvas;\r\nimport android.graphics.Color;\r\nimport android.graphics.Paint;\r\nimport android.graphics.drawable.BitmapDrawable;\r\nimport android.graphics.drawable.Drawable;\r\nimport android.media.MediaPlayer;\r\nimport android.os.Bundle;\r\nimport android.os.Environment;\r\nimport master.flame.danmaku.danmaku.util.SystemClock;\r\nimport android.text.Spannable;\r\nimport android.text.SpannableStringBuilder;\r\nimport android.text.Spanned;\r\nimport android.text.TextPaint;\r\nimport android.text.style.BackgroundColorSpan;\r\nimport android.text.style.ImageSpan;\r\nimport android.util.Log;\r\nimport android.view.Menu;\r\nimport android.view.View;\r\nimport android.widget.Button;\r\nimport android.widget.PopupWindow;\r\nimport android.widget.VideoView;\r\n\r\nimport java.io.IOException;\r\nimport java.io.InputStream;\r\nimport java.net.MalformedURLException;\r\nimport java.net.URL;\r\nimport java.net.URLConnection;\r\nimport java.util.HashMap;\r\nimport java.util.Timer;\r\nimport java.util.TimerTask;\r\n\r\nimport master.flame.danmaku.controller.IDanmakuView;\r\nimport master.flame.danmaku.danmaku.loader.ILoader;\r\nimport master.flame.danmaku.danmaku.loader.IllegalDataException;\r\nimport master.flame.danmaku.danmaku.loader.android.DanmakuLoaderFactory;\r\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\r\nimport master.flame.danmaku.danmaku.model.DanmakuTimer;\r\nimport master.flame.danmaku.danmaku.model.IDanmakus;\r\nimport master.flame.danmaku.danmaku.model.IDisplayer;\r\nimport master.flame.danmaku.danmaku.model.android.BaseCacheStuffer;\r\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext;\r\nimport master.flame.danmaku.danmaku.model.android.Danmakus;\r\nimport master.flame.danmaku.danmaku.model.android.SpannedCacheStuffer;\r\nimport master.flame.danmaku.danmaku.parser.BaseDanmakuParser;\r\nimport master.flame.danmaku.danmaku.parser.IDataSource;\r\nimport master.flame.danmaku.danmaku.util.IOUtils;\r\n\r\npublic class MainActivity extends Activity implements View.OnClickListener {\r\n\r\n    private IDanmakuView mDanmakuView;\r\n\r\n    private View mMediaController;\r\n\r\n    public PopupWindow mPopupWindow;\r\n\r\n    private Button mBtnRotate;\r\n\r\n    private Button mBtnHideDanmaku;\r\n\r\n    private Button mBtnShowDanmaku;\r\n\r\n    private BaseDanmakuParser mParser;\r\n\r\n    private Button mBtnPauseDanmaku;\r\n\r\n    private Button mBtnResumeDanmaku;\r\n\r\n    private Button mBtnSendDanmaku;\r\n\r\n    private Button mBtnSendDanmakuTextAndImage;\r\n\r\n    private Button mBtnSendDanmakus;\r\n    private DanmakuContext mContext;\r\n    private BaseCacheStuffer.Proxy mCacheStufferAdapter = new BaseCacheStuffer.Proxy() {\r\n\r\n        private Drawable mDrawable;\r\n\r\n        @Override\r\n        public void prepareDrawing(final BaseDanmaku danmaku, boolean fromWorkerThread) {\r\n            if (danmaku.text instanceof Spanned) { // 根据你的条件检查是否需要需要更新弹幕\r\n                // FIXME 这里只是简单启个线程来加载远程url图片，请使用你自己的异步线程池，最好加上你的缓存池\r\n                new Thread() {\r\n\r\n                    @Override\r\n                    public void run() {\r\n                        String url = \"http://www.bilibili.com/favicon.ico\";\r\n                        InputStream inputStream = null;\r\n                        Drawable drawable = mDrawable;\r\n                        if(drawable == null) {\r\n                            try {\r\n                                URLConnection urlConnection = new URL(url).openConnection();\r\n                                inputStream = urlConnection.getInputStream();\r\n                                drawable = BitmapDrawable.createFromStream(inputStream, \"bitmap\");\r\n                                mDrawable = drawable;\r\n                            } catch (MalformedURLException e) {\r\n                                e.printStackTrace();\r\n                            } catch (IOException e) {\r\n                                e.printStackTrace();\r\n                            } finally {\r\n                                IOUtils.closeQuietly(inputStream);\r\n                            }\r\n                        }\r\n                        if (drawable != null) {\r\n                            drawable.setBounds(0, 0, 100, 100);\r\n                            SpannableStringBuilder spannable = createSpannable(drawable);\r\n                            danmaku.text = spannable;\r\n                            if(mDanmakuView != null) {\r\n                                mDanmakuView.invalidateDanmaku(danmaku, false);\r\n                            }\r\n                            return;\r\n                        }\r\n                    }\r\n                }.start();\r\n            }\r\n        }\r\n\r\n        @Override\r\n        public void releaseResource(BaseDanmaku danmaku) {\r\n            // TODO 重要:清理含有ImageSpan的text中的一些占用内存的资源 例如drawable\r\n        }\r\n    };\r\n\r\n    /**\r\n     * 绘制背景(自定义弹幕样式)\r\n     */\r\n    private static class BackgroundCacheStuffer extends SpannedCacheStuffer {\r\n        // 通过扩展SimpleTextCacheStuffer或SpannedCacheStuffer个性化你的弹幕样式\r\n        final Paint paint = new Paint();\r\n\r\n        @Override\r\n        public void measure(BaseDanmaku danmaku, TextPaint paint, boolean fromWorkerThread) {\r\n            danmaku.padding = 10;  // 在背景绘制模式下增加padding\r\n            super.measure(danmaku, paint, fromWorkerThread);\r\n        }\r\n\r\n        @Override\r\n        public void drawBackground(BaseDanmaku danmaku, Canvas canvas, float left, float top) {\r\n            paint.setColor(0x8125309b);\r\n            canvas.drawRect(left + 2, top + 2, left + danmaku.paintWidth - 2, top + danmaku.paintHeight - 2, paint);\r\n        }\r\n\r\n        @Override\r\n        public void drawStroke(BaseDanmaku danmaku, String lineText, Canvas canvas, float left, float top, Paint paint) {\r\n            // 禁用描边绘制\r\n        }\r\n    }\r\n\r\n    @Override\r\n    protected void onCreate(Bundle savedInstanceState) {\r\n        super.onCreate(savedInstanceState);\r\n        setContentView(R.layout.activity_main);\r\n        findViews();\r\n    }\r\n\r\n    private BaseDanmakuParser createParser(InputStream stream) {\r\n\r\n        if (stream == null) {\r\n            return new BaseDanmakuParser() {\r\n\r\n                @Override\r\n                protected Danmakus parse() {\r\n                    return new Danmakus();\r\n                }\r\n            };\r\n        }\r\n\r\n        ILoader loader = DanmakuLoaderFactory.create(DanmakuLoaderFactory.TAG_BILI);\r\n\r\n        try {\r\n            loader.load(stream);\r\n        } catch (IllegalDataException e) {\r\n            e.printStackTrace();\r\n        }\r\n        BaseDanmakuParser parser = new BiliDanmukuParser();\r\n        IDataSource<?> dataSource = loader.getDataSource();\r\n        parser.load(dataSource);\r\n        return parser;\r\n\r\n    }\r\n\r\n    private void findViews() {\r\n\r\n        mMediaController = findViewById(R.id.media_controller);\r\n        mBtnRotate = (Button) findViewById(R.id.rotate);\r\n        mBtnHideDanmaku = (Button) findViewById(R.id.btn_hide);\r\n        mBtnShowDanmaku = (Button) findViewById(R.id.btn_show);\r\n        mBtnPauseDanmaku = (Button) findViewById(R.id.btn_pause);\r\n        mBtnResumeDanmaku = (Button) findViewById(R.id.btn_resume);\r\n        mBtnSendDanmaku = (Button) findViewById(R.id.btn_send);\r\n        mBtnSendDanmakuTextAndImage = (Button) findViewById(R.id.btn_send_image_text);\r\n        mBtnSendDanmakus = (Button) findViewById(R.id.btn_send_danmakus);\r\n        mBtnRotate.setOnClickListener(this);\r\n        mBtnHideDanmaku.setOnClickListener(this);\r\n        mMediaController.setOnClickListener(this);\r\n        mBtnShowDanmaku.setOnClickListener(this);\r\n        mBtnPauseDanmaku.setOnClickListener(this);\r\n        mBtnResumeDanmaku.setOnClickListener(this);\r\n        mBtnSendDanmaku.setOnClickListener(this);\r\n        mBtnSendDanmakuTextAndImage.setOnClickListener(this);\r\n        mBtnSendDanmakus.setOnClickListener(this);\r\n\r\n        // VideoView\r\n        VideoView mVideoView = (VideoView) findViewById(R.id.videoview);\r\n        // DanmakuView\r\n\r\n        // 设置最大显示行数\r\n        HashMap<Integer, Integer> maxLinesPair = new HashMap<Integer, Integer>();\r\n        maxLinesPair.put(BaseDanmaku.TYPE_SCROLL_RL, 5); // 滚动弹幕最大显示5行\r\n        // 设置是否禁止重叠\r\n        HashMap<Integer, Boolean> overlappingEnablePair = new HashMap<Integer, Boolean>();\r\n        overlappingEnablePair.put(BaseDanmaku.TYPE_SCROLL_RL, true);\r\n        overlappingEnablePair.put(BaseDanmaku.TYPE_FIX_TOP, true);\r\n\r\n        mDanmakuView = (IDanmakuView) findViewById(R.id.sv_danmaku);\r\n        mContext = DanmakuContext.create();\r\n        mContext.setDanmakuStyle(IDisplayer.DANMAKU_STYLE_STROKEN, 3).setDuplicateMergingEnabled(false).setScrollSpeedFactor(1.2f).setScaleTextSize(1.2f)\r\n        .setCacheStuffer(new SpannedCacheStuffer(), mCacheStufferAdapter) // 图文混排使用SpannedCacheStuffer\r\n//        .setCacheStuffer(new BackgroundCacheStuffer())  // 绘制背景使用BackgroundCacheStuffer\r\n        .setMaximumLines(maxLinesPair)\r\n        .preventOverlapping(overlappingEnablePair).setDanmakuMargin(40);\r\n        if (mDanmakuView != null) {\r\n            mParser = createParser(this.getResources().openRawResource(R.raw.comments));\r\n            mDanmakuView.setCallback(new master.flame.danmaku.controller.DrawHandler.Callback() {\r\n                @Override\r\n                public void updateTimer(DanmakuTimer timer) {\r\n                }\r\n\r\n                @Override\r\n                public void drawingFinished() {\r\n\r\n                }\r\n\r\n                @Override\r\n                public void danmakuShown(BaseDanmaku danmaku) {\r\n//                    Log.d(\"DFM\", \"danmakuShown(): text=\" + danmaku.text);\r\n                }\r\n\r\n                @Override\r\n                public void prepared() {\r\n                    mDanmakuView.start();\r\n                }\r\n            });\r\n            mDanmakuView.setOnDanmakuClickListener(new IDanmakuView.OnDanmakuClickListener() {\r\n\r\n                @Override\r\n                public boolean onDanmakuClick(IDanmakus danmakus) {\r\n                    Log.d(\"DFM\", \"onDanmakuClick: danmakus size:\" + danmakus.size());\r\n                    BaseDanmaku latest = danmakus.last();\r\n                    if (null != latest) {\r\n                        Log.d(\"DFM\", \"onDanmakuClick: text of latest danmaku:\" + latest.text);\r\n                        return true;\r\n                    }\r\n                    return false;\r\n                }\r\n\r\n                @Override\r\n                public boolean onDanmakuLongClick(IDanmakus danmakus) {\r\n                    return false;\r\n                }\r\n\r\n                @Override\r\n                public boolean onViewClick(IDanmakuView view) {\r\n                    mMediaController.setVisibility(View.VISIBLE);\r\n                    return false;\r\n                }\r\n            });\r\n            mDanmakuView.prepare(mParser, mContext);\r\n            mDanmakuView.showFPS(true);\r\n            mDanmakuView.enableDanmakuDrawingCache(true);\r\n        }\r\n\r\n        if (mVideoView != null) {\r\n            mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {\r\n                @Override\r\n                public void onPrepared(MediaPlayer mediaPlayer) {\r\n                    mediaPlayer.start();\r\n                }\r\n            });\r\n            mVideoView.setVideoPath(Environment.getExternalStorageDirectory() + \"/1.flv\");\r\n        }\r\n\r\n    }\r\n\r\n    @Override\r\n    protected void onPause() {\r\n        super.onPause();\r\n        if (mDanmakuView != null && mDanmakuView.isPrepared()) {\r\n            mDanmakuView.pause();\r\n        }\r\n    }\r\n\r\n    @Override\r\n    protected void onResume() {\r\n        super.onResume();\r\n        if (mDanmakuView != null && mDanmakuView.isPrepared() && mDanmakuView.isPaused()) {\r\n            mDanmakuView.resume();\r\n        }\r\n    }\r\n\r\n    @Override\r\n    protected void onDestroy() {\r\n        super.onDestroy();\r\n        if (mDanmakuView != null) {\r\n            // dont forget release!\r\n            mDanmakuView.release();\r\n            mDanmakuView = null;\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void onBackPressed() {\r\n        super.onBackPressed();\r\n        if (mDanmakuView != null) {\r\n            // dont forget release!\r\n            mDanmakuView.release();\r\n            mDanmakuView = null;\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public boolean onCreateOptionsMenu(Menu menu) {\r\n        getMenuInflater().inflate(R.menu.main, menu);\r\n        return true;\r\n    }\r\n\r\n    @Override\r\n    public void onClick(View v) {\r\n        if (v == mMediaController) {\r\n            mMediaController.setVisibility(View.GONE);\r\n        }\r\n        if (mDanmakuView == null || !mDanmakuView.isPrepared())\r\n            return;\r\n        if (v == mBtnRotate) {\r\n            setRequestedOrientation(getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);\r\n        } else if (v == mBtnHideDanmaku) {\r\n            mDanmakuView.hide();\r\n            // mPausedPosition = mDanmakuView.hideAndPauseDrawTask();\r\n        } else if (v == mBtnShowDanmaku) {\r\n            mDanmakuView.show();\r\n            // mDanmakuView.showAndResumeDrawTask(mPausedPosition); // sync to the video time in your practice\r\n        } else if (v == mBtnPauseDanmaku) {\r\n            mDanmakuView.pause();\r\n        } else if (v == mBtnResumeDanmaku) {\r\n            mDanmakuView.resume();\r\n        } else if (v == mBtnSendDanmaku) {\r\n            addDanmaku(false);\r\n        } else if (v == mBtnSendDanmakuTextAndImage) {\r\n            addDanmaKuShowTextAndImage(false);\r\n        } else if (v == mBtnSendDanmakus) {\r\n            Boolean b = (Boolean) mBtnSendDanmakus.getTag();\r\n            timer.cancel();\r\n            if (b == null || !b) {\r\n                mBtnSendDanmakus.setText(R.string.cancel_sending_danmakus);\r\n                timer = new Timer();\r\n                timer.schedule(new AsyncAddTask(), 0, 1000);\r\n                mBtnSendDanmakus.setTag(true);\r\n            } else {\r\n                mBtnSendDanmakus.setText(R.string.send_danmakus);\r\n                mBtnSendDanmakus.setTag(false);\r\n            }\r\n        }\r\n    }\r\n\r\n    Timer timer = new Timer();\r\n\r\n    class AsyncAddTask extends TimerTask {\r\n\r\n        @Override\r\n        public void run() {\r\n            for (int i = 0; i < 20; i++) {\r\n                addDanmaku(true);\r\n                SystemClock.sleep(20);\r\n            }\r\n        }\r\n    };\r\n\r\n    private void addDanmaku(boolean islive) {\r\n        BaseDanmaku danmaku = mContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);\r\n        if (danmaku == null || mDanmakuView == null) {\r\n            return;\r\n        }\r\n        // for(int i=0;i<100;i++){\r\n        // }\r\n        danmaku.text = \"这是一条弹幕\" + System.nanoTime();\r\n        danmaku.padding = 5;\r\n        danmaku.priority = 0;  // 可能会被各种过滤器过滤并隐藏显示\r\n        danmaku.isLive = islive;\r\n        danmaku.setTime(mDanmakuView.getCurrentTime() + 1200);\r\n        danmaku.textSize = 25f * (mParser.getDisplayer().getDensity() - 0.6f);\r\n        danmaku.textColor = Color.RED;\r\n        danmaku.textShadowColor = Color.WHITE;\r\n        // danmaku.underlineColor = Color.GREEN;\r\n        danmaku.borderColor = Color.GREEN;\r\n        mDanmakuView.addDanmaku(danmaku);\r\n\r\n    }\r\n\r\n    private void addDanmaKuShowTextAndImage(boolean islive) {\r\n        BaseDanmaku danmaku = mContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);\r\n        Drawable drawable = getResources().getDrawable(R.drawable.ic_launcher);\r\n        drawable.setBounds(0, 0, 100, 100);\r\n        SpannableStringBuilder spannable = createSpannable(drawable);\r\n        danmaku.text = spannable;\r\n        danmaku.padding = 5;\r\n        danmaku.priority = 1;  // 一定会显示, 一般用于本机发送的弹幕\r\n        danmaku.isLive = islive;\r\n        danmaku.setTime(mDanmakuView.getCurrentTime() + 1200);\r\n        danmaku.textSize = 25f * (mParser.getDisplayer().getDensity() - 0.6f);\r\n        danmaku.textColor = Color.RED;\r\n        danmaku.textShadowColor = 0; // 重要：如果有图文混排，最好不要设置描边(设textShadowColor=0)，否则会进行两次复杂的绘制导致运行效率降低\r\n        danmaku.underlineColor = Color.GREEN;\r\n        mDanmakuView.addDanmaku(danmaku);\r\n    }\r\n\r\n    private SpannableStringBuilder createSpannable(Drawable drawable) {\r\n        String text = \"bitmap\";\r\n        SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(text);\r\n        ImageSpan span = new ImageSpan(drawable);//ImageSpan.ALIGN_BOTTOM);\r\n        spannableStringBuilder.setSpan(span, 0, text.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);\r\n        spannableStringBuilder.append(\"图文混排\");\r\n        spannableStringBuilder.setSpan(new BackgroundColorSpan(Color.parseColor(\"#8A2233B1\")), 0, spannableStringBuilder.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\r\n        return spannableStringBuilder;\r\n    }\r\n\r\n    @Override\r\n    public void onConfigurationChanged(Configuration newConfig) {\r\n        super.onConfigurationChanged(newConfig);\r\n        if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {\r\n            mDanmakuView.getConfig().setDanmakuMargin(20);\r\n        } else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {\r\n            mDanmakuView.getConfig().setDanmakuMargin(40);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "Sample/src/main/java/com/sample/UglyViewCacheStufferSampleActivity.java",
    "content": "\npackage com.sample;\n\nimport android.app.Activity;\nimport android.content.pm.ActivityInfo;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.media.MediaPlayer;\nimport android.os.Bundle;\nimport android.os.Environment;\nimport android.text.Spannable;\nimport android.text.SpannableStringBuilder;\nimport android.text.Spanned;\nimport android.text.TextPaint;\nimport android.text.style.BackgroundColorSpan;\nimport android.text.style.ImageSpan;\nimport android.util.Log;\nimport android.util.TypedValue;\nimport android.view.Menu;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.PopupWindow;\nimport android.widget.TextView;\nimport android.widget.VideoView;\n\nimport com.nostra13.universalimageloader.cache.memory.impl.LruMemoryCache;\nimport com.nostra13.universalimageloader.core.ImageLoader;\nimport com.nostra13.universalimageloader.core.ImageLoaderConfiguration;\nimport com.nostra13.universalimageloader.core.assist.ImageSize;\nimport com.nostra13.universalimageloader.core.assist.ViewScaleType;\nimport com.nostra13.universalimageloader.core.imageaware.NonViewAware;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.ref.WeakReference;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLConnection;\nimport java.util.HashMap;\nimport java.util.Timer;\nimport java.util.TimerTask;\n\nimport master.flame.danmaku.controller.IDanmakuView;\nimport master.flame.danmaku.danmaku.loader.ILoader;\nimport master.flame.danmaku.danmaku.loader.IllegalDataException;\nimport master.flame.danmaku.danmaku.loader.android.DanmakuLoaderFactory;\nimport master.flame.danmaku.danmaku.model.BaseDanmaku;\nimport master.flame.danmaku.danmaku.model.DanmakuTimer;\nimport master.flame.danmaku.danmaku.model.IDanmakus;\nimport master.flame.danmaku.danmaku.model.IDisplayer;\nimport master.flame.danmaku.danmaku.model.android.AndroidDisplayer;\nimport master.flame.danmaku.danmaku.model.android.BaseCacheStuffer;\nimport master.flame.danmaku.danmaku.model.android.DanmakuContext;\nimport master.flame.danmaku.danmaku.model.android.Danmakus;\nimport master.flame.danmaku.danmaku.model.android.SpannedCacheStuffer;\nimport master.flame.danmaku.danmaku.model.android.ViewCacheStuffer;\nimport master.flame.danmaku.danmaku.parser.BaseDanmakuParser;\nimport master.flame.danmaku.danmaku.parser.IDataSource;\nimport master.flame.danmaku.danmaku.util.IOUtils;\nimport master.flame.danmaku.danmaku.util.SystemClock;\n\npublic class UglyViewCacheStufferSampleActivity extends Activity implements View.OnClickListener {\n\n    private IDanmakuView mDanmakuView;\n\n    private View mMediaController;\n\n    public PopupWindow mPopupWindow;\n\n    private Button mBtnRotate;\n\n    private Button mBtnHideDanmaku;\n\n    private Button mBtnShowDanmaku;\n\n    private BaseDanmakuParser mParser;\n\n    private Button mBtnPauseDanmaku;\n\n    private Button mBtnResumeDanmaku;\n\n    private Button mBtnSendDanmaku;\n\n    private Button mBtnSendDanmakuTextAndImage;\n\n    private Button mBtnSendDanmakus;\n    private DanmakuContext mContext;\n    private BaseCacheStuffer.Proxy mCacheStufferAdapter = new BaseCacheStuffer.Proxy() {\n\n        private Drawable mDrawable;\n\n        @Override\n        public void prepareDrawing(final BaseDanmaku danmaku, boolean fromWorkerThread) {\n            if (danmaku.text instanceof Spanned) { // 根据你的条件检查是否需要需要更新弹幕\n                // FIXME 这里只是简单启个线程来加载远程url图片，请使用你自己的异步线程池，最好加上你的缓存池\n                new Thread() {\n\n                    @Override\n                    public void run() {\n                        String url = \"http://www.bilibili.com/favicon.ico\";\n                        InputStream inputStream = null;\n                        Drawable drawable = mDrawable;\n                        if (drawable == null) {\n                            try {\n                                URLConnection urlConnection = new URL(url).openConnection();\n                                inputStream = urlConnection.getInputStream();\n                                drawable = BitmapDrawable.createFromStream(inputStream, \"bitmap\");\n                                mDrawable = drawable;\n                            } catch (MalformedURLException e) {\n                                e.printStackTrace();\n                            } catch (IOException e) {\n                                e.printStackTrace();\n                            } finally {\n                                IOUtils.closeQuietly(inputStream);\n                            }\n                        }\n                        if (drawable != null) {\n                            drawable.setBounds(0, 0, 100, 100);\n                            SpannableStringBuilder spannable = createSpannable(drawable);\n                            danmaku.text = spannable;\n                            if (mDanmakuView != null) {\n                                mDanmakuView.invalidateDanmaku(danmaku, false);\n                            }\n                            return;\n                        }\n                    }\n                }.start();\n            }\n        }\n\n        @Override\n        public void releaseResource(BaseDanmaku danmaku) {\n            // TODO 重要:清理含有ImageSpan的text中的一些占用内存的资源 例如drawable\n        }\n    };\n    private int mIconWidth;\n\n    /**\n     * 绘制背景(自定义弹幕样式)\n     */\n    private static class BackgroundCacheStuffer extends SpannedCacheStuffer {\n        // 通过扩展SimpleTextCacheStuffer或SpannedCacheStuffer个性化你的弹幕样式\n        final Paint paint = new Paint();\n\n        @Override\n        public void measure(BaseDanmaku danmaku, TextPaint paint, boolean fromWorkerThread) {\n            danmaku.padding = 10;  // 在背景绘制模式下增加padding\n            super.measure(danmaku, paint, fromWorkerThread);\n        }\n\n        @Override\n        public void drawBackground(BaseDanmaku danmaku, Canvas canvas, float left, float top) {\n            paint.setColor(0x8125309b);\n            canvas.drawRect(left + 2, top + 2, left + danmaku.paintWidth - 2, top + danmaku.paintHeight - 2, paint);\n        }\n\n        @Override\n        public void drawStroke(BaseDanmaku danmaku, String lineText, Canvas canvas, float left, float top, Paint paint) {\n            // 禁用描边绘制\n        }\n    }\n\n    public class MyViewHolder extends ViewCacheStuffer.ViewHolder {\n\n        private final ImageView mIcon;\n        private final TextView mText;\n\n        public MyViewHolder(View itemView) {\n            super(itemView);\n            mIcon = (ImageView) itemView.findViewById(R.id.icon);\n            mText = (TextView) itemView.findViewById(R.id.text);\n        }\n\n    }\n\n    public static class MyImageWare extends NonViewAware {\n\n        private long start;\n        private int id;\n        private WeakReference<IDanmakuView> danmakuViewRef;\n        private BaseDanmaku danmaku;\n        private Bitmap bitmap;\n\n        public MyImageWare(String imageUri, BaseDanmaku danmaku, int width, int height, IDanmakuView danmakuView) {\n            this(imageUri, new ImageSize(width, height), ViewScaleType.FIT_INSIDE);\n            if (danmaku == null) {\n                throw new IllegalArgumentException(\"danmaku may not be null\");\n            }\n            this.danmaku = danmaku;\n            this.id = danmaku.hashCode();\n            this.danmakuViewRef = new WeakReference<>(danmakuView);\n            this.start = SystemClock.uptimeMillis();\n        }\n\n        @Override\n        public int getId() {\n            return this.id;\n        }\n\n        public String getImageUri() {\n            return this.imageUri;\n        }\n\n        private MyImageWare(ImageSize imageSize, ViewScaleType scaleType) {\n            super(imageSize, scaleType);\n        }\n\n        private MyImageWare(String imageUri, ImageSize imageSize, ViewScaleType scaleType) {\n            super(imageUri, imageSize, scaleType);\n        }\n\n        @Override\n        public boolean setImageDrawable(Drawable drawable) {\n            return super.setImageDrawable(drawable);\n        }\n\n        @Override\n        public boolean setImageBitmap(Bitmap bitmap) {\n//            if (this.danmaku.isTimeOut() || this.danmaku.isFiltered()) {\n//                return true;\n//            }\n            if (this.danmaku.text.toString().contains(\"textview\")) {\n                Log.e(\"DFM\", (SystemClock.uptimeMillis() - this.start) + \"ms=====> inside\" + danmaku.tag + \":\" + danmaku.getActualTime() + \",url: bitmap\" + (bitmap == null));\n            }\n            this.bitmap = bitmap;\n            IDanmakuView danmakuView = danmakuViewRef.get();\n            if (danmakuView != null) {\n                danmakuView.invalidateDanmaku(danmaku, true);\n            }\n            return true;\n        }\n    }\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        // Create global configuration and initialize ImageLoader with this config\n        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this).memoryCache(new LruMemoryCache(2 * 1024 * 1024))\n                .memoryCacheSize(2 * 1024 * 1024)\n                .memoryCacheSizePercentage(13).build(); // default\n        ImageLoader.getInstance().init(config);\n        setContentView(R.layout.activity_main);\n        findViews();\n    }\n\n    private BaseDanmakuParser createParser(InputStream stream) {\n\n        if (stream == null) {\n            return new BaseDanmakuParser() {\n\n                @Override\n                protected Danmakus parse() {\n                    return new Danmakus();\n                }\n            };\n        }\n\n        ILoader loader = DanmakuLoaderFactory.create(DanmakuLoaderFactory.TAG_BILI);\n\n        try {\n            loader.load(stream);\n        } catch (IllegalDataException e) {\n            e.printStackTrace();\n        }\n        BaseDanmakuParser parser = new BiliDanmukuParser();\n        IDataSource<?> dataSource = loader.getDataSource();\n        parser.load(dataSource);\n        return parser;\n\n    }\n\n    private void findViews() {\n\n        mMediaController = findViewById(R.id.media_controller);\n        mBtnRotate = (Button) findViewById(R.id.rotate);\n        mBtnHideDanmaku = (Button) findViewById(R.id.btn_hide);\n        mBtnShowDanmaku = (Button) findViewById(R.id.btn_show);\n        mBtnPauseDanmaku = (Button) findViewById(R.id.btn_pause);\n        mBtnResumeDanmaku = (Button) findViewById(R.id.btn_resume);\n        mBtnSendDanmaku = (Button) findViewById(R.id.btn_send);\n        mBtnSendDanmakuTextAndImage = (Button) findViewById(R.id.btn_send_image_text);\n        mBtnSendDanmakus = (Button) findViewById(R.id.btn_send_danmakus);\n        mBtnRotate.setOnClickListener(this);\n        mBtnHideDanmaku.setOnClickListener(this);\n        mMediaController.setOnClickListener(this);\n        mBtnShowDanmaku.setOnClickListener(this);\n        mBtnPauseDanmaku.setOnClickListener(this);\n        mBtnResumeDanmaku.setOnClickListener(this);\n        mBtnSendDanmaku.setOnClickListener(this);\n        mBtnSendDanmakuTextAndImage.setOnClickListener(this);\n        mBtnSendDanmakus.setOnClickListener(this);\n\n        // VideoView\n        VideoView mVideoView = (VideoView) findViewById(R.id.videoview);\n        // DanmakuView\n\n        // 设置最大显示行数\n        HashMap<Integer, Integer> maxLinesPair = new HashMap<Integer, Integer>();\n        maxLinesPair.put(BaseDanmaku.TYPE_SCROLL_RL, 5); // 滚动弹幕最大显示5行\n        // 设置是否禁止重叠\n        HashMap<Integer, Boolean> overlappingEnablePair = new HashMap<Integer, Boolean>();\n        overlappingEnablePair.put(BaseDanmaku.TYPE_SCROLL_RL, true);\n        overlappingEnablePair.put(BaseDanmaku.TYPE_FIX_TOP, true);\n\n        mDanmakuView = (IDanmakuView) findViewById(R.id.sv_danmaku);\n        mContext = DanmakuContext.create();\n\n        mIconWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30f, getResources().getDisplayMetrics());\n        mContext.setDanmakuBold(true);\n        mContext.setDanmakuStyle(IDisplayer.DANMAKU_STYLE_STROKEN, 3).setDuplicateMergingEnabled(false).setScrollSpeedFactor(1.2f).setScaleTextSize(1.2f)\n                .setCacheStuffer(new ViewCacheStuffer<MyViewHolder>() {\n\n                    @Override\n                    public MyViewHolder onCreateViewHolder(int viewType) {\n                        Log.e(\"DFM\", \"onCreateViewHolder:\" + viewType);\n                        return new MyViewHolder(View.inflate(getApplicationContext(), R.layout.layout_view_cache, null));\n                    }\n\n                    @Override\n                    public void onBindViewHolder(int viewType, MyViewHolder viewHolder, BaseDanmaku danmaku, AndroidDisplayer.DisplayerConfig displayerConfig, TextPaint paint) {\n                        if (paint != null)\n                            viewHolder.mText.getPaint().set(paint);\n                        viewHolder.mText.setText(danmaku.text);\n                        viewHolder.mText.setTextColor(danmaku.textColor);\n                        viewHolder.mText.setTextSize(TypedValue.COMPLEX_UNIT_PX, danmaku.textSize);\n                        Bitmap bitmap = null;\n                        MyImageWare imageWare = (MyImageWare) danmaku.tag;\n                        if (imageWare != null) {\n                            bitmap = imageWare.bitmap;\n                            if (danmaku.text.toString().contains(\"textview\")) {\n                                Log.e(\"DFM\", \"onBindViewHolder======> bitmap:\" + (bitmap == null) + \"  \" + danmaku.tag + \"url:\" + imageWare.getImageUri());\n                            }\n                        }\n                        if (bitmap != null) {\n                            viewHolder.mIcon.setImageBitmap(bitmap);\n                            if (danmaku.text.toString().contains(\"textview\")) {\n                                Log.e(\"DFM\", \"onBindViewHolder======>\" + danmaku.tag + \"url:\" + imageWare.getImageUri());\n                            }\n                        } else {\n                            viewHolder.mIcon.setImageResource(R.drawable.ic_launcher);\n                        }\n                    }\n\n                    @Override\n                    public void releaseResource(BaseDanmaku danmaku) {\n                        MyImageWare imageWare = (MyImageWare) danmaku.tag;\n                        if (imageWare != null) {\n                            ImageLoader.getInstance().cancelDisplayTask(imageWare);\n                        }\n                        danmaku.setTag(null);\n                        Log.e(\"DFM\", \"releaseResource url:\" + danmaku.text);\n                    }\n\n\n                    String[] avatars = { \"http://i0.hdslb.com/bfs/face/e13fcb94342c325debb2d3a1d9e503ac4f083514.jpg@45w_45h.webp\",\n                            \"http://i0.hdslb.com/bfs/bangumi/2558e1341d2e934a7e06bb7d92551fef5c82c172.jpg@72w_72h.webp\", \"http://i0.hdslb.com/bfs/face/128edefeef7ce9cfc443a2489d8a1c7d44d88b80.jpg@72w_72h.webp\"};\n                    @Override\n                    public void prepare(BaseDanmaku danmaku, boolean fromWorkerThread) {\n                        if (danmaku.isTimeOut()) {\n                            return;\n                        }\n                        MyImageWare imageWare = (MyImageWare) danmaku.tag;\n                        if (imageWare == null) {\n                            String avatar = avatars[danmaku.index % avatars.length];\n                            imageWare = new MyImageWare(avatar, danmaku, mIconWidth, mIconWidth, mDanmakuView);\n                            danmaku.setTag(imageWare);\n                        }\n                        if (danmaku.text.toString().contains(\"textview\")) {\n                            Log.e(\"DFM\", \"onAsyncLoadResource======>\" + danmaku.tag + \"url:\" + imageWare.getImageUri());\n                        }\n                        ImageLoader.getInstance().displayImage(imageWare.getImageUri(), imageWare);\n                    }\n\n                }, null) // 图文混排使用SpannedCacheStuffer\n//        .setCacheStuffer(new BackgroundCacheStuffer())  // 绘制背景使用BackgroundCacheStuffer\n                .setMaximumLines(maxLinesPair)\n                .preventOverlapping(overlappingEnablePair);\n        if (mDanmakuView != null) {\n            mParser = createParser(this.getResources().openRawResource(R.raw.comments));\n            mDanmakuView.setCallback(new master.flame.danmaku.controller.DrawHandler.Callback() {\n                @Override\n                public void updateTimer(DanmakuTimer timer) {\n                }\n\n                @Override\n                public void drawingFinished() {\n\n                }\n\n                @Override\n                public void danmakuShown(BaseDanmaku danmaku) {\n//                    Log.d(\"DFM\", \"danmakuShown(): text=\" + danmaku.text);\n                }\n\n                @Override\n                public void prepared() {\n                    mDanmakuView.start();\n                }\n            });\n            mDanmakuView.setOnDanmakuClickListener(new IDanmakuView.OnDanmakuClickListener() {\n\n                @Override\n                public boolean onDanmakuClick(IDanmakus danmakus) {\n                    Log.d(\"DFM\", \"onDanmakuClick: danmakus size:\" + danmakus.size());\n                    BaseDanmaku latest = danmakus.last();\n                    if (null != latest) {\n                        Log.d(\"DFM\", \"onDanmakuClick: text of latest danmaku:\" + latest.text);\n                        return true;\n                    }\n                    return false;\n                }\n\n                @Override\n                public boolean onDanmakuLongClick(IDanmakus danmakus) {\n                    return false;\n                }\n\n                @Override\n                public boolean onViewClick(IDanmakuView view) {\n                    mMediaController.setVisibility(View.VISIBLE);\n                    return false;\n                }\n            });\n            mDanmakuView.prepare(mParser, mContext);\n            mDanmakuView.showFPS(true);\n            mDanmakuView.enableDanmakuDrawingCache(true);\n        }\n\n        if (mVideoView != null) {\n            mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {\n                @Override\n                public void onPrepared(MediaPlayer mediaPlayer) {\n                    mediaPlayer.start();\n                }\n            });\n            mVideoView.setVideoPath(Environment.getExternalStorageDirectory() + \"/1.flv\");\n        }\n\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        if (mDanmakuView != null && mDanmakuView.isPrepared()) {\n            mDanmakuView.pause();\n        }\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        if (mDanmakuView != null && mDanmakuView.isPrepared() && mDanmakuView.isPaused()) {\n            mDanmakuView.resume();\n        }\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        if (mDanmakuView != null) {\n            // dont forget release!\n            mDanmakuView.release();\n            mDanmakuView = null;\n        }\n        ImageLoader.getInstance().destroy();\n    }\n\n    @Override\n    public void onBackPressed() {\n        super.onBackPressed();\n        if (mDanmakuView != null) {\n            // dont forget release!\n            mDanmakuView.release();\n            mDanmakuView = null;\n        }\n    }\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        getMenuInflater().inflate(R.menu.main, menu);\n        return true;\n    }\n\n    @Override\n    public void onClick(View v) {\n        if (v == mMediaController) {\n            mMediaController.setVisibility(View.GONE);\n        }\n        if (mDanmakuView == null || !mDanmakuView.isPrepared())\n            return;\n        if (v == mBtnRotate) {\n            setRequestedOrientation(getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);\n        } else if (v == mBtnHideDanmaku) {\n            mDanmakuView.hide();\n            // mPausedPosition = mDanmakuView.hideAndPauseDrawTask();\n        } else if (v == mBtnShowDanmaku) {\n            mDanmakuView.show();\n            // mDanmakuView.showAndResumeDrawTask(mPausedPosition); // sync to the video time in your practice\n        } else if (v == mBtnPauseDanmaku) {\n            mDanmakuView.pause();\n        } else if (v == mBtnResumeDanmaku) {\n            mDanmakuView.resume();\n        } else if (v == mBtnSendDanmaku) {\n            addDanmaku(false);\n        } else if (v == mBtnSendDanmakuTextAndImage) {\n            addDanmaKuShowTextAndImage(false);\n        } else if (v == mBtnSendDanmakus) {\n            Boolean b = (Boolean) mBtnSendDanmakus.getTag();\n            timer.cancel();\n            if (b == null || !b) {\n                mBtnSendDanmakus.setText(R.string.cancel_sending_danmakus);\n                timer = new Timer();\n                timer.schedule(new AsyncAddTask(), 0, 1000);\n                mBtnSendDanmakus.setTag(true);\n            } else {\n                mBtnSendDanmakus.setText(R.string.send_danmakus);\n                mBtnSendDanmakus.setTag(false);\n            }\n        }\n    }\n\n    Timer timer = new Timer();\n\n    class AsyncAddTask extends TimerTask {\n\n        @Override\n        public void run() {\n            for (int i = 0; i < 20; i++) {\n                addDanmaku(true);\n                SystemClock.sleep(20);\n            }\n        }\n    }\n\n    ;\n\n    private void addDanmaku(boolean islive) {\n        BaseDanmaku danmaku = mContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);\n        if (danmaku == null || mDanmakuView == null) {\n            return;\n        }\n        // for(int i=0;i<100;i++){\n        // }\n        danmaku.text = \"这是一条弹幕\" + System.nanoTime();\n        danmaku.padding = 5;\n        danmaku.priority = 0;  // 可能会被各种过滤器过滤并隐藏显示\n        danmaku.isLive = islive;\n        danmaku.setTime(mDanmakuView.getCurrentTime() + 1200);\n        danmaku.textSize = 25f * (mParser.getDisplayer().getDensity() - 0.6f);\n        danmaku.textColor = Color.RED;\n        danmaku.textShadowColor = Color.WHITE;\n        // danmaku.underlineColor = Color.GREEN;\n        danmaku.borderColor = Color.GREEN;\n        mDanmakuView.addDanmaku(danmaku);\n\n    }\n\n    private void addDanmaKuShowTextAndImage(boolean islive) {\n        BaseDanmaku danmaku = mContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);\n        Drawable drawable = getResources().getDrawable(R.drawable.ic_launcher);\n        drawable.setBounds(0, 0, 100, 100);\n        //SpannableStringBuilder spannable = createSpannable(drawable);\n        danmaku.text = \"这是文本，图片在textview左侧\";\n        danmaku.setTag(new MyImageWare(\"http://i0.hdslb.com/bfs/face/084bd13eb5dc51a64674085bb28e958ecd5addd0.jpg@180w_180h.webp\", danmaku, mIconWidth, mIconWidth, mDanmakuView));\n        danmaku.padding = 5;\n        danmaku.priority = 1;  // 一定会显示, 一般用于本机发送的弹幕\n        danmaku.isLive = islive;\n        danmaku.setTime(mDanmakuView.getCurrentTime() + 1200);\n        danmaku.textSize = 25f * (mParser.getDisplayer().getDensity() - 0.6f);\n        danmaku.textColor = Color.RED;\n        danmaku.textShadowColor = 0; // 重要：如果有图文混排，最好不要设置描边(设textShadowColor=0)，否则会进行两次复杂的绘制导致运行效率降低\n        danmaku.underlineColor = Color.GREEN;\n        mDanmakuView.addDanmaku(danmaku);\n    }\n\n    private SpannableStringBuilder createSpannable(Drawable drawable) {\n        String text = \"bitmap\";\n        SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(text);\n        ImageSpan span = new ImageSpan(drawable);//ImageSpan.ALIGN_BOTTOM);\n        spannableStringBuilder.setSpan(span, 0, text.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);\n        spannableStringBuilder.append(\"图文混排\");\n        spannableStringBuilder.setSpan(new BackgroundColorSpan(Color.parseColor(\"#8A2233B1\")), 0, spannableStringBuilder.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);\n        return spannableStringBuilder;\n    }\n\n}\n"
  },
  {
    "path": "Sample/src/main/project.properties",
    "content": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# This file must be checked in Version Control Systems.\n#\n# To customize properties used by the Ant build system edit\n# \"ant.properties\", and override values to adapt the script to your\n# project structure.\n#\n# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):\n#proguard.config=${sdk.dir}\\tools\\proguard\\proguard-android.txt:proguard-project.txt\n\n# Project target.\ntarget=android-17\nandroid.library.reference.1=../../../DanmakuFlameMaster/src/main\n"
  },
  {
    "path": "Sample/src/main/res/layout/activity_main.xml",
    "content": "<!--\r\n  ~ Copyright (C) 2013 Chen Hui <calmer91@gmail.com>\r\n  ~\r\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\r\n  ~ you may not use this file except in compliance with the License.\r\n  ~ You may obtain a copy of the License at\r\n  ~\r\n  ~      http://www.apache.org/licenses/LICENSE-2.0\r\n  ~\r\n  ~ Unless required by applicable law or agreed to in writing, software\r\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\r\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n  ~ See the License for the specific language governing permissions and\r\n  ~ limitations under the License.\r\n-->\r\n\r\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    xmlns:tools=\"http://schemas.android.com/tools\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\"\r\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\r\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\r\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\r\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\r\n    tools:context=\".MainActivity\" >\r\n    \r\n\r\n\r\n    <VideoView\r\n        android:id=\"@+id/videoview\"\r\n        android:layout_width=\"fill_parent\"\r\n        android:layout_height=\"fill_parent\" />\r\n    \r\n<ImageView android:src=\"@drawable/ic_launcher\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\"/>\r\n\r\n    <master.flame.danmaku.ui.widget.DanmakuView\r\n        android:id=\"@+id/sv_danmaku\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"match_parent\" />\r\n\r\n    <include android:id=\"@+id/media_controller\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"fill_parent\"\r\n        layout=\"@layout/media_controller\" />\r\n\r\n</FrameLayout>"
  },
  {
    "path": "Sample/src/main/res/layout/layout_view_cache.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center\">\n\n    <ImageView\n        android:id=\"@+id/icon\"\n        android:layout_width=\"30dp\"\n        android:layout_height=\"30dp\"\n        android:layout_gravity=\"center_vertical\"\n        android:src=\"@drawable/ic_launcher\"\n        android:scaleType=\"fitXY\"\n        />\n\n    <TextView\n        android:id=\"@+id/text\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:text=\"@string/action_settings\" />\n\n</LinearLayout>"
  },
  {
    "path": "Sample/src/main/res/layout/media_controller.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\" >\r\n\r\n    <LinearLayout\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"60dip\"\r\n        android:layout_gravity=\"top\"\r\n        android:background=\"#8a006161\" >\r\n    </LinearLayout>\r\n\r\n    <LinearLayout\r\n        android:gravity=\"center_vertical\"\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:layout_gravity=\"bottom\"\r\n        android:background=\"#8acc0000\" >\r\n\r\n        <Button\r\n            android:layout_weight=\"1\"\r\n            android:id=\"@+id/rotate\"\r\n            android:layout_width=\"0dp\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:text=\"@string/rotate\" />\r\n\r\n        <Button\r\n            android:layout_width=\"0dp\"\r\n            android:layout_weight=\"1\"\r\n            android:id=\"@+id/btn_hide\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:text=\"@string/hide_danmaku\" />\r\n\r\n        <Button\r\n            android:id=\"@+id/btn_show\"\r\n            android:layout_width=\"0dp\"\r\n            android:layout_weight=\"1\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:text=\"@string/show_danmaku\" />\r\n\r\n        <Button\r\n            android:id=\"@+id/btn_pause\"\r\n            android:layout_width=\"0dp\"\r\n            android:layout_weight=\"1\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:text=\"@string/pause_danmaku\" />\r\n\r\n        <Button\r\n            android:id=\"@+id/btn_resume\"\r\n            android:layout_width=\"0dp\"\r\n            android:layout_weight=\"1\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:text=\"@string/resume_danmaku\" />\r\n\r\n        <Button\r\n            android:id=\"@+id/btn_send\"\r\n            android:layout_width=\"0dp\"\r\n            android:layout_weight=\"1\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:text=\"@string/send_danmaku\" />\r\n\r\n        <Button\r\n            android:id=\"@+id/btn_send_image_text\"\r\n            android:layout_width=\"0dp\"\r\n            android:layout_weight=\"1\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:text=\"@string/send_danmaku_image_text\" />\r\n        \r\n        <Button\r\n            android:id=\"@+id/btn_send_danmakus\"\r\n            android:layout_width=\"0dp\"\r\n            android:layout_weight=\"1\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:text=\"@string/send_danmakus\" />\r\n    </LinearLayout>\r\n\r\n</FrameLayout>"
  },
  {
    "path": "Sample/src/main/res/menu/main.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:id=\"@+id/action_settings\"\n        android:title=\"@string/action_settings\"\n        android:orderInCategory=\"100\"\n        android:showAsAction=\"never\" />\n</menu>\n"
  },
  {
    "path": "Sample/src/main/res/raw/comments.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><i><chatserver>chat.bilibili.com</chatserver><chatid>8729348</chatid><mission>0</mission><maxlimit>1000</maxlimit><source>k-v</source><d p=\"205.2259979248,4,25,6830715,1484219280,0,47f24a19,2839373425\">厉害了我的弹幕君！</d>\r\n    <d p=\"219.36999511719,4,25,6830715,1484219329,0,47f24a19,2839376125\">hhhhhhhhh</d>\r\n    <d p=\"14.810000419617,1,25,16777215,1484225491,0,fd3b921a,2839759733\">合影</d>\r\n    <d p=\"87.306999206543,1,25,0,1484234306,0,78673da7,2840354383\">_(•̀ω•́ 」∠)_见一次进一次</d>\r\n    <d p=\"5.8600001335144,5,25,15138834,1484234937,0,fbcfc551,2840397503\">高级弹幕合影</d>\r\n    <d p=\"128.45399475098,5,25,15138834,1484235125,0,fbcfc551,2840406535\">高级弹幕合影</d>\r\n    <d p=\"145.2559967041,1,25,16777215,1484235141,0,e38328cb,2840407505\">合影！！</d>\r\n    <d p=\"97.525001525879,5,25,15138834,1484235121,0,fbcfc551,2840408957\">高级弹幕合影</d>\r\n    <d p=\"114.58499908447,5,25,9599289,1484235191,0,fbcfc551,2840410523\">合影</d>\r\n    <d p=\"177.42500305176,5,25,9599289,1484235278,0,fbcfc551,2840415859\">????????????</d>\r\n    <d p=\"188.71299743652,1,25,16707842,1484235376,0,0bdc3c05,2840421761\">卡的我手机都不动了，额????</d>\r\n    <d p=\"188.71299743652,1,25,16707842,1484235399,0,0bdc3c05,2840423111\">卡的我手机都不动了(;｀O´)o额????额????</d>\r\n    <d p=\"197.87399291992,1,25,16711680,1484272610,0,a0f488b7,2841134205\">合影</d>\r\n    <d p=\"64.790000915527,1,25,8700107,1484273058,0,fa4eecac,2841146663\">表白</d>\r\n    <d p=\"76.55899810791,1,25,8700107,1484273082,0,fa4eecac,2841147299\">????????</d>\r\n    <d p=\"50.284000396729,1,25,16777215,1484275161,0,2f6ec8cb,2841227937\">厉害了</d>\r\n    <d p=\"144.18099975586,1,25,16777215,1484275251,0,2f6ec8cb,2841230907\">飞机………</d>\r\n    <d p=\"169.48599243164,1,25,0,1484369394,0,f0f9102d,2845190451\">一个耳朵BOX另一个van是耳机不行了吗</d>\r\n    <d p=\"68.504997253418,1,25,0,1484369490,0,c68f3935,2845196309\">一起抖呗</d>\r\n    <d p=\"37.665000915527,1,25,16777215,1484370959,0,0e478a00,2845290049\">额  这特效</d>\r\n    <d p=\"59.638999938965,1,25,16777215,1484370981,0,0e478a00,2845291389\">出不去了</d>\r\n    <d p=\"61.506000518799,1,25,6830715,1484372656,0,f04febdb,2845405817\">尾部有洛殿和音</d>\r\n    <d p=\"45.754001617432,7,15,16711680,1484376780,0,b8054584,2845658409\">[1,500,\"0.98-0.1\",4.5,\"好想跟你表白❤❤❤❤❤\",1,100,600,200,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"105.43399810791,7,50,16711680,1484376936,0,b8054584,2845667909\">[1,500,\"0.98-0.1\",4.5,\"好想跟你表白❤❤❤❤❤\",360,20,31,281,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"105.43399810791,7,50,16711680,1484376949,0,b8054584,2845668711\">[1,500,\"0.98-0.1\",4.5,\"好想跟你表白❤❤❤❤❤\",360,20,27,356,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"105.43399810791,7,50,16711680,1484376969,0,b8054584,2845669829\">[1,500,\"0.98-0.1\",4.5,\"好想跟你表白❤❤❤❤❤\",360,20,32,322,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"105.43399810791,7,50,16711680,1484377002,0,b8054584,2845670949\">[1,500,\"0.98-0.1\",4.5,\"好想跟你表白❤❤❤❤❤\",360,20,40,268,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"105.43399810791,7,50,16711680,1484377007,0,b8054584,2845672121\">[1,500,\"0.98-0.1\",4.5,\"好想跟你表白❤❤❤❤❤\",360,20,21,138,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"162.26899719238,7,50,16711680,1484377172,0,b8054584,2845681127\">[37,514,\"0.98-0.1\",4.5,\"好想跟你表白❤❤❤❤❤\",360,360,30,38,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"162.26899719238,7,50,16711680,1484377170,0,b8054584,2845681881\">[37,514,\"0.98-0.1\",4.5,\"好想跟你表白❤❤❤❤❤\",360,360,27,103,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"162.26899719238,7,50,16711680,1484377187,0,b8054584,2845682853\">[37,514,\"0.98-0.1\",4.5,\"好想跟你表白❤❤❤❤❤\",360,360,29,248,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"162.26899719238,7,50,16711680,1484377215,0,b8054584,2845684559\">[37,514,\"0.98-0.1\",4.5,\"好想跟你表白❤❤❤❤❤\",360,360,44,276,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"162.26899719238,7,50,16711680,1484377242,0,b8054584,2845685307\">[37,514,\"0.98-0.1\",4.5,\"好想跟你表白❤❤❤❤❤\",360,360,54,548,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"166.9700012207,1,25,0,1484393142,0,4f4c3f6f,2846960211\">①只老兲尻尾</d>\r\n    <d p=\"209.70399475098,1,25,0,1484393188,0,4f4c3f6f,2846963411\">添衣是大家的</d>\r\n    <d p=\"196.41000366211,1,25,16777215,1484393273,0,4f4c3f6f,2846969665\">合影啊啊啊啊啊啊啊啊啊快点</d>\r\n    <d p=\"99.744003295898,5,25,16777215,1484427740,0,19d1cb02,2848849169\">高级弹幕合影</d>\r\n    <d p=\"106.21700286865,5,25,13369344,1484427768,0,19d1cb02,2848849383\">神弹幕合影留念</d>\r\n    <d p=\"166.95399475098,5,25,6737151,1484427899,0,19d1cb02,2848850543\">神弹幕再次合影留念（天依蓝）</d>\r\n    <d p=\"69.380996704102,1,25,16777215,1484452768,0,fbcfc551,2849498293\">高级弹幕合影</d>\r\n    <d p=\"102.14199829102,1,25,16777215,1484452972,0,fbcfc551,2849509997\">高级弹幕合影</d>\r\n    <d p=\"25.101499557495,1,25,16777215,1484454710,0,27dc66ea,2849612005\">asd</d>\r\n    <d p=\"42.417499542236,1,25,3368652,1484454728,0,27dc66ea,2849613059\">asd</d>\r\n    <d p=\"153.8990020752,1,25,16777215,1484455675,0,729028be,2849671817\">瞬间爆炸</d>\r\n    <d p=\"170.73899841309,1,25,14811775,1484455719,0,729028be,2849674659\">有毒</d>\r\n    <d p=\"185.17900085449,1,25,15138834,1484455744,0,729028be,2849676271\">高级弹幕</d>\r\n    <d p=\"76.221000671387,1,25,16777215,1484457958,0,5ebd46c1,2849828241\">❤❤❤❤❤❤❤</d>\r\n    <d p=\"91.807998657227,1,25,16777215,1484463411,0,dd4f0f1b,2850193181\">老哥稳</d>\r\n    <d p=\"23.593399047852,1,25,16777215,1484473625,0,27dc66ea,2850878245\">阿萨德</d>\r\n    <d p=\"41.342498779297,1,18,16777215,1484473642,0,27dc66ea,2850879529\">阿诗丹顿</d>\r\n    <d p=\"67.41300201416,1,25,16777215,1484487981,0,413d65ff,2851951247\">不就是投币么，都给你</d>\r\n    <d p=\"77.444999694824,1,25,16777215,1484488396,0,690d7147,2851985347\">发现镇站之宝</d>\r\n    <d p=\"150.08500671387,1,25,16777215,1484488472,0,690d7147,2851991773\">2333333</d>\r\n    <d p=\"99.041000366211,1,25,16777215,1484489882,0,ba926592,2852107927\">一直老王八</d>\r\n    <d p=\"135.12699890137,4,25,16777215,1484502892,0,83929014,2852879293\">上面即将打飞机↑↑↑</d>\r\n    <d p=\"175.64700317383,4,25,16777215,1484503028,0,83929014,2852883321\">前方弹幕核能预警</d>\r\n    <d p=\"175.64700317383,4,25,16777215,1484503045,0,83929014,2852883863\">前方弹幕核能预警</d>\r\n    <d p=\"122.8629989624,1,25,16711935,1484529231,0,e9c5ccf2,2853219251\">抖腿抖得都要痉挛了~~~</d>\r\n    <d p=\"172.89100646973,1,25,16711935,1484529281,0,e9c5ccf2,2853220589\">蜜汁点头</d>\r\n    <d p=\"54.333000183105,1,25,16777215,1484529324,0,4fffc8ea,2853221761\">八</d>\r\n    <d p=\"216.45799255371,1,25,16711935,1484529325,0,e9c5ccf2,2853221787\">动次动次动次次</d>\r\n    <d p=\"94.414001464844,1,25,16777215,1484529386,0,4fffc8ea,2853223511\">m9( ｀д´ )!!!!</d>\r\n    <d p=\"19.52499961853,1,25,16777215,1484529635,0,6c95f1ed,2853230105\">我败了。。。</d>\r\n    <d p=\"144.93600463867,1,25,16777215,1484529767,0,6c95f1ed,2853234013\">这。。。</d>\r\n    <d p=\"206.89300537109,1,25,15772458,1484530137,0,074b9024,2853244861\">t</d>\r\n    <d p=\"214.85299682617,1,18,15772458,1484530164,0,074b9024,2853245469\">233</d>\r\n    <d p=\"39.49100112915,1,25,0,1484530888,0,030b1e7a,2853267937\">ㄟ( ▔, ▔ )ㄏ给您拜年了</d>\r\n    <d p=\"59.289001464844,1,25,0,1484530908,0,030b1e7a,2853268435\">(●´ϖ`●)卡的我忍不住发了条弹幕</d>\r\n    <d p=\"63.50899887085,1,25,9487136,1484534282,0,024a3424,2853394129\">(○｀ε´○)</d>\r\n    <d p=\"143.66299438477,1,25,9487136,1484534389,0,024a3424,2853398619\">(#／。＼#)</d>\r\n    <d p=\"6.9649200439453,1,25,16777215,1484535343,0,27dc66ea,2853439989\">sdf</d>\r\n    <d p=\"6.7520399093628,1,25,16777215,1484535385,0,27dc66ea,2853441855\">test</d>\r\n    <d p=\"71.169998168945,4,25,9599289,1484539268,0,4a6945d9,2853623275\">♥♞</d>\r\n    <d p=\"214.72999572754,4,25,16777062,1484544030,0,4a6945d9,2853897945\">瞎晃了</d>\r\n    <d p=\"64.988998413086,1,25,16777215,1484553359,0,fe1069e5,2854454171\">ee</d>\r\n    <d p=\"78.723999023438,1,25,16777215,1484553373,0,fe1069e5,2854455061\">eeee</d>\r\n    <d p=\"119.66100311279,1,25,16777215,1484553414,0,fe1069e5,2854457509\">eeeeeeeeeeeeeeeeeee</d>\r\n    <d p=\"93.865997314453,1,25,16777215,1484554954,0,c2b004e3,2854549135\">ha</d>\r\n    <d p=\"163.70199584961,1,25,16777215,1484557311,0,27dc66ea,2854695793\">????????????????????????????????????☕️☕️????????????????????????????????</d>\r\n    <d p=\"166.52499389648,1,25,16777215,1484557336,0,27dc66ea,2854697459\">????</d>\r\n    <d p=\"159.06100463867,1,25,16777215,1484572074,0,413d65ff,2856091751\">????</d>\r\n    <d p=\"84.339996337891,1,25,16777215,1484576280,0,4f5948ae,2856472555\">????????????????????????????❤????????</d>\r\n    <d p=\"129.7799987793,1,25,6830715,1484576327,0,4f5948ae,2856477273\">！！</d>\r\n    <d p=\"20.018999099731,1,25,16777215,1484587166,0,d511dd52,2857232053\">百万助攻</d>\r\n    <d p=\"81.41300201416,4,25,16777215,1484631113,0,2ccb5b23,2858430473\">vvg桐谷歌</d>\r\n    <d p=\"145.30099487305,1,25,41194,1484653008,0,391c5835,2859871251\">高级弹幕666</d>\r\n    <d p=\"44.562999725342,1,25,16777215,1484654241,0,41b58337,2859973761\">动次打次动词打次</d>\r\n    <d p=\"198.59199523926,5,25,16777215,1484654408,0,41b58337,2859987435\">什么鬼</d>\r\n    <d p=\"16.212999343872,1,25,16777215,1484661419,0,f50c90de,2860598703\">合影</d>\r\n    <d p=\"27.167999267578,1,25,16777215,1484661742,0,485410e5,2860627483\">前来考古</d>\r\n    <d p=\"163.93499755859,1,25,16777215,1484664500,0,19722a1b,2860868151\">高级弹幕合影</d>\r\n    <d p=\"71.541000366211,5,25,6750003,1484692358,0,3b1e5dfb,2861761657\">搞基弹幕合影</d>\r\n    <d p=\"141.36099243164,5,25,6750003,1484692429,0,3b1e5dfb,2861762149\">BOOM</d>\r\n    <d p=\"160.20199584961,1,25,16777215,1484702618,0,cd692501,2861932709\">好多高级弹幕</d>\r\n    <d p=\"189.08599853516,1,25,16777215,1484729346,0,29e41c19,2863591783\">二营长，我他娘的意大利炮呢</d>\r\n    <d p=\"142.71200561523,1,25,16777215,1484752513,0,ac61bac5,2865609399\">～(￣▽￣～)~</d>\r\n    <d p=\"200.78700256348,1,25,16777215,1484752524,0,ac61bac5,2865614271\">┻━┻︵╰(‵□′)╯︵┻━┻</d>\r\n    <d p=\"90.843002319336,1,25,0,1484752671,0,169af40a,2865622799\">在下输了  硬币给你</d>\r\n    <d p=\"20.305999755859,5,25,16777011,1484752861,0,17386d26,2865638989\">秘籍——反复日马↘</d>\r\n    <d p=\"71.231002807617,5,25,16777011,1484752922,0,17386d26,2865644239\">秘籍——反复日马↓</d>\r\n    <d p=\"134.49899291992,1,25,16777215,1484756141,0,24bcea4f,2865907313\">????????????????????</d>\r\n    <d p=\"66.592002868652,1,25,16777215,1484756239,0,ce69ff49,2865913837\">????</d>\r\n    <d p=\"70.621002197266,1,25,16777215,1484798362,0,b1b4f7e8,2867055528\">百万助攻</d>\r\n    <d p=\"153.04699707031,1,25,16777215,1484798444,0,b1b4f7e8,2867060602\">表白我家天依老婆的和声</d>\r\n    <d p=\"61.785999298096,5,25,15138834,1484800830,0,8a71efd2,2867213702\">。。</d>\r\n    <d p=\"64.785003662109,1,25,16777215,1484810546,0,03835de9,2867926802\">我不是看见了天依吗？天依哪去了</d>\r\n    <d p=\"185.38299560547,1,25,16777215,1484810582,0,03835de9,2867929286\">锦依卫救驾来迟</d>\r\n    <d p=\"79.532997131348,1,25,16777215,1484821449,0,23f046a5,2868722654\">^  _  ^</d>\r\n    <d p=\"127.59999847412,1,25,16707842,1484821609,0,23f046a5,2868733764\">&gt; _ &lt;  ••••••••</d>\r\n    <d p=\"173.46699523926,1,25,16707842,1484821652,0,23f046a5,2868740592\">..哇</d>\r\n    <d p=\"3.8239998817444,1,25,16777215,1484826102,0,b1c39f77,2869088036\">卧槽</d>\r\n    <d p=\"6.6119999885559,1,25,16777215,1484845218,0,d48c5c0b,2870808196\">厂</d>\r\n    <d p=\"1.9179999828339,1,25,16777215,1484870721,0,c65457ff,2871296428\">百万助攻</d>\r\n    <d p=\"129.51100158691,5,25,15138834,1484870928,0,c65457ff,2871300260\">二营长，你他娘的意大利炮呢！！？</d>\r\n    <d p=\"154.42799377441,1,25,16777215,1484872903,0,5b390ccb,2871346376\">❤❤❤❤❤❤❤❤</d>\r\n    <d p=\"163.33200073242,1,25,16777215,1484873968,0,b961a5f7,2871379398\">快</d>\r\n    <d p=\"166.39599609375,1,25,16777215,1484873999,0,b961a5f7,2871380468\">看到后面莫名死机</d>\r\n    <d p=\"94.130996704102,1,25,16777215,1484874463,0,1e3a889b,2871396450\">-＝ ∧ ∧ 我来啦-＝と( •∀•)　-＝/ と_ノ-＝_/／⌒ノ</d>\r\n    <d p=\"44.196998596191,1,25,16777215,1484874615,0,d4f27876,2871402122\">我有一句妈卖批一定要讲</d>\r\n    <d p=\"176.76800537109,1,25,16777215,1484874757,0,d4f27876,2871407336\">迷之抖腿</d>\r\n    <d p=\"112.87999725342,5,18,16777215,1484880152,0,226fc277,2871680558\">❤❤❤❤❤❤❤</d>\r\n    <d p=\"106.04199981689,5,25,38979,1484882662,0,fd3b3b9e,2871849062\">漫长的3分钟</d>\r\n    <d p=\"15.508999824524,5,25,41194,1484883009,0,87783bc5,2871869490\">hhhhhhhh</d>\r\n    <d p=\"23.940999984741,1,25,15138834,1484891544,0,cd9f93ec,2872547252\">……他吃了几次了…( ´_ゝ｀)这人还活着简直奇迹</d>\r\n    <d p=\"80.672996520996,1,25,16777215,1484891668,0,3d3e48a9,2872557244\">我仿佛看到了彩虹</d>\r\n    <d p=\"119.52899932861,5,25,16777215,1484891707,0,3d3e48a9,2872560350\">厉害了高级弹幕</d>\r\n    <d p=\"137.23699951172,5,25,16777215,1484891725,0,3d3e48a9,2872561812\">高级弹幕合影</d>\r\n    <d p=\"164.65699768066,1,25,16777215,1484891752,0,3d3e48a9,2872563968\">哇</d>\r\n    <d p=\"202.27900695801,5,25,16777215,1484891790,0,3d3e48a9,2872567110\">合影</d>\r\n    <d p=\"184.82499694824,1,25,16777215,1484893493,0,3052e34d,2872699420\">高级弹幕合影</d>\r\n    <d p=\"96.262001037598,1,25,16777215,1484893567,0,9c062bca,2872705298\">兲</d>\r\n    <d p=\"146.75100708008,1,25,16777215,1484893859,0,4b916a6f,2872727014\">合影</d>\r\n    <d p=\"159.34899902344,1,25,16777215,1484899079,0,378c4060,2873131818\">合影</d>\r\n    <d p=\"136.76100158691,1,25,6750003,1484900464,0,5dcbd485,2873266710\">我怎么绿了</d>\r\n    <d p=\"20.853000640869,1,25,16777215,1484901533,0,d4e1274f,2873351634\">厉害</d>\r\n    <d p=\"1.0599999427795,1,25,16777215,1484903791,0,42644b71,2873532614\">看到天依滚了进来</d>\r\n    <d p=\"60.259998321533,1,25,16777215,1484903868,0,42644b71,2873538800\">高级弹幕</d>\r\n    <d p=\"88.680000305176,4,25,14811775,1484905020,0,ba981988,2873670654\">ghh</d>\r\n    <d p=\"4.0999999046326,1,25,16777215,1484905053,0,7af05ae4,2873674084\">我从未见过有如此厚颜无耻之人</d>\r\n    <d p=\"19.719999313354,5,25,16777215,1484905076,0,7af05ae4,2873675612\">搞基</d>\r\n    <d p=\"38.439998626709,5,25,16777215,1484905099,0,7af05ae4,2873677438\">真不要脸</d>\r\n    <d p=\"51.959999084473,5,25,16777215,1484905119,0,7af05ae4,2873678968\">脸真不要</d>\r\n    <d p=\"63.939998626709,5,25,16777215,1484905156,0,7af05ae4,2873682108\">脸要不</d>\r\n    <d p=\"89.879997253418,5,25,16777215,1484905203,0,7af05ae4,2873685438\">八王之一</d>\r\n    <d p=\"112.08000183105,5,25,16777215,1484905227,0,7af05ae4,2873687370\">卧槽</d>\r\n    <d p=\"126.25900268555,5,25,16777215,1484905252,0,7af05ae4,2873689436\">——哇</d>\r\n    <d p=\"145.73899841309,5,25,16777215,1484905278,0,7af05ae4,2873691778\">moob</d>\r\n    <d p=\"163.57899475098,5,25,16777215,1484905335,0,7af05ae4,2873696120\">白表你跟想好</d>\r\n    <d p=\"204.04200744629,5,25,16777215,1484906416,0,7286266e,2873779380\">合影！</d>\r\n    <d p=\"130.86599731445,1,25,16777215,1484906478,0,cd9f93ec,2873783966\">看，飞机✈</d>\r\n    <d p=\"185.75799560547,5,25,41194,1484906586,0,cd9f93ec,2873792024\">啦啦啦啦</d>\r\n    <d p=\"12.870400428772,1,25,16777215,1484910583,0,bf55746e,2874099904\">1.25倍</d>\r\n    <d p=\"4.7800002098083,5,25,41194,1484911894,0,dedc12cc,2874211920\">见依滚</d>\r\n    <d p=\"67.238998413086,1,25,16777215,1484912796,0,8f983b93,2874292032\">变色</d>\r\n    <d p=\"3.8199999332428,1,25,0,1484913085,0,f02076e2,2874321758\">合影还来得及不</d>\r\n    <d p=\"112,1,25,16777215,1484913252,0,52332fc2,2874332852\">双音道</d>\r\n    <d p=\"98.099998474121,5,25,16777215,1484914454,0,7e4dcbf9,2874442406\">前方高能</d>\r\n    <d p=\"98.099998474121,5,25,16707842,1484914471,0,7e4dcbf9,2874443890\">前方高能</d>\r\n    <d p=\"98.099998474121,5,25,41194,1484914483,0,7e4dcbf9,2874444914\">前方高能</d>\r\n    <d p=\"98.099998474121,5,25,9487136,1484914495,0,7e4dcbf9,2874446138\">前方高能</d>\r\n    <d p=\"98.099998474121,5,25,15772458,1484914508,0,7e4dcbf9,2874447324\">前方高能</d>\r\n    <d p=\"16.496999740601,1,25,16777215,1484919492,0,5b4f1516,2874921544\">腻害了</d>\r\n    <d p=\"46.319999694824,1,25,16777215,1484919522,0,5b4f1516,2874924346\">合影</d>\r\n    <d p=\"90.162002563477,1,25,0,1484921705,0,ac2ee630,2875142906\">吓得我脸都绿了</d>\r\n    <d p=\"170.74699401855,1,25,16777215,1484925249,0,81b790c2,2875499900\">第一个硬币给你了。</d>\r\n    <d p=\"65.279998779297,1,25,16777215,1484927065,0,a229dd88,2875670386\">音乐区，拔剑吧！</d>\r\n    <d p=\"127.05999755859,5,25,65280,1484927321,0,e31fdb7e,2875692968\">诸君快看  还有一个灰机</d>\r\n    <d p=\"70.609001159668,1,25,16777215,1484931220,0,26a37ad1,2875971192\">厉害了</d>\r\n    <d p=\"21.823999404907,1,25,16777215,1484935822,0,1dc4c8dd,2876174130\">(*・_・)ノ&lt;(＃＃)&gt;彡来个烤红薯冷静一下</d>\r\n    <d p=\"170.27900695801,1,25,41194,1484936143,0,1dc4c8dd,2876184330\">                     天依蓝             </d>\r\n    <d p=\"58.179000854492,1,25,16777215,1484936371,0,79ae6a59,2876191054\">✧(≖ ◡ ≖✿)</d>\r\n    <d p=\"64.628997802734,1,25,16777215,1484947946,0,444ae6f5,2876367392\">????????????????????????????????????????????????</d>\r\n    <d p=\"42.632999420166,1,25,16777215,1484948695,0,207384ee,2876372932\">全程神弹幕合影</d>\r\n    <d p=\"128.32899475098,1,25,16777215,1484961345,0,52af9773,2876556950\">特效字幕？？？？？？？？？？</d>\r\n    <d p=\"181.06300354004,1,25,0,1484962519,0,504b5785,2876598710\">我老婆快出来了</d>\r\n    <d p=\"87.73999786377,1,25,16777215,1484963531,0,f6fc133c,2876658202\">萧亚轩已经报警</d>\r\n    <d p=\"148.0659942627,1,25,16777215,1484964746,0,f262c81a,2876712468\">合影</d>\r\n    <d p=\"46.057998657227,1,25,16777215,1484964865,0,f262c81a,2876718060\">搞基弹幕合影</d>\r\n    <d p=\"3.1719999313354,5,25,16737945,1484966101,0,6a69a961,2876780072\">高级弹幕合影</d>\r\n    <d p=\"158.57699584961,5,25,16737945,1484966300,0,6a69a961,2876790780\">飞机</d>\r\n    <d p=\"123.77899932861,1,25,16777215,1484967001,0,4bcc0189,2876828870\">合影留念</d>\r\n    <d p=\"29.721000671387,1,25,16777215,1484970140,0,ad16c50f,2877014958\">蜜汁抖腿</d>\r\n    <d p=\"74.181098937988,1,25,16777215,1484978375,0,c52dfb93,2877606426\">耳机福利带感</d>\r\n    <d p=\"133.00100708008,1,25,16777215,1484978433,0,c52dfb93,2877610760\">一边抖腿一边摇头</d>\r\n    <d p=\"187.18099975586,1,25,16777215,1484978488,0,c52dfb93,2877614722\">表白坐下和右下</d>\r\n    <d p=\"214.82099914551,1,25,16777215,1484978516,0,c52dfb93,2877616738\">侍女团参见天依</d>\r\n    <d p=\"65.483001708984,1,25,10053375,1484981106,0,b0e7777b,2877800812\">配合头像食用更佳</d>\r\n    <d p=\"126.60299682617,1,25,16777215,1484981151,0,3ef0999a,2877804146\">高级弹幕之前怎么都被清了</d>\r\n    <d p=\"124.72899627686,1,25,16777215,1484981228,0,f3e7b16a,2877809386\">我的腿 停不下来</d>\r\n    <d p=\"170.69500732422,1,25,16777215,1484981274,0,f3e7b16a,2877812516\">UP治好了我多年的关节炎</d>\r\n    <d p=\"123.8990020752,1,25,16777215,1484983012,0,3fd36817,2877933786\">显卡过关</d>\r\n    <d p=\"9.3620004653931,1,25,16777215,1484984155,0,357f0f65,2878016400\">醉了</d>\r\n    <d p=\"167.6130065918,1,25,16777215,1484986348,0,622f9793,2878169104\">诸葛笑了????</d>\r\n    <d p=\"197.79499816895,1,25,16777215,1484986393,0,622f9793,2878172356\">参见吃货殿下</d>\r\n    <d p=\"176.89199829102,1,25,16777215,1484990056,0,18d17e46,2878442854\">节奏跟的好 66666</d>\r\n    <d p=\"191.33000183105,1,25,16777215,1484991242,0,2d40f120,2878530974\">牛逼</d>\r\n    <d p=\"166.54800415039,1,25,6750207,1484995071,0,3686df5d,2878828458\">..</d>\r\n    <d p=\"188.06199645996,1,18,6750207,1484995274,0,66de6426,2878843928\">哇</d>\r\n    <d p=\"14.074999809265,1,25,16777215,1484998151,0,5c9fb780,2879074190\">1.5倍速，发现新大陆</d>\r\n    <d p=\"55.513999938965,1,25,16777215,1484998801,0,cd2a11f1,2879130810\">∠( °ω°)／</d>\r\n    <d p=\"16.625,1,25,16777215,1485002995,0,5083d406,2879505494\">终于 可以发 弹幕了~ 哈哈哈哈</d>\r\n    <d p=\"55.939998626709,4,25,16777215,1485004098,0,1313d712,2879610158\">全是高级弹幕 合影</d>\r\n    <d p=\"68.213996887207,4,25,65280,1485004111,0,1313d712,2879611362\">合影高级弹幕</d>\r\n    <d p=\"213.41999816895,4,25,65280,1485004257,0,1313d712,2879625118\">厉害了高级弹幕</d>\r\n    <d p=\"188.96200561523,1,25,16777215,1485004651,0,f5173ec3,2879662520\">怎么回事?</d>\r\n    <d p=\"68.620002746582,1,25,16707842,1485008380,0,c77d1c40,2880029430\">faf'fa</d>\r\n    <d p=\"176.25300598145,4,25,16777215,1485009698,0,fc8f31d5,2880158140\">怎么说</d>\r\n    <d p=\"173.30999755859,1,25,16777215,1485009898,0,0ee84538,2880177298\">——</d>\r\n    <d p=\"133.05299377441,1,25,16777215,1485011995,0,bf800665,2880372074\">硬币还是膝盖，选一个就是，送你了</d>\r\n    <d p=\"82.481002807617,1,25,16777215,1485016452,0,4e01fe60,2880717374\">毫无违和感~~ 大赞！</d>\r\n    <d p=\"167.98599243164,1,25,16777215,1485016538,0,4e01fe60,2880722840\">鬼畜有文化，哲学都害怕！ 厉害了，我的哥！</d>\r\n    <d p=\"36.648998260498,1,25,16777215,1485025226,0,d7308b43,2881073848\">阿！会心一击</d>\r\n    <d p=\"62.639999389648,1,25,0,1485025363,0,2b978b17,2881075804\">ρθπρρρπθθνπθριππρρθπθρν</d>\r\n    <d p=\"172.14100646973,5,25,6830715,1485025568,0,2b978b17,2881080098\">.▄█▀█●◖|´⌣`*|◗·˳♪⁎˚♫◖|´⌣`*|◗·˳♪⁎˚♫</d>\r\n    <d p=\"173.84700012207,1,25,0,1485025814,0,2b978b17,2881084988\">高级弹幕6的不行</d>\r\n    <d p=\"104.18599700928,1,25,0,1485026816,0,2b978b17,2881104710\">☞好想和你表白☜</d>\r\n    <d p=\"103.21199798584,4,25,15138834,1485029301,0,2b978b17,2881144474\">                     好想和你表白</d>\r\n    <d p=\"59.419998168945,1,25,16777215,1485039410,0,cd35461f,2881235232\">????</d>\r\n    <d p=\"29.096000671387,1,25,16777215,1485043254,0,deda7066,2881283140\">2333333333</d>\r\n    <d p=\"47.101001739502,1,25,16777215,1485045374,0,8085fcba,2881326186\">合影</d>\r\n    <d p=\"117.81999969482,1,25,16777215,1485045447,0,8085fcba,2881327954\">合影</d>\r\n    <d p=\"50.699001312256,1,25,16777215,1485047432,0,7f91e1b6,2881384834\">合影</d>\r\n    <d p=\"107.25900268555,1,25,16777215,1485047492,0,7f91e1b6,2881386772\">合影</d>\r\n    <d p=\"114.49900054932,1,25,16777215,1485047504,0,7f91e1b6,2881387218\">草</d>\r\n    <d p=\"130.53900146484,1,25,16777215,1485047534,0,7f91e1b6,2881388276\">快看有飞机</d>\r\n    <d p=\"139.45899963379,1,25,16777215,1485047545,0,7f91e1b6,2881388740\">打飞机了</d>\r\n    <d p=\"183.41900634766,1,25,16777215,1485047599,0,7f91e1b6,2881390702\">参见公主殿下</d>\r\n    <d p=\"198.49899291992,1,25,16777215,1485047796,0,7f91e1b6,2881397272\">我老婆</d>\r\n    <d p=\"151.68499755859,1,25,16777215,1485049516,0,6cf00a3c,2881464262\">高级弹幕无敌了</d>\r\n    <d p=\"210.74600219727,1,25,16777215,1485049577,0,6cf00a3c,2881466896\">参见天依殿下</d>\r\n    <d p=\"105.39800262451,1,25,16777215,1485056211,0,5fb6c96f,2881819850\">好多高级弹幕</d>\r\n    <d p=\"164.26800537109,1,25,16777011,1485057107,0,f52e2929,2881876328\">高级弹幕 合影</d>\r\n    <d p=\"56.817001342773,1,25,16777215,1485059059,0,1fcad20e,2882016640\">高级弹幕合影</d>\r\n    <d p=\"6.8210000991821,1,25,16777215,1485059721,0,5a4adb73,2882059448\">这个好好听</d>\r\n    <d p=\"121.23899841309,1,25,16777215,1485059896,0,8d11081d,2882070794\">弹幕满分????</d>\r\n    <d p=\"105.55500030518,1,25,16777215,1485059965,0,0ac5ea12,2882075356\">卡在这</d>\r\n    <d p=\"35.382999420166,1,25,16777215,1485064254,0,0e76be50,2882381030\">233333333333333</d>\r\n    <d p=\"160.58799743652,1,25,16777215,1485064455,0,0e76be50,2882395572\">23333</d>\r\n    <d p=\"205.43699645996,1,25,16777215,1485064514,0,0e76be50,2882399490\">????????</d>\r\n    <d p=\"39.987998962402,1,25,16777215,1485064547,0,0e76be50,2882401838\">2333</d>\r\n    <d p=\"136.44700622559,1,25,16777215,1485064618,0,0e76be50,2882406876\">???</d>\r\n    <d p=\"168.71099853516,1,25,16777215,1485064651,0,0e76be50,2882409128\">???????????????????</d>\r\n    <d p=\"40.606998443604,1,25,16777215,1485066098,0,bcca326f,2882533498\">八嘎！西克秀</d>\r\n    <d p=\"185.28900146484,1,25,16777215,1485066243,0,bcca326f,2882543848\">合影高级弹幕</d>\r\n    <d p=\"32.847999572754,1,25,16777215,1485066309,0,bcca326f,2882548472\">扣6啊</d>\r\n    <d p=\"120.94599914551,1,25,16777215,1485066618,0,bcca326f,2882570414\">合影</d>\r\n    <d p=\"145.02200317383,1,25,16711935,1485066642,0,bcca326f,2882572040\">哇</d>\r\n    <d p=\"57.665000915527,1,25,16777215,1485067084,0,2ee3573c,2882603322\">..</d>\r\n    <d p=\"63.919998168945,1,25,16777215,1485068196,0,50c99856,2882681562\">(๑•̀㉨•́ฅ✧ 早</d>\r\n    <d p=\"176.75999450684,1,25,16777215,1485071455,0,510649c8,2882904964\">23333</d>\r\n    <d p=\"56.826000213623,1,25,16777215,1485072305,0,bd9da7e9,2882966146\">听说这个视频全程高级弹幕</d>\r\n    <d p=\"127.99299621582,1,25,16777215,1485072397,0,bd9da7e9,2882972824\">前方打飞机预警</d>\r\n    <d p=\"147.07899475098,1,25,16777215,1485072419,0,bd9da7e9,2882974226\">打飞机厉害</d>\r\n    <d p=\"169.49499511719,1,25,16777215,1485072440,0,bd9da7e9,2882975796\">合影</d>\r\n    <d p=\"197.91000366211,1,25,16777215,1485073437,0,75ac5c64,2883053170\">静静的看着</d>\r\n    <d p=\"60.967998504639,4,25,10040115,1485073813,0,40cc5ba2,2883077582\">高级弹幕？</d>\r\n    <d p=\"85.28800201416,4,25,65280,1485073836,0,40cc5ba2,2883079368\">6的飞起，真是爆炸了</d>\r\n    <d p=\"97.622001647949,4,25,65280,1485073848,0,40cc5ba2,2883080362\">控♂制</d>\r\n    <d p=\"164.63800048828,4,25,65280,1485073889,0,40cc5ba2,2883083268\">跪了、、、、、</d>\r\n    <d p=\"155.97200012207,1,25,65280,1485073913,0,40cc5ba2,2883085100\">绿了。。。换个蛋2222333</d>\r\n    <d p=\"206.56900024414,1,25,65280,1485073963,0,40cc5ba2,2883088742\">卧槽。。。。。。这不是米雪大大么？</d>\r\n    <d p=\"75.374000549316,1,25,16777215,1485075102,0,343a1c8b,2883173386\">带感</d>\r\n    <d p=\"22.950000762939,5,25,41194,1485075119,0,d1223d5e,2883174940\">BGM蓝色天空?</d>\r\n    <d p=\"1.4609999656677,1,25,16777215,1485075919,0,2697183c,2883233922\">见封滚</d>\r\n    <d p=\"118.7259979248,1,18,52479,1485076188,0,1c418ff4,2883253660\">高级弹幕66666</d>\r\n    <d p=\"1.7059999704361,1,25,16777215,1485076382,0,ff09c340,2883267986\">1.5倍速食用更佳</d>\r\n    <d p=\"1.7059999704361,1,25,38979,1485076396,0,ff09c340,2883269010\">1.5倍速食用更佳</d>\r\n    <d p=\"1.7059999704361,1,25,9487136,1485076408,0,ff09c340,2883269870\">1.5倍速食用更佳</d>\r\n    <d p=\"1.7059999704361,1,25,8700107,1485076371,0,ff09c340,2883270854\">1.5倍速食用更佳</d>\r\n    <d p=\"27.343000411987,1,25,16777215,1485082331,0,f850ae60,2883711968\">第十遍开始</d>\r\n    <d p=\"70.727996826172,1,25,3355443,1485082374,0,f850ae60,2883715208\">贼带感</d>\r\n    <d p=\"219.75300598145,1,25,0,1485088045,0,008164f3,2884181602\">神级弹幕！！</d>\r\n    <d p=\"47.341999053955,1,25,0,1485089881,0,e99e0b31,2884350030\">▄█▀█●给跪了</d>\r\n    <d p=\"201.50500488281,1,25,0,1485091528,0,af4ee3c3,2884512040\">表白天依老婆</d>\r\n    <d p=\"64.481002807617,1,25,16751052,1485091942,0,30c6d314,2884551668\">233333333333333333333333</d>\r\n    <d p=\"73.849998474121,1,25,16777215,1485093536,0,1f9c8c36,2884702990\">搞基弹幕合影</d>\r\n    <d p=\"40.402000427246,1,25,16777215,1485093751,0,bc6abef4,2884723778\">☼</d>\r\n    <d p=\"77.561996459961,1,25,16777215,1485093943,0,dcdc55a8,2884742660\">好像给你表白</d>\r\n    <d p=\"191.14599609375,1,25,16777215,1485094495,0,188a9893,2884798338\">高级弹幕在哪？我为什么看不到</d>\r\n    <d p=\"128.77699279785,5,25,16777215,1485094539,0,188a9893,2884802350\">高级弹幕在哪呢？我怎么看不到</d>\r\n    <d p=\"156.1840057373,1,25,16777215,1485095974,0,4a42fedb,2884936058\">搞基弹幕好厉害</d>\r\n    <d p=\"135.2380065918,1,25,16777215,1485096127,0,8a87930d,2884950886\">写没感觉到吗</d>\r\n    <d p=\"91.068000793457,1,25,16777215,1485096788,0,38a31971,2885019642\">1.5倍加速，爆了</d>\r\n    <d p=\"183.41499328613,1,25,16777215,1485098513,0,6eaafea1,2885188976\">motion:{ x:{fromValue:7, toValue:640, lifeTime:6}}</d>\r\n    <d p=\"99.039001464844,1,25,16777215,1485100150,0,53eae372,2885330422\">哇~</d>\r\n    <d p=\"180,1,25,16777215,1485105808,0,78a3f763,2885680750\">来来来，我们一起摇摆～</d>\r\n    <d p=\"204.8990020752,1,25,16777215,1485105861,0,78a3f763,2885683122\">高级弹幕合影～</d>\r\n    <d p=\"205.10000610352,1,25,16777215,1485106134,0,b3723c13,2885695070\">手机下载食用更佳</d>\r\n    <d p=\"70.392997741699,1,25,0,1485108592,0,30a064fc,2885785830\">你懂个锤子</d>\r\n    <d p=\"29.214000701904,1,25,16777215,1485115647,0,032b14b9,2885931224\">1.5真爽</d>\r\n    <d p=\"35.877998352051,1,25,16777215,1485135272,0,39055287,2886225664\">全程右下角</d>\r\n    <d p=\"132.45899963379,1,25,16777215,1485135370,0,39055287,2886229518\">为什么有Bbox的声音</d>\r\n    <d p=\"170.5590057373,1,25,16777215,1485135407,0,39055287,2886231054\">Bbox又来了</d>\r\n    <d p=\"104.95500183105,1,25,16777215,1485136564,0,86e67c3e,2886282246\">哇！</d>\r\n    <d p=\"147.34599304199,1,18,16707842,1485136815,0,2b94d485,2886294032\">这么好的</d>\r\n    <d p=\"74.536003112793,1,25,16777215,1485139368,0,56aa01e8,2886426882\">高级弹幕合影留念</d>\r\n    <d p=\"142.92500305176,1,25,16777215,1485139716,0,1cfb9747,2886447450\">厉害了我的弹幕</d>\r\n    <d p=\"138.57400512695,1,25,16777215,1485140045,0,c97329b9,2886466988\">叼</d>\r\n    <d p=\"13.01340007782,1,25,16777215,1485141608,0,afe4fa53,2886562458\">0.5倍新世界</d>\r\n    <d p=\"13.395999908447,1,25,16777215,1485142252,0,0ed14d0f,2886603424\">合影！！！！</d>\r\n    <d p=\"210.88800048828,1,25,16777215,1485142538,0,0ed14d0f,2886625660\">大家封面见！</d>\r\n    <d p=\"136.36199951172,1,25,16777215,1485143918,0,a3e2082b,2886717332\">我tm全程找高级弹幕没找到2333</d>\r\n    <d p=\"57.934700012207,1,25,16776960,1485144472,0,045e8647,2886753550\">合影，快，给我相机</d>\r\n    <d p=\"67.181999206543,1,25,16777215,1485145165,0,5b61445b,2886799630\">1.5倍带感</d>\r\n    <d p=\"31.877000808716,1,25,16777215,1485148459,0,e5a2802b,2887042390\">给▄█▀█●</d>\r\n    <d p=\"64.966003417969,1,25,16777215,1485148497,0,e5a2802b,2887045346\">▄█▀█●○|￣|_</d>\r\n    <d p=\"55.574001312256,1,25,16777215,1485148560,0,51d6f26e,2887050316\">闺女了</d>\r\n    <d p=\"73.087997436523,1,25,16777215,1485148577,0,51d6f26e,2887051476\">彻底跪了</d>\r\n    <d p=\"166.78500366211,1,25,16777215,1485148604,0,e5a2802b,2887053546\">哇哇哇</d>\r\n    <d p=\"105.16799926758,1,25,16777215,1485148610,0,51d6f26e,2887054004\">我是萧亚轩我已经报警了</d>\r\n    <d p=\"124.68299865723,1,25,16777215,1485148628,0,51d6f26e,2887055554\">合影加我个</d>\r\n    <d p=\"156.14799499512,1,25,3407616,1485148660,0,51d6f26e,2887057840\">吓得我绿了</d>\r\n    <d p=\"4.9219999313354,5,25,15138834,1485148904,0,90ebfc41,2887076342\">高级弹幕合影</d>\r\n    <d p=\"217.93200683594,5,25,15138834,1485149178,0,90ebfc41,2887097424\">1.5超爽</d>\r\n    <d p=\"125.30599975586,1,25,16777215,1485149844,0,25d7b1ea,2887148516\">哇</d>\r\n    <d p=\"27.596000671387,1,25,16777215,1485150016,0,8b0555b2,2887161552\">2333333333</d>\r\n    <d p=\"53.563999176025,5,25,16777215,1485152102,0,44a52f46,2887327210\">????????</d>\r\n    <d p=\"61.370998382568,1,25,16777215,1485152736,0,758369f8,2887374656\">23333</d>\r\n    <d p=\"81.250999450684,1,25,16777215,1485155953,0,7a44cda2,2887618152\">突</d>\r\n    <d p=\"104.00800323486,1,25,16776960,1485156032,0,7a44cda2,2887624132\">靠</d>\r\n    <d p=\"215.67100524902,1,25,16777215,1485157599,0,ce784e20,2887743882\">..</d>\r\n    <d p=\"193.89700317383,1,25,16777215,1485157665,0,6db9e780,2887749060\">锦依卫参见公主殿下</d>\r\n    <d p=\"111.59899902344,1,25,16777215,1485158928,0,74763e68,2887844480\">火钳</d>\r\n    <d p=\"104.74099731445,1,25,38979,1485159650,0,b978802b,2887903880\">☜(@⇔@)☞</d>\r\n    <d p=\"65.305000305176,1,25,16711680,1485161479,0,88b6306a,2888039748\">蜜汁感动</d>\r\n    <d p=\"182.95399475098,1,25,41194,1485161858,0,34ad16a4,2888068508\">在哪儿</d>\r\n    <d p=\"196.86399841309,1,25,41194,1485161838,0,34ad16a4,2888070998\">在哪饿</d>\r\n    <d p=\"150.76499938965,1,25,16777215,1485164927,0,2f4b365d,2888305972\">233333333333333333</d>\r\n    <d p=\"169.41499328613,1,25,16777215,1485168462,0,77c3323c,2888573976\">合影</d>\r\n    <d p=\"182.05000305176,1,25,16777215,1485168475,0,77c3323c,2888574996\">合影合影</d>\r\n    <d p=\"48.451999664307,1,25,16777215,1485168997,0,970a5af4,2888616650\">又翻上来了系列</d>\r\n    <d p=\"76.405403137207,1,25,65382,1485171068,0,924fc2e0,2888790868\">吓得我都绿了</d>\r\n    <d p=\"55.36600112915,1,25,16777215,1485171195,0,054b7035,2888801624\">合影</d>\r\n    <d p=\"116.85900115967,1,25,16777215,1485171481,0,e5fe7eea,2888826628\">合影</d>\r\n    <d p=\"79.660003662109,1,25,16777215,1485173199,0,4b46b977,2888977984\">????????????????</d>\r\n    <d p=\"130,1,25,14811775,1485173274,0,4b46b977,2888985068\">搞基弹幕</d>\r\n    <d p=\"93.61799621582,1,25,16777215,1485174227,0,f4f495cc,2889070178\">٩(●´৺`●)૭٩(●´৺`●)و</d>\r\n    <d p=\"160.06900024414,1,25,16777215,1485174678,0,2561e20d,2889115816\">♥♥♥</d>\r\n    <d p=\"208.43800354004,1,25,16777215,1485176670,0,48d21aad,2889296244\">么么哒，厉害厉害</d>\r\n    <d p=\"180.10699462891,1,25,16777215,1485178150,0,ad7b6d7a,2889440680\">左下角那个噗呲克才厉害</d>\r\n    <d p=\"1.460000038147,5,25,16707842,1485178897,0,3b40bf1d,2889513956\">看到封面天依就进了</d>\r\n    <d p=\"65.976997375488,4,18,16777215,1485179968,0,c465c382,2889619698\">还是你们会玩</d>\r\n    <d p=\"11.630999565125,1,25,16777215,1485180852,0,20012aee,2889709776\">::: (\\_(\\  I*: (=’ :’)  :* miss •..(,(”)(”) ¤°.¸¸.•´¯`» you</d>\r\n    <d p=\"20.95299911499,1,25,16777215,1485180958,0,0baea31c,2889720710\">天天循环</d>\r\n    <d p=\"62.792999267578,1,25,16776960,1485181000,0,0baea31c,2889724986\">OTZ、</d>\r\n    <d p=\"103.95300292969,1,25,16776960,1485181055,0,0baea31c,2889730584\">( ⊙o⊙ )哇</d>\r\n    <d p=\"179.88200378418,1,25,15138834,1485182224,0,27bc00e6,2889853196\">本来是全程点头 后来跟着孔明和马摇了起来</d>\r\n    <d p=\"127.12300109863,1,25,16777215,1485185664,0,af274d0f,2890193668\">合影</d>\r\n    <d p=\"50.694999694824,1,25,16777215,1485191931,0,b6848510,2890636696\">配合up主头像食用更佳</d>\r\n    <d p=\"107.54000091553,1,25,16777215,1485194263,0,8334aa13,2890755392\">233333</d>\r\n    <d p=\"83.931396484375,5,18,16724889,1485195096,0,7051eaf7,2890784960\">全程跟着摇头。。 - -</d>\r\n    <d p=\"127.68000030518,4,25,15138834,1485217149,0,2dff60a8,2891088122\">合影</d>\r\n    <d p=\"126.67199707031,4,18,15138834,1485217186,0,2dff60a8,2891088808\">高级弹幕</d>\r\n    <d p=\"62.641998291016,1,25,16777215,1485218625,0,ad9af102,2891123642\">蜜汁抖腿</d>\r\n    <d p=\"155.52200317383,1,25,16777215,1485219587,0,9d4ee814,2891154104\">合影合影合影</d>\r\n    <d p=\"108.672996521,1,25,16777215,1485219969,0,84e73c51,2891167072\">怎发的</d>\r\n    <d p=\"13.130999565125,7,100,13408614,1485221210,0,06cea297,2891213354\">[471,238,\"1-1\",4.5,\"我一直很有自信\",4,4,0,0,1300,0,true,\"宋体\",1]</d>\r\n    <d p=\"40.762001037598,1,25,16777215,1485221294,0,fb29074a,2891216704\">懂哲学么少年？( ͡° ͜ʖ ͡°)╯♂</d>\r\n    <d p=\"76.646003723145,7,105,13395456,1485221300,0,06cea297,2891216982\">[471,238,\"1-1\",4.5,\"搞基不怕说出口\",4,4,0,0,1300,0,true,\"宋体\",1]</d>\r\n    <d p=\"106.91899871826,7,80,13395456,1485221386,0,06cea297,2891220514\">[228,100,\"1-1\",4.5,\"好像跟你表白❤❤❤\",4,4,0,0,1500,0,true,\"宋体\",1]</d>\r\n    <d p=\"122.03900146484,7,255,13395456,1485221432,0,06cea297,2891222358\">[228,100,\"1-1\",4.5,\"哇~\",4,4,0,0,1500,0,true,\"宋体\",1]</d>\r\n    <d p=\"130.39500427246,7,255,3381606,1485221533,0,06cea297,2891226310\">[228,86,\"0.81-0.92\",4.5,\"无耻老贼\",7,2,38,5,1700,300,true,\"微软雅黑\",1]</d>\r\n    <d p=\"164.18699645996,7,255,3381606,1485221791,0,06cea297,2891236652\">[862,568,\"0.81-0.92\",1,\"哇\",11,2,111,0,400,300,true,\"微软雅黑\",1]</d>\r\n    <d p=\"172.89399719238,7,255,6684672,1485221834,0,06cea297,2891238468\">[862,568,\"0.81-0.92\",1,\"哇 哇 哇\",11,2,222,0,400,300,true,\"微软雅黑\",1]</d>\r\n    <d p=\"126.03099822998,1,25,16777215,1485226507,0,4c1d9bf8,2891480896\">厉害弹幕</d>\r\n    <d p=\"64.799003601074,1,25,41194,1485230940,0,a2138163,2891769708\">合影</d>\r\n    <d p=\"35.942001342773,1,25,16777215,1485231188,0,610fb19c,2891786322\">高级弹幕合影</d>\r\n    <d p=\"68.061996459961,1,25,16777215,1485231233,0,610fb19c,2891789256\">高级弹幕合影</d>\r\n    <d p=\"95.069000244141,1,25,16777215,1485232069,0,0e357602,2891845960\">高级弹幕，祝你们鸡年大吉吧</d>\r\n    <d p=\"201.38600158691,1,25,16777215,1485232324,0,195113a7,2891864464\">笑的很好。。。</d>\r\n    <d p=\"62,1,25,16777215,1485232468,0,e14e27b4,2891874862\">❤❤❤❤❤❤❤❤❤❤</d>\r\n    <d p=\"140.33599853516,1,25,16777215,1485232562,0,e14e27b4,2891881986\">✈✈✈✈✈✈</d>\r\n    <d p=\"72.796997070312,1,25,0,1485233377,0,e70e0ad0,2891942270\">这个怎么搞？！</d>\r\n    <d p=\"64.138999938965,1,25,16777215,1485233381,0,4a6cf61b,2891942868\">真当萧亚轩不看b站？</d>\r\n    <d p=\"53.998001098633,1,25,16777215,1485233763,0,079737e2,2891971170\">表情好gay</d>\r\n    <d p=\"80.087997436523,5,25,16777215,1485233841,0,9c903863,2891977206\">卧槽，搞基弹幕</d>\r\n    <d p=\"177.48599243164,4,25,16776960,1485234340,0,4955f934,2892015572\">我是纯洁的白色</d>\r\n    <d p=\"163.49800109863,1,25,16777215,1485236700,0,7c6da1e1,2892201558\">.</d>\r\n    <d p=\"69.78099822998,1,25,16777215,1485238977,0,61374758,2892376096\">厉害</d>\r\n    <d p=\"213.46000671387,1,25,14811775,1485242558,0,2e327cc6,2892646086\">确认存活</d>\r\n    <d p=\"124.14199829102,1,25,16777215,1485248640,0,12b21a93,2893107908\">兲</d>\r\n    <d p=\"130.3869934082,1,25,0,1485250589,0,c47629bb,2893260888\">厉害了</d>\r\n    <d p=\"52.259998321533,1,25,16777215,1485251011,0,fa77b83c,2893294900\">FFF团:好团员</d>\r\n    <d p=\"162.71600341797,1,25,16777215,1485252650,0,722bb17a,2893423576\">哇！</d>\r\n    <d p=\"57.988998413086,1,25,0,1485255616,0,bdf79a0f,2893653230\">合影</d>\r\n    <d p=\"124.12899780273,1,25,0,1485255632,0,bdf79a0f,2893658424\">合影</d>\r\n    <d p=\"58.793998718262,5,25,16777215,1485259721,0,d31c4167,2894012206\">哇</d>\r\n    <d p=\"86.643997192383,1,25,16777215,1485261746,0,3b2a8eb0,2894205866\">23333333333333</d>\r\n    <d p=\"167.07000732422,1,25,16777215,1485261827,0,3b2a8eb0,2894213712\">w</d>\r\n    <d p=\"191.61799621582,1,25,16777215,1485261851,0,3b2a8eb0,2894216064\">啦23333</d>\r\n    <d p=\"105.57900238037,1,25,16777215,1485262463,0,916a586d,2894276134\">合影</d>\r\n    <d p=\"18.246999740601,1,25,16711680,1485264138,0,165ad0a2,2894440878\">233333</d>\r\n    <d p=\"34.755001068115,1,25,16711680,1485264156,0,165ad0a2,2894442724\">233333333333</d>\r\n    <d p=\"51.854999542236,1,25,16711680,1485264175,0,165ad0a2,2894444588\">囍</d>\r\n    <d p=\"63.557998657227,1,25,16711680,1485264188,0,165ad0a2,2894445804\">囍 </d>\r\n    <d p=\"112.02600097656,1,25,16711680,1485264237,0,165ad0a2,2894450716\">囍囍囍囍</d>\r\n    <d p=\"149.22300720215,1,25,16711680,1485264274,0,165ad0a2,2894454288\">合影</d>\r\n    <d p=\"197.17900085449,5,25,41194,1485265188,0,c5bd4d5f,2894545882\">参见殿下</d>\r\n    <d p=\"76.358001708984,1,25,16777215,1485265688,0,ccbd82b1,2894595928\">喵~</d>\r\n    <d p=\"63.785999298096,1,25,0,1485265726,0,5d4f7efc,2894599698\">精了</d>\r\n    <d p=\"0,1,25,16777215,1485266954,0,e5bd78f5,2894723780\">http://www.bilibili.com/video/av8123420/</d>\r\n    <d p=\"32.14400100708,1,25,0,1485266964,0,54ccadb7,2894724666\">蜜汁带感</d>\r\n    <d p=\"0,1,25,16777215,1485266974,0,e5bd78f5,2894725782\">✈</d>\r\n    <d p=\"53.291000366211,1,25,0,1485266985,0,54ccadb7,2894726764\">合影</d>\r\n    <d p=\"97.55899810791,1,18,16777215,1485266985,0,b5df5715,2894727240\">顶上去</d>\r\n    <d p=\"26.04400062561,1,25,16777215,1485267576,0,e5bd78f5,2894786252\">有意识</d>\r\n    <d p=\"102.18499755859,1,25,16707842,1485267803,0,597c39ea,2894813866\">黄了</d>\r\n    <d p=\"129.19500732422,1,25,3381708,1485269566,0,84a23579,2894986642\">叼叼叼</d>\r\n    <d p=\"147.66900634766,1,25,3381708,1485269584,0,84a23579,2894988586\">看看飞机</d>\r\n    <d p=\"173.90899658203,1,25,3381708,1485269611,0,84a23579,2894991158\">合影</d>\r\n    <d p=\"12.744000434875,1,25,16777215,1485275815,0,23b12efe,2895499522\">在顶顶</d>\r\n    <d p=\"4.1420001983643,1,25,13369344,1485284941,0,8a29a0b8,2895867348\">阿姆斯特朗螺旋稳</d>\r\n    <d p=\"97.747001647949,1,25,16777215,1485287782,0,aac0e020,2895930204\">表白</d>\r\n    <d p=\"0,1,25,16777215,1485309051,0,1afe8ba9,2896286482\">看到天依就滚进来了</d>\r\n    <d p=\"38.418998718262,1,25,16777215,1485313900,0,0116986d,2896564990\">百万助攻</d>\r\n    <d p=\"80.71900177002,5,25,16707842,1485314423,0,d9fa1419,2896600376\">hhhh</d>\r\n    <d p=\"205.6609954834,1,25,16777215,1485316104,0,78613f07,2896720314\">这怎么弄得</d>\r\n    <d p=\"28.743999481201,1,25,16777215,1485316327,0,d1ef3095,2896732438\">额</d>\r\n    <d p=\"3.4110000133514,1,25,16777215,1485316413,0,0698da14,2896738438\">QAQ画质感人</d>\r\n    <d p=\"155.25999450684,1,25,16777215,1485319625,0,c4c757f5,2896974922\">哇哇哇</d>\r\n    <d p=\"182.39100646973,1,25,16777215,1485319653,0,c4c757f5,2896977288\">抖腿</d>\r\n    <d p=\"97.508003234863,1,25,16777215,1485320276,0,095032aa,2897027890\">这BGM太6</d>\r\n    <d p=\"57.182998657227,1,25,16777215,1485329545,0,8fcad84a,2897767402\">百万助攻</d>\r\n    <d p=\"62.179000854492,1,18,15138834,1485330702,0,6ced6d4e,2897856968\">woc</d>\r\n    <d p=\"101.34600067139,1,25,0,1485331358,0,d250c4af,2897908158\">hhhhh</d>\r\n    <d p=\"19.920000076294,1,25,16777215,1485337465,0,032f7073,2898392662\">233333</d>\r\n    <d p=\"35.013000488281,1,25,41194,1485337541,0,032f7073,2898398464\">唔噗噗噗噗</d>\r\n    <d p=\"58.720001220703,1,25,41194,1485337601,0,032f7073,2898403280\">????</d>\r\n    <d p=\"64.500999450684,1,25,0,1485341990,0,54a0e42b,2898753008\">????????????</d>\r\n    <d p=\"154.3390045166,1,25,16777215,1485344432,0,294c5269,2898957752\">老马有老手</d>\r\n    <d p=\"116.297996521,1,25,65535,1485348107,0,ac4e8b8b,2899284814\">哇哇哇~</d>\r\n    <d p=\"208.78900146484,1,25,16777215,1485404531,0,5592ae84,2901945368\">←↓↗↘↑←↙→↓←↗↓←↖↓</d>\r\n    <d p=\"71.498001098633,1,25,16777215,1485415201,0,822967d8,2902764584\">❀</d>\r\n    <d p=\"138.96000671387,1,25,13311,1485415649,0,822967d8,2902798826\">◆</d>\r\n    <d p=\"215.5659942627,1,25,13311,1485415725,0,822967d8,2902804638\">▃◣</d>\r\n    <d p=\"10.979000091553,1,25,16707842,1485418000,0,10c78d79,2902978194\">娃娃娃娃娃娃</d>\r\n    <d p=\"46.179000854492,1,25,16707842,1485418076,0,10c78d79,2902983836\">☞</d>\r\n    <d p=\"196.11999511719,1,25,16707842,1485418360,0,10c78d79,2903005472\">新年快乐˒˒????₍₍????₎₎????新年快乐˒˒????₍₍????₎₎????</d>\r\n    <d p=\"116.05300140381,1,25,16777215,1485427735,0,2f1befc4,2903734990\">❤❤❤❤❤❤</d>\r\n    <d p=\"112.44200134277,1,25,16777215,1485432737,0,71993e22,2904162386\">兲</d>\r\n    <d p=\"127.51300048828,5,25,15138834,1485443941,0,400b2c8a,2905423130\">R!P!G!</d>\r\n    <d p=\"37.56600189209,1,25,16777215,1485443956,0,8c18234e,2905424088\">蜜汁带感</d>\r\n    <d p=\"119.69300079346,1,25,15138834,1485444078,0,43dbe663,2905440166\">腻害呢</d>\r\n    <d p=\"207.0659942627,1,25,16777215,1485449556,0,c0c757c3,2905852458\">给跪了大哥</d>\r\n    <d p=\"2.6578199863434,1,25,65280,1485468936,0,4e3f828c,2906292392\">-。-</d>\r\n    <d p=\"164.70100402832,5,25,16777215,1485476872,0,f0551f3f,2906389830\">米雪</d>\r\n    <d p=\"120.04499816895,1,25,16777215,1485481675,0,265ebf6d,2906554826\">哇</d>\r\n    <d p=\"73.822998046875,1,25,16777215,1485484027,0,7e0845de,2906669250\">迷之带感</d>\r\n    <d p=\"179.66600036621,1,25,16777215,1485503852,0,a7c05035,2908142504\">列害了我的歌</d>\r\n    <d p=\"36.180999755859,1,25,16777215,1485505590,0,e9404515,2908277822\">几年大吉吧</d>\r\n    <d p=\"92.019996643066,1,25,0,1485510776,0,026f710c,2908667128\">233333</d>\r\n    <d p=\"74.634399414062,1,25,16777215,1485525880,0,c2b004e3,2910941272\">看看俺</d>\r\n    <d p=\"74.458999633789,4,25,16707842,1485539888,0,ddc2283b,2912344996\">????????????????????????</d>\r\n    <d p=\"88.91300201416,5,25,16707842,1485564814,0,568e09d1,2912814694\">全程看左下。</d>\r\n    <d p=\"115.40899658203,5,25,16707842,1485564861,0,568e09d1,2912816050\">合影合影！</d>\r\n    <d p=\"117.84300231934,5,25,16707842,1485564874,0,568e09d1,2912816498\">合影合影！</d>\r\n    <d p=\"220.05400085449,1,25,16711680,1485565305,0,35db1383,2912830890\">吃吃吃</d>\r\n    <d p=\"42.261001586914,1,25,16777215,1485567254,0,cc011e38,2912906728\">▄█▀█●</d>\r\n    <d p=\"86.86799621582,1,25,10040166,1485593227,0,8430e3a9,2914996806\">弹幕被你们玩坏了</d>\r\n    <d p=\"125.31999969482,1,25,16777215,1485596218,0,2c69ec56,2915261588\">好棒呀</d>\r\n    <d p=\"205.48899841309,1,25,16777215,1485612659,0,62c60024,2916845776\">坚持住！</d>\r\n    <d p=\"120.29000091553,1,25,16777215,1485620753,0,5bbf4294,2917654572\">合影</d>\r\n    <d p=\"57.645999908447,4,25,13369344,1485627416,0,9f48116e,2918022636\">↙全程左下</d>\r\n    <d p=\"21.885999679565,1,25,16777215,1485658021,0,077d215b,2918628772\">嘿嘿</d>\r\n    <d p=\"20.065999984741,1,25,16777215,1485658800,0,9db2be6c,2918669198\">heyign </d>\r\n    <d p=\"46.498001098633,1,25,16777215,1485658827,0,9db2be6c,2918670590\">合影先</d>\r\n    <d p=\"7.8689999580383,1,25,16777215,1485664986,0,de77c453,2919035808\">坚决</d>\r\n    <d p=\"124.94000244141,1,25,16777215,1485669054,0,0b917e19,2919318838\">合影</d>\r\n    <d p=\"77.819000244141,1,25,38979,1485673003,0,ae345992,2919595498\">妙啊！</d>\r\n    <d p=\"154.55000305176,1,25,3407616,1485675985,0,4e3f828c,2919812682\">哇!</d>\r\n    <d p=\"177.47399902344,1,25,3407616,1485676007,0,4e3f828c,2919814446\">哇哇哇！</d>\r\n    <d p=\"208.30999755859,1,25,3407616,1485676038,0,4e3f828c,2919816822\">全程德国boy</d>\r\n    <d p=\"98.013999938965,1,25,0,1485679602,0,f9e27e2b,2920085390\">????</d>\r\n    <d p=\"207.0590057373,5,18,41194,1485680986,0,0b7d3098,2920189838\">锦衣卫的出来  骑士团搞事</d>\r\n    <d p=\"12.671999931335,1,25,0,1485685443,0,ff1d1fc4,2920516158\">可啪</d>\r\n    <d p=\"75.55899810791,1,25,0,1485685518,0,6f2fdebd,2920521732\">fa♂乐器好评  这个硬币赏给你了</d>\r\n    <d p=\"91.318000793457,5,25,153,1485714216,0,62087ad7,2922758382\">尴尬</d>\r\n    <d p=\"148.65899658203,1,25,11890,1485730544,0,353509e8,2922982898\">好像跟你表白</d>\r\n    <d p=\"132.45500183105,1,25,0,1485762577,0,5d2bcdf1,2924673902\">合影</d>\r\n    <d p=\"184.77600097656,1,25,0,1485762678,0,5d2bcdf1,2924677622\">合影</d>\r\n    <d p=\"69.494003295898,1,25,0,1485763124,0,e5aade77,2924709204\">迷之抖腿~~~~~</d>\r\n    <d p=\"130.69999694824,1,25,15138834,1485779258,0,4767080a,2925923312\">卧操</d>\r\n    <d p=\"144.92700195312,1,25,0,1485786719,0,6f2fdebd,2926612154\">????</d>\r\n    <d p=\"18.19700050354,1,25,0,1485786833,0,6f2fdebd,2926622992\">哈哈哈哈哈    来上车</d>\r\n    <d p=\"59.818000793457,1,25,0,1485786881,0,6f2fdebd,2926627604\">还想跟你表白</d>\r\n    <d p=\"106.45099639893,5,25,14811775,1485786976,0,6f2fdebd,2926637560\">好想和你表白✌✌✌✌✌</d>\r\n    <d p=\"128.70199584961,5,25,14811775,1485787095,0,6f2fdebd,2926649094\">????看这里有个飞机</d>\r\n    <d p=\"161.55499267578,5,25,14811775,1485787455,0,6f2fdebd,2926682798\">好想跟你表白????????????</d>\r\n    <d p=\"215.04699707031,5,25,14811775,1485787517,0,6f2fdebd,2926688762\">哈哈哈哈哈</d>\r\n    <d p=\"217,5,25,14811775,1485787538,0,6f2fdebd,2926690578\">哈哈哈哈 来了上♂车</d>\r\n    <d p=\"105.19200134277,5,25,14811775,1485787682,0,6f2fdebd,2926704668\">好想跟你表白  好想跟你表白～～</d>\r\n    <d p=\"184.31799316406,5,25,14811775,1485787758,0,6f2fdebd,2926716342\">啦啦啦啦～～</d>\r\n    <d p=\"4.0269999504089,1,25,16777215,1485794331,0,e0dc4b2a,2927272758\">233333333333</d>\r\n    <d p=\"1.7450000047684,5,25,16777215,1485795997,0,c24a118c,2927372928\">恭喜发现双声道耳机福利鬼畜</d>\r\n    <d p=\"1.7450000047684,5,25,16707842,1485796062,0,c24a118c,2927373574\">恭喜发现双声道耳机福利鬼畜</d>\r\n    <d p=\"1.7450000047684,5,25,15772458,1485796075,0,c24a118c,2927374268\">恭喜发现双声道耳机福利鬼畜</d>\r\n    <d p=\"210.98399353027,1,25,14811775,1485826013,0,6f2fdebd,2927934284\">????????????????????????</d>\r\n    <d p=\"160.87199401855,5,25,16777215,1485826609,0,6f2fdebd,2927957494\">????蜜汁微笑   哈哈哈哈✌✌✌✌✌</d>\r\n    <d p=\"56.033000946045,1,25,0,1485828548,0,1e8e9bb6,2928041204\">▄█▀█●○|￣|_</d>\r\n    <d p=\"109.26699829102,1,25,16777215,1485828719,0,3c13cb92,2928048638\">合影</d>\r\n    <d p=\"10.579000473022,5,25,15138834,1485838549,0,5ebd46c1,2928640172\">来看高级弹幕的</d>\r\n    <d p=\"55.619998931885,5,25,15138834,1485838679,0,5ebd46c1,2928645724\">二零一七</d>\r\n    <d p=\"76.980003356934,5,25,15138834,1485838707,0,5ebd46c1,2928647692\">喵啊</d>\r\n    <d p=\"158.03999328613,5,25,15138834,1485838742,0,5ebd46c1,2928653958\">蜜汁点头</d>\r\n    <d p=\"146.82000732422,1,25,16777215,1485856619,0,4b46b977,2930041032\">????</d>\r\n    <d p=\"8.1450004577637,1,25,16777215,1485861724,0,072ab674,2930451106\">合影</d>\r\n    <d p=\"59.688999176025,1,25,16777215,1485864073,0,9b28f276,2930666326\">↪</d>\r\n    <d p=\"132.74800109863,1,25,16777215,1485864222,0,a4102349,2930684144\">合影</d>\r\n    <d p=\"120.22299957275,1,25,16777215,1485870353,0,cc3bf645,2931287252\">合影</d>\r\n    <d p=\"149.58700561523,1,25,16777215,1485872085,0,4b6daf6e,2931475934\">♛</d>\r\n    <d p=\"152.02699279785,1,25,41194,1485872103,0,4b6daf6e,2931477908\">■■■■■■■</d>\r\n    <d p=\"159.02699279785,1,25,41194,1485872148,0,4b6daf6e,2931482878\">♂♀♂♀♂♀♂♀♂</d>\r\n    <d p=\"4.5009999275208,1,25,16777215,1485872231,0,d08587de,2931485888\">合影</d>\r\n    <d p=\"173.82699584961,1,25,41194,1485872263,0,4b6daf6e,2931489382\">♥♥♥♥♥♥</d>\r\n    <d p=\"46.96900177002,1,25,16777215,1485872277,0,d08587de,2931490956\">合影</d>\r\n    <d p=\"53.111999511719,1,25,16777215,1485872292,0,d08587de,2931492580\">高级弹幕合影快快快</d>\r\n    <d p=\"186.52900695801,1,25,0,1485875433,0,aa7f67f7,2931822900\">看到这里手机直接卡退了</d>\r\n    <d p=\"17.30299949646,1,25,16777215,1485887635,0,5306a9f9,2932626852\">合影高级弹幕</d>\r\n    <d p=\"44.596000671387,1,25,16777215,1485919058,0,2a9a8300,2933366894\">←↓↗↘↑←↙→↓←↗↓←↖↓</d>\r\n    <d p=\"178.625,1,25,16777215,1485919252,0,37beaada,2933378936\">✈️</d>\r\n    <d p=\"76.947998046875,5,25,16707842,1485923551,0,8a1b5615,2933673174\">下的我都黄了</d>\r\n    <d p=\"126.54699707031,1,25,16777215,1485923675,0,8a1b5615,2933678278\">不就是膝盖么？给你了</d>\r\n    <d p=\"134.44000244141,1,25,16777215,1485924071,0,0e74dd2f,2933710952\">额。。。</d>\r\n    <d p=\"171.69200134277,5,25,15138834,1485929974,0,538b2035,2934169666\">低端机做好爆炸准备</d>\r\n    <d p=\"176.83599853516,1,25,16777215,1485941602,0,9a1241d7,2935124484\">❤️</d>\r\n    <d p=\"59.399299621582,1,25,16777215,1485943595,0,5f4ae6e4,2935287016\">高级弹幕</d>\r\n    <d p=\"61.083000183105,1,25,16777215,1485950921,0,5f2762a0,2935947402\">高级弹幕合影</d>\r\n    <d p=\"166.11999511719,1,25,0,1485957578,0,51432525,2936641704\">承包洛天依</d>\r\n    <d p=\"53.950000762939,5,25,15138834,1486002651,0,79ae6a59,2938581430\">禁忌秘术——神の四重奏↓</d>\r\n    <d p=\"123.90200042725,5,25,14811775,1486002759,0,79ae6a59,2938587018\">好想跟你表白~♥♥♥♥♥</d>\r\n    <d p=\"153.0890045166,1,25,8700107,1486008490,0,59c48382,2938945942\">哇</d>\r\n    <d p=\"161.24899291992,5,18,8700107,1486008510,0,59c48382,2938947346\">哇                                      </d>\r\n    <d p=\"74.43399810791,1,25,16777215,1486014566,0,d3654deb,2939414364\">2333333333</d>\r\n    <d p=\"105.20099639893,1,25,13395507,1486014596,0,d3654deb,2939416876\">up主人才啊。。。。。。</d>\r\n    <d p=\"134.6130065918,1,25,16777215,1486019313,0,4d0bb719,2939806186\">✈️</d>\r\n    <d p=\"157.56700134277,1,25,16777215,1486019351,0,4d0bb719,2939809260\">❤️❤️❤️</d>\r\n    <d p=\"219.77499389648,1,25,16777215,1486019421,0,4d0bb719,2939815336\">➡️</d>\r\n    <d p=\"86.199996948242,1,25,16777215,1486019620,0,793167a3,2939831974\">发条弹幕压压惊Σ(っ °Д °;)っ</d>\r\n    <d p=\"109.87899780273,1,25,16777215,1486019969,0,84c9f63a,2939860866\">A A A A A A A AA </d>\r\n    <d p=\"87.081001281738,1,18,9599289,1486032453,0,5b37c297,2940920438\">我勒个去</d>\r\n    <d p=\"124.98999786377,1,18,6830715,1486032460,0,5b37c297,2940926574\">辛苦高级老司机了</d>\r\n    <d p=\"80.565002441406,1,25,16777215,1486033078,0,fb29074a,2940976538\">????</d>\r\n    <d p=\"7.2529997825623,1,25,16777215,1486040998,0,1afe69a6,2941770020\">渡劫助攻</d>\r\n    <d p=\"71.05899810791,1,25,0,1486041056,0,910481df,2941776074\">????????????????</d>\r\n    <d p=\"110.1370010376,1,25,0,1486041090,0,910481df,2941780292\">喔操</d>\r\n    <d p=\"197.625,1,25,0,1486041183,0,910481df,2941789708\">✈✈????????????????????????????????????</d>\r\n    <d p=\"136.45799255371,1,25,16777215,1486041535,0,85c17597,2941828350\">睾论</d>\r\n    <d p=\"122.87999725342,1,25,16777215,1486047234,0,177d5cfe,2942457110\">为啥这么好听</d>\r\n    <d p=\"173.26699829102,1,25,16777215,1486070966,0,bcc6f6ff,2943599018\">哇哇哇</d>\r\n    <d p=\"205.72900390625,1,25,16777215,1486084875,0,a01bf9c4,2943794790\">弹幕厉害</d>\r\n    <d p=\"157.26800537109,1,25,9487136,1486097694,0,90003678,2944649748\">好想和你表白</d>\r\n    <d p=\"180.94799804688,1,25,9487136,1486097740,0,90003678,2944653738\">啦啦啦啦</d>\r\n    <d p=\"163.83799743652,1,25,16777215,1486125911,0,dba05c1e,2947240458\">❤</d>\r\n    <d p=\"78.819999694824,4,25,11890,1486126060,0,4b4cccf6,2947256646\">GG</d>\r\n    <d p=\"164.7200012207,1,25,6737151,1486126256,0,bac26d64,2947276720\">高级弹幕合影</d>\r\n    <d p=\"120.06300354004,5,25,16777215,1486126880,0,7a48e63e,2947344500\">搞基弹幕合影</d>\r\n    <d p=\"179.23199462891,5,25,41194,1486126970,0,7a48e63e,2947353750\">老婆（天依）蓝</d>\r\n    <d p=\"202.12699890137,5,25,41194,1486127003,0,7a48e63e,2947357114\">搞基弹幕合影</d>\r\n    <d p=\"6.0590000152588,1,25,16777215,1486128226,0,7f80cfec,2947490520\">合影留念</d>\r\n    <d p=\"30.464000701904,1,25,16777215,1486128475,0,30432ee2,2947518260\">我竟然跟着点头...中毒了</d>\r\n    <d p=\"28.379999160767,1,25,8700107,1486130004,0,ce6e45aa,2947688236\">三生三世</d>\r\n    <d p=\"24.280899047852,7,28,65280,1486181036,0,4e3f828c,2950880630\">[0.93,0.02,\"1-1\",4.5,\"-。-。-。-。-。-。-。-。-。-。-。-。-\\n\",0,0,0,0,500,0,1,\"SimHei\",true]</d>\r\n    <d p=\"9.1140003204346,1,25,16777215,1486190121,0,5e7a5ab3,2951650468\">百万助攻</d>\r\n    <d p=\"120.09100341797,1,25,16777215,1486208172,0,58e71fe7,2953424616\">服</d>\r\n    <d p=\"58.34700012207,7,36,16777215,1486213867,0,1be79791,2953998400\">[264,100,\"1-1\",4.5,\"2333333333333333333\",4,0,400,500,500,0,1,\"SimHei\",true]</d>\r\n    <d p=\"53.165000915527,1,25,16777215,1486218035,0,f3dc50a5,2954461262\">合影</d>\r\n    <d p=\"97.78099822998,1,25,16777215,1486260754,0,4d1c047b,2956254142\">233</d>\r\n    <d p=\"73.19580078125,7,100,65280,1486267230,0,4e3f828c,2956694876\">[506,282,\"1-1\",5,\"妙\",0,0,520,277,500,0,1,\"'Microsoft YaHei'\",true]</d>\r\n    <d p=\"147.51899719238,1,25,16777215,1486273580,0,1fe89b7e,2957218706\">????</d>\r\n    <d p=\"172.67500305176,1,25,16777215,1486273680,0,1fe89b7e,2957223004\">✈</d>\r\n    <d p=\"64.900001525879,1,25,16777215,1486274618,0,6e2fe7a7,2957303088\">????</d>\r\n    <d p=\"76.84400177002,1,25,16777215,1486275516,0,4409ae26,2957383990\">????????????</d>\r\n    <d p=\"122.30200195312,1,25,16777215,1486275603,0,f598ec80,2957391486\">厉害厉害</d>\r\n    <d p=\"9.6920003890991,1,25,0,1486283323,0,6c5758a4,2958074516\">百万助攻</d>\r\n    <d p=\"97.032997131348,4,25,16707842,1486299254,0,26583286,2959518896\">脚不由自主的抖了起来</d>\r\n    <d p=\"80.809997558594,1,25,16777215,1486299684,0,95c87c89,2959568242\">❤</d>\r\n    <d p=\"108.70400238037,1,25,16777215,1486304188,0,178e02df,2960038602\">弹幕比视频好看系列</d>\r\n    <d p=\"121.52300262451,1,25,16777215,1486312176,0,998e95f9,2960831650\">合影+1！！</d>\r\n    <d p=\"169.93299865723,5,25,16776960,1486312275,0,998e95f9,2960839110\">再次合影O(∩_∩)</d>\r\n    <d p=\"88.61799621582,1,25,16777215,1486324915,0,57369f2e,2961372434\">牛</d>\r\n    <d p=\"117.69799804688,1,25,16777215,1486324946,0,57369f2e,2961372954\">卧槽??</d>\r\n    <d p=\"18.820999145508,1,25,9487136,1486350080,0,b9d44101,2961945470\">666555666555</d>\r\n    <d p=\"16.767000198364,1,25,0,1486366522,0,1081f43d,2963360102\">额</d>\r\n    <d p=\"42.755001068115,7,36,16711680,1486368902,0,46047e6d,2963542422\">[0,0,\"1-1\",4.5,\"为什么 喜欢你 \",0,0,500,100,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"32.198001861572,1,25,16777215,1486377906,0,ca78e584,2964251224\">给弹幕跪了</d>\r\n    <d p=\"186.70199584961,1,25,16777215,1486380060,0,2c9bada3,2964432792\">有毒</d>\r\n    <d p=\"58.978000640869,1,25,16707842,1486389153,0,381bdc37,2965301738\">念大吉吧！</d>\r\n    <d p=\"5.0139999389648,1,25,0,1486407474,0,f179e7f0,2966699856\">百万助攻</d>\r\n    <d p=\"137.17599487305,1,25,16777215,1486433798,0,9796335f,2967197320\">看。灰机！！</d>\r\n    <d p=\"53.73099899292,5,25,15138834,1486437700,0,62e1e15a,2967437126\">汝等可识得此阵？</d>\r\n    <d p=\"9.0719995498657,5,25,15138834,1486438434,0,5d715760,2967483366\">高级弹幕合影</d>\r\n    <d p=\"57.370998382568,5,25,15138834,1486438503,0,5d715760,2967487728\">高级弹幕厉害了</d>\r\n    <d p=\"118.21299743652,5,25,15138834,1486438590,0,5d715760,2967493604\">高级弹幕合影????????(๑•̀ㅂ•́)و✧</d>\r\n    <d p=\"130.65100097656,5,25,15138834,1486438591,0,5d715760,2967497392\">哇，米娜快看有一架灰机</d>\r\n    <d p=\"66.122001647949,1,25,16777215,1486438714,0,e56e0f10,2967501696\">????</d>\r\n    <d p=\"79.702003479004,5,25,16777215,1486438701,0,e56e0f10,2967504596\">厉害了  </d>\r\n    <d p=\"116.03299713135,5,25,15138834,1486438869,0,e56e0f10,2967511856\">话说这么发高级弹幕啊</d>\r\n    <d p=\"1.4730000495911,1,25,0,1486438916,0,8985547d,2967515340\">看见天依就滚进来了</d>\r\n    <d p=\"36.410999298096,1,25,16777215,1486445175,0,fe1069e5,2967987488\">eeeeeeeeee</d>\r\n    <d p=\"77.311996459961,1,25,16777215,1486445458,0,0e5c090a,2968010676\">左下</d>\r\n    <d p=\"74.462997436523,1,25,0,1486459034,0,d88759c4,2969114626\">????</d>\r\n    <d p=\"111.03399658203,1,25,0,1486459070,0,d88759c4,2969117522\">????</d>\r\n    <d p=\"126.78199768066,1,25,0,1486459086,0,d88759c4,2969118698\">????</d>\r\n    <d p=\"140.12899780273,1,25,0,1486459100,0,d88759c4,2969119794\">????</d>\r\n    <d p=\"67.208000183105,1,25,16777215,1486474875,0,44de64a8,2970489528\">高級彈幕合影</d>\r\n    <d p=\"56.028999328613,1,25,16777215,1486483368,0,08709485,2971333206\">什么歌</d>\r\n    <d p=\"114.06900024414,1,25,16777215,1486483426,0,08709485,2971337892\">必须合影</d>\r\n    <d p=\"139.51699829102,1,25,16777215,1486483451,0,08709485,2971340014\">汇过去啦</d>\r\n    <d p=\"192.31500244141,1,25,16777215,1486483504,0,08709485,2971344466\">表白</d>\r\n    <d p=\"211.44299316406,1,25,16777215,1486483523,0,08709485,2971345992\">表白~~~~~~~~~~~~~~~~~~~~~~··</d>\r\n    <d p=\"86.150001525879,5,25,6750207,1486496478,0,a21ae8d4,2971954550\">欢迎发现镇站之宝+永远有人系列+高级弹幕集合之地</d>\r\n    <d p=\"79.720001220703,1,25,16777215,1486536435,0,7c4be7a8,2973582972\">bgm是什么？？？</d>\r\n    <d p=\"2.9509999752045,1,25,16777215,1486543624,0,eae8fa57,2974165430\">66了</d>\r\n    <d p=\"59.526298522949,1,25,16711680,1486543785,0,a4370c36,2974178998\">好听</d>\r\n    <d p=\"73.206100463867,1,25,16711680,1486543799,0,a4370c36,2974180140\">蜜汁抖腿</d>\r\n    <d p=\"102.16699981689,1,25,16711680,1486543827,0,a4370c36,2974182382\">233333</d>\r\n    <d p=\"125.52600097656,1,25,16711680,1486543851,0,a4370c36,2974184298\">合影</d>\r\n    <d p=\"9.1863098144531,1,25,16711680,1486543957,0,a4370c36,2974193108\">100万助攻</d>\r\n    <d p=\"20.986299514771,1,25,16711680,1486543968,0,a4370c36,2974194038\">百万助攻</d>\r\n    <d p=\"31.575000762939,1,25,15138834,1486558379,0,fc4541a1,2975453394\">弹幕</d>\r\n    <d p=\"127.78099822998,1,25,16777215,1486560177,0,6e1966e2,2975633634\">合影</d>\r\n    <d p=\"28.688999176025,1,25,16777215,1486568393,0,98fecd13,2976508430\">高级弹幕合影</d>\r\n    <d p=\"191.43499755859,1,25,16777215,1486615801,0,cf3d1037,2978411592\">ddd</d>\r\n    <d p=\"194.76800537109,1,25,16777215,1486615832,0,cf3d1037,2978414126\">dd</d>\r\n    <d p=\"65.377998352051,4,25,16777215,1486619926,0,fdfb537d,2978754490\">全程左下</d>\r\n    <d p=\"43.97200012207,1,25,16776960,1486619946,0,b8dece63,2978756030\">吓人</d>\r\n    <d p=\"157.07299804688,1,25,16776960,1486620051,0,b8dece63,2978764412\">高级弹幕好厉害</d>\r\n    <d p=\"182.75700378418,1,25,16776960,1486620077,0,b8dece63,2978766352\">厉害厉害</d>\r\n    <d p=\"148.14100646973,1,25,16777215,1486621336,0,3d90d378,2978867480\">????????????????????????????????????????</d>\r\n    <d p=\"89.251998901367,1,25,6750156,1486623717,0,de2cd555,2979056356\">搞基弹幕</d>\r\n    <d p=\"133.01100158691,1,25,14811775,1486624086,0,9341d411,2979085320\">→_→</d>\r\n    <d p=\"29.760999679565,5,25,16711680,1486627897,0,b9bb877c,2979389560\">合影</d>\r\n    <d p=\"52.231998443604,5,25,16711680,1486627920,0,b9bb877c,2979391328\">蜜汁</d>\r\n    <d p=\"78.097999572754,5,25,16711680,1486627946,0,b9bb877c,2979393408\">全程高能</d>\r\n    <d p=\"46.837001800537,7,36,3407871,1486638186,0,cf3d1037,2980269106\">[2,1,\"1-1\",4.5,\"拒绝跟你们合影\",7,6,8,5,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"108.6009979248,7,36,3407871,1486638248,0,cf3d1037,2980274266\">[1,1,\"1-1\",4.5,\"望八\",7,6,50,50,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"171.97399902344,7,36,3407871,1486638312,0,cf3d1037,2980279676\">[1,1,\"1-1\",4.5,\"哇\",7,6,682,438,1500,0,true,\"黑体\",1]</d>\r\n    <d p=\"183.91900634766,7,36,3407871,1486638324,0,cf3d1037,2980280648\">[1,1,\"1-1\",4.5,\"啦啦啦啦\",7,6,682,438,1500,0,true,\"黑体\",1]</d>\r\n    <d p=\"201.49800109863,7,36,3407871,1486638341,0,cf3d1037,2980282142\">[1,1,\"1-1\",4.5,\"啦啦啦啦啦啦啦啦啦啦啦啦啦\",7,6,682,438,2000,0,true,\"黑体\",1]</d>\r\n    <d p=\"82.237998962402,7,36,3407871,1486638455,0,cf3d1037,2980291622\">[1,1,\"1-1\",4.5,\"望八\",7,6,682,438,5000,0,true,\"楷体\",1]</d>\r\n    <d p=\"201.01400756836,7,36,3407871,1486638574,0,cf3d1037,2980301758\">[1,1,\"1-1\",4.5,\"啦啦啦啦啦啦啦啦啦啦啦啦啦\",7,6,682,438,5000,0,true,\"楷体\",1]</d>\r\n    <d p=\"5.9409999847412,1,25,16777215,1486639901,0,057d4ca2,2980418352\">见依滚系列</d>\r\n    <d p=\"5.9409999847412,1,25,16707842,1486639916,0,057d4ca2,2980419676\">谁又给我顶上来了</d>\r\n    <d p=\"81.081001281738,1,25,16707842,1486639995,0,057d4ca2,2980427236\">兲</d>\r\n    <d p=\"9.871000289917,1,25,9599289,1486640044,0,72291b8d,2980431790\">合影</d>\r\n    <d p=\"126.08100128174,1,25,16707842,1486640015,0,057d4ca2,2980434484\">bgm:萧亚轩-表白</d>\r\n    <d p=\"186.04100036621,1,25,16707842,1486640131,0,057d4ca2,2980445508\">满屏的66ccff…可以这很搞基弹幕</d>\r\n    <d p=\"196.78100585938,1,25,16707842,1486640215,0,057d4ca2,2980447822\">言和已报警</d>\r\n    <d p=\"211.92100524902,1,25,16707842,1486640319,0,057d4ca2,2980458112\">紧依卫狄仁杰……</d>\r\n    <d p=\"7.5390000343323,7,80,65535,1486641918,0,cf3d1037,2980609560\">[1,1,\"1-1\",5,\"全程搞基弹幕\",7,6,682,438,5000,0,false,\"楷体\",1]</d>\r\n    <d p=\"109.78800201416,7,80,65535,1486642150,0,cf3d1037,2980631466\">[682,438,\"1-1\",5,\"表♂白\",7,6,1,1,5000,0,false,\"楷体\",1]</d>\r\n    <d p=\"77.213996887207,7,80,65535,1486642374,0,cf3d1037,2980653192\">[682,438,\"1-1\",10,\"★/n       ★/n              ★/n\",7,6,1,1,5000,0,false,\"楷体\",1]</d>\r\n    <d p=\"164.39999389648,7,80,65535,1486642461,0,cf3d1037,2980661512\">[682,438,\"1-1\",10,\"好像跟你表白\",7,6,1,1,5000,0,false,\"楷体\",1]</d>\r\n    <d p=\"155.86500549316,1,25,16777215,1486646542,0,62958a35,2981066276\">哈哈哈哈哈哈</d>\r\n    <d p=\"212.70399475098,1,25,16777215,1486646642,0,62958a35,2981076408\">杠铃般的笑声</d>\r\n    <d p=\"104,1,25,16777215,1486647237,0,78d8351b,2981137512\">比原唱有故事</d>\r\n    <d p=\"97.059997558594,1,25,16777215,1486651447,0,af466f00,2981572538\">进来 就 出不去了。，。</d>\r\n    <d p=\"66.90599822998,1,25,41194,1486651735,0,78d35164,2981601696\">鸡年大吉吧????</d>\r\n    <d p=\"53.587001800537,1,25,16777215,1486661868,0,5e648903,2982424500\">咦</d>\r\n    <d p=\"35.173000335693,1,25,16777215,1486665854,0,978b12cf,2982579366\">JJ撑大捅</d>\r\n    <d p=\"12.298999786377,1,25,15772458,1486688022,0,513a7095,2982921224\">真的吗？</d>\r\n    <d p=\"44.88399887085,1,25,16777215,1486695035,0,4d1c047b,2983280856\">我是萧亚轩，我已经报警了</d>\r\n    <d p=\"101.12000274658,1,25,16777215,1486695115,0,4d1c047b,2983285992\">萧亚轩已经报警了！！！！！！</d>\r\n    <d p=\"183.92500305176,1,25,16777215,1486695209,0,4d1c047b,2983292100\">天依~~</d>\r\n    <d p=\"63.380001068115,1,25,16777215,1486696378,0,73b6b8b0,2983375698\">表白</d>\r\n    <d p=\"76.046997070312,1,25,16777215,1486696390,0,73b6b8b0,2983376560\">秒</d>\r\n    <d p=\"104.25299835205,1,25,16777215,1486696418,0,73b6b8b0,2983378458\">化学老师摇试管的样子</d>\r\n    <d p=\"97.615997314453,5,25,41194,1486717828,0,ca6766a6,2985110092\">靠♂</d>\r\n    <d p=\"120.30200195312,5,25,41194,1486717902,0,ca6766a6,2985116144\">前方打飞机福利</d>\r\n    <d p=\"65.339996337891,5,18,6830715,1486725405,0,c1a549b2,2985751632\">好多高级弹幕。。</d>\r\n    <d p=\"73.759002685547,1,25,16777215,1486731785,0,1d956865,2986370516\">OTR</d>\r\n    <d p=\"179.57800292969,1,25,16777215,1486731891,0,1d956865,2986381136\">抖腿ing</d>\r\n    <d p=\"14.741000175476,1,25,16707842,1486737873,0,285e68d8,2987008796\">试试酷不酷</d>\r\n    <d p=\"24.888000488281,1,18,16707842,1486737897,0,285e68d8,2987011388\">天呐</d>\r\n    <d p=\"31.319999694824,1,18,16707842,1486737910,0,285e68d8,2987012798\">慌了</d>\r\n    <d p=\"115.8990020752,1,25,16777215,1486738089,0,d0b32c52,2987031764\">合影合影</d>\r\n    <d p=\"170.07699584961,1,25,0,1486743047,0,03efb318,2987549822\">兲</d>\r\n    <d p=\"71.973999023438,1,25,0,1486747625,0,61739d4e,2987871494\">深夜重温</d>\r\n    <d p=\"51.407001495361,1,25,16777215,1486778504,0,fe1069e5,2988605698\">eeeeeeeeeee</d>\r\n    <d p=\"43.627101898193,1,25,16777215,1486779168,0,606cad41,2988641448\">二倍速马头要超神</d>\r\n    <d p=\"117.45999908447,1,25,16777215,1486781840,0,bbf7ef48,2988807204\">卧槽，高级弹幕炸了</d>\r\n    <d p=\"126.47899627686,1,25,16777215,1486783517,0,cbd30de4,2988916370\">……</d>\r\n    <d p=\"125.52100372314,1,25,16777215,1486788968,0,86b0e192,2989309868\">我是萧亚轩 我报警了</d>\r\n    <d p=\"70.878997802734,4,25,15138834,1486802943,0,e5cda6b0,2990456048\">                                                                   ????????????????????????????????</d>\r\n    <d p=\"27.884000778198,1,25,16777215,1486812798,0,92586c89,2991216382\">姿势美如画( *・ω・)✄╰ひ╯</d>\r\n    <d p=\"83.623001098633,1,25,16777215,1486819076,0,e5c9e5fb,2991774576\">合影</d>\r\n    <d p=\"126.15399932861,1,25,16777215,1486820456,0,d04f45a9,2991909820\">吊吊吊</d>\r\n    <d p=\"45.139999389648,1,25,16777215,1486820521,0,cb753e42,2991916058\">╭(╯^╰)╮</d>\r\n    <d p=\"20.006399154663,5,25,16711680,1486820749,0,97fc0e13,2991938432\">qwq</d>\r\n    <d p=\"51.312000274658,1,25,16737792,1486821321,0,a11f12a9,2991994446\">高级弹幕</d>\r\n    <d p=\"113.077003479,1,25,16737792,1486821387,0,a11f12a9,2992001044\">合影</d>\r\n    <d p=\"206.14100646973,1,25,41194,1486821636,0,6246ffb0,2992031536\">吃货殿下！！！</d>\r\n    <d p=\"12.034999847412,1,25,16777215,1486824318,0,91b9ed68,2992297068\">这首歌屌！！！！！！</d>\r\n    <d p=\"67.930000305176,1,25,16777215,1486824373,0,91b9ed68,2992302728\">太屌啦！！！！！！！！！！</d>\r\n    <d p=\"135.5590057373,1,25,16777215,1486824441,0,91b9ed68,2992309374\">23333333333333333</d>\r\n    <d p=\"160.84399414062,1,25,0,1486840013,0,c4464989,2993337050\">∑</d>\r\n    <d p=\"218.65199279785,1,25,0,1486840130,0,c4464989,2993338466\">????</d>\r\n    <d p=\"4.1389999389648,1,25,15138834,1486877641,0,970f345b,2994642092\">我从未见过有如此厚颜无耻之人</d>\r\n    <d p=\"2.558000087738,5,25,15138834,1486877699,0,970f345b,2994646536\">我从未见过有如此厚颜无耻之人</d>\r\n    <d p=\"10.880999565125,1,25,0,1486916495,0,0bd2e8da,2997524852\">百万助攻</d>\r\n    <d p=\"62.458000183105,1,25,0,1486916546,0,0bd2e8da,2997527798\">不知听了多少遍</d>\r\n    <d p=\"52.90599822998,1,25,16777215,1486917438,0,d5c1971d,2997578878\">这不就是原版歌词？</d>\r\n    <d p=\"0.0060000000521541,1,25,16777215,1486952186,0,0cace948,2998272112\">233333</d>\r\n    <d p=\"147.04699707031,1,25,16777215,1486952336,0,0cace948,2998277162\">23333333</d>\r\n    <d p=\"8.1092500686646,1,25,16777215,1486952543,0,0cace948,2998283924\">可以</d>\r\n    <d p=\"73.370597839355,1,18,16777215,1486952829,0,0cace948,2998293866\">可以</d>\r\n    <d p=\"22.378999710083,1,25,15138834,1486969661,0,991ef3d3,2999127824\">可怕</d>\r\n    <d p=\"181.80299377441,1,25,16777215,1486969984,0,c4f44b8d,2999142958\">神级弹幕合影</d>\r\n    <d p=\"181.80299377441,5,25,16777215,1486969998,0,c4f44b8d,2999143630\">神级弹幕合影</d>\r\n    <d p=\"186.41799926758,1,25,16777215,1486989335,0,836f3d55,3000234058\">2333333</d>\r\n    <d p=\"28.09760093689,1,25,16777215,1486994178,0,a5be94a2,3000606580\">夏人</d>\r\n    <d p=\"65.00700378418,1,25,16777215,1486996393,0,a393e1d3,3000783290\">❤</d>\r\n    <d p=\"96.419998168945,1,25,16777215,1487086486,0,5e7a5ab3,3004642932\">214是时候百万了</d>\r\n    <d p=\"11.861000061035,1,25,16777215,1487086701,0,51ce0c9c,3004662092\">兄贵</d>\r\n    <d p=\"16.554000854492,1,25,16777215,1487086785,0,51ce0c9c,3004663616\">德国boyrap时间</d>\r\n    <d p=\"57.087001800537,1,25,16777215,1487086838,0,51ce0c9c,3004667178\">Elva已报警</d>\r\n    <d p=\"116.30899810791,1,25,16777215,1487086938,0,51ce0c9c,3004674082\">兄贵站在那儿一动不动</d>\r\n    <d p=\"190.20799255371,1,25,16777215,1487087033,0,51ce0c9c,3004680386\">参加天依公主</d>\r\n    <d p=\"140.1490020752,1,25,16777215,1487133602,0,0e572466,3005854794\">玛德我们飞机被打了！！</d>\r\n    <d p=\"41.189800262451,1,25,16777215,1487155186,0,3b01343f,3006943958\">这马头 可以 稳</d>\r\n    <d p=\"126.22899627686,1,25,16777215,1487155272,0,3b01343f,3006948616\">哇哇哇哇哇</d>\r\n    <d p=\"198.72999572754,1,25,16777215,1487155343,0,3b01343f,3006952688\">妙啊</d>\r\n    <d p=\"51.907001495361,5,25,16724889,1487155499,0,3dd319ab,3006961726\">飘逸的弹幕~</d>\r\n    <d p=\"59.587001800537,5,25,16724889,1487155519,0,3dd319ab,3006962950\">妙啊</d>\r\n    <d p=\"172.06300354004,5,25,16724889,1487155633,0,3dd319ab,3006969428\">做得比一个半导铁匠好多了~</d>\r\n    <d p=\"54.292999267578,1,25,16777215,1487160788,0,02efc52b,3007302134\">合影</d>\r\n    <d p=\"153.77600097656,1,25,16777215,1487161566,0,d70be0d1,3007355646\">高级弹屏LOW到哭，跪着看下去</d>\r\n    <d p=\"187.60800170898,1,25,16777215,1487161600,0,d70be0d1,3007358022\">莫名的跟着唱</d>\r\n    <d p=\"217.00700378418,1,25,16777215,1487161657,0,d70be0d1,3007361884\">这波厉害，攻下他们！</d>\r\n    <d p=\"181.26400756836,1,25,16777215,1487225776,0,880f5035,3009825420\">圆桌骑士团在此</d>\r\n    <d p=\"101.125,1,18,16777215,1487231189,0,516722d2,3010124264\">一直旺八出中国</d>\r\n    <d p=\"31.259000778198,4,25,16777215,1487297983,0,68d09659,3012833748\">孔明神文圣武</d>\r\n    <d p=\"31.906999588013,4,25,16777215,1487298078,0,68d09659,3012836806\">抱大器，扫四方</d>\r\n    <d p=\"33.284000396729,4,25,16777215,1487298189,0,68d09659,3012840556\">吉吉成大统，席席卷八荒</d>\r\n    <d p=\"94.696998596191,4,25,16777215,1487298463,0,68d09659,3012850182\">大洋出中国一只王八</d>\r\n    <d p=\"118.69699859619,4,25,16777215,1487298507,0,68d09659,3012851494\">经典弹幕</d>\r\n    <d p=\"37.798999786377,1,25,16777215,1487342528,0,59f26727,3015593370\">▄█▀█●</d>\r\n    <d p=\"62.918998718262,1,25,16777215,1487342557,0,59f26727,3015595922\">回口回</d>\r\n    <d p=\"50.960998535156,1,25,0,1487343600,0,68e6477b,3015690014\">合影</d>\r\n    <d p=\"66.619003295898,1,25,16777215,1487344201,0,d83d3e13,3015743322\">蜜汁带感～</d>\r\n    <d p=\"44.768001556396,1,25,16777215,1487344276,0,1b956053,3015750186\">蜜汁摇头</d>\r\n    <d p=\"210.46000671387,1,25,16777215,1487346206,0,cf1046ca,3015916636\">必火</d>\r\n    <d p=\"178.59300231934,1,25,0,1487346242,0,a8bf6e58,3015924774\">交出了膝盖</d>\r\n    <d p=\"24.02499961853,1,25,0,1487346649,0,a8bf6e58,3015953048\">开口跪????????</d>\r\n    <d p=\"128.55299377441,1,25,16777215,1487351843,0,bde6d7fd,3016299700\">马航，我终于找到你了????</d>\r\n    <d p=\"136.91200256348,5,25,16777215,1487351885,0,bde6d7fd,3016301278\">马航，你肿么了，你醒醒????????????????????</d>\r\n    <d p=\"130.90400695801,5,25,16777215,1487352164,0,4b9d9b30,3016314158\">高级弹幕合影</d>\r\n    <d p=\"169.57899475098,5,25,16777215,1487352202,0,4b9d9b30,3016315948\">王者之哇</d>\r\n    <d p=\"58.473300933838,1,25,16777215,1487357252,0,3a80b4bc,3016483848\">高级弹幕合影</d>\r\n    <d p=\"11.809000015259,1,25,16777215,1487388369,0,d2fc6046,3017222040\">合影</d>\r\n    <d p=\"86.805999755859,1,25,16777215,1487393763,0,f8494216,3017566068\">orz</d>\r\n    <d p=\"40.604000091553,1,25,0,1487398336,0,021be093,3017891456\">▄█▀█●</d>\r\n    <d p=\"57.354999542236,1,25,0,1487398409,0,021be093,3017892560\">囧rz</d>\r\n    <d p=\"51.13399887085,1,18,16777215,1487398852,0,e5ad9056,3017923372\">合影合影合影</d>\r\n    <d p=\"104.28299713135,1,25,16777215,1487413759,0,5167b8f7,3018980236\">遍地神弹幕</d>\r\n    <d p=\"116.06900024414,1,25,0,1487414656,0,a52adc85,3019045950\">HHHHHHHHHH</d>\r\n    <d p=\"185.25799560547,1,25,16777215,1487431220,0,b95d4562,3020496764\">老婆！！！！！！！！</d>\r\n    <d p=\"202.54600524902,1,25,41194,1487434050,0,b090cd78,3020735726\">救我天依老婆！</d>\r\n    <d p=\"96.703002929688,1,25,0,1487436060,0,4e051891,3020883132\">好贵的视频啊</d>\r\n    <d p=\"22.191999435425,1,25,0,1487436597,0,6da1bfe5,3020917712\">前来考古</d>\r\n    <d p=\"69.919998168945,1,25,0,1487436657,0,6da1bfe5,3020921786\">晋舞团</d>\r\n    <d p=\"15.546999931335,5,25,16711680,1487438975,0,9a568bd0,3021047566\">高级但目录合影</d>\r\n    <d p=\"75.363998413086,5,25,16711680,1487439035,0,9a568bd0,3021050294\">百万助攻</d>\r\n    <d p=\"126.32099914551,5,25,16711680,1487439086,0,9a568bd0,3021052534\">握草，合影</d>\r\n    <d p=\"189.85800170898,1,25,41194,1487467828,0,26ff5d95,3021646320\">哇</d>\r\n    <d p=\"118.08899688721,1,25,16777215,1487492136,0,fe1069e5,3023309432\">?////</d>\r\n    <d p=\"63.437301635742,1,25,16777215,1487502888,0,d0138603,3024059636\">厉害</d>\r\n    <d p=\"141.57800292969,1,25,16777215,1487502966,0,d0138603,3024065084\">头一次见这么发高弹的，真土壕</d>\r\n    <d p=\"27.895000457764,1,25,0,1487503313,0,2e9e602a,3024089268\">23333333</d>\r\n    <d p=\"42.145999908447,1,25,0,1487503327,0,2e9e602a,3024090236\">up主人才</d>\r\n    <d p=\"61.131999969482,1,25,0,1487503346,0,2e9e602a,3024091552\">音乐区，拔剑吧！</d>\r\n    <d p=\"27.70599937439,1,25,16777215,1487505457,0,1ab93381,3024242120\">23333333333333</d>\r\n    <d p=\"202.42999267578,1,25,16777215,1487567511,0,278c4d19,3026408304\">合影</d>\r\n    <d p=\"67.366500854492,5,25,10027008,1487568093,0,77f28320,3026440782\">晋舞团</d>\r\n    <d p=\"0.76099997758865,1,25,41194,1487569811,0,fb9aba2f,3026527264\">洛天依记包子铺。</d>\r\n    <d p=\"1.9390000104904,1,25,16777215,1487589949,0,68d09659,3027465410\">100万助攻</d>\r\n    <d p=\"17.32200050354,1,18,16777215,1487599301,0,2a6d5c72,3028090924\">百万助攻</d>\r\n    <d p=\"47.773998260498,1,25,16777215,1487599847,0,1fc43500,3028131948\">百万助攻~~</d>\r\n    <d p=\"26.736000061035,1,25,16777215,1487600468,0,bd9b268d,3028180818\">耳机福利啊</d>\r\n    <d p=\"219.71200561523,1,25,0,1487618725,0,d72f3ae1,3028995762\">7</d>\r\n    <d p=\"0.0060000000521541,1,25,16777215,1487664745,0,27dc66ea,3030148462\">a</d>\r\n    <d p=\"0.0060000000521541,1,25,16777215,1487665128,0,27dc66ea,3030163994\">阿萨德</d>\r\n    <d p=\"64.94100189209,1,25,16777215,1487679588,0,58a823a9,3030932370\">向百万进军</d>\r\n    <d p=\"131.13900756836,1,25,38979,1487688716,0,b6e19733,3031599746\">服服服</d>\r\n    <d p=\"67.988998413086,1,25,16777215,1487688880,0,b2d801da,3031612816\">迷之带感</d>\r\n    <d p=\"98.055000305176,1,25,16711680,1487688910,0,b2d801da,3031615306\">来来来 一起抖腿</d>\r\n    <d p=\"40.956001281738,1,25,16777215,1487696204,0,2376d8ae,3032057468\">这段调子超好听！</d>\r\n    <d p=\"0.0060000000521541,1,25,16777215,1487730142,0,27dc66ea,3032601980\">asdasdasd</d>\r\n    <d p=\"68.716003417969,1,25,16777215,1487734529,0,d7db6a8b,3032739874\">♥</d>\r\n    <d p=\"0.0060000000521541,1,25,16777215,1487751134,0,27dc66ea,3033519608\">巫师3</d>\r\n    <d p=\"55.102001190186,4,25,16707842,1487753468,0,f8ec3cd8,3033623940\">←我一直在看这两位→</d>\r\n    <d p=\"75.107200622559,1,25,16777215,1487757199,0,e661a0e4,3033795942\">妙啊，妙啊</d>\r\n    <d p=\"123.06800079346,1,25,16711935,1487763931,0,76da7b93,3034182252\">这个配乐,真的比原唱带感太多了</d>\r\n    <d p=\"1.597000002861,1,25,16777215,1487778999,0,f3e7b16a,3035239042\">百万助攻</d>\r\n    <d p=\"100.50099945068,1,25,16777215,1487779164,0,f3e7b16a,3035246882\">腿要抖断了</d>\r\n    <d p=\"175.99800109863,1,25,16707842,1487817125,0,5d9da37b,3036041414\">♡♡♡♡♡♡♡</d>\r\n    <d p=\"48.359100341797,1,25,16777215,1487834577,0,660de76b,3036793372\">123</d>\r\n    <d p=\"64.376197814941,1,25,16711680,1487859714,0,c2d8450b,3038191158\">合影合影</d>\r\n    <d p=\"131.50799560547,1,25,16777215,1487864537,0,bc279153,3038539260\">高级弹幕强行合影</d>\r\n    <d p=\"14.977999687195,1,25,16777215,1487898577,0,8a4a09e4,3039169326\">老狗，我们走</d>\r\n    <d p=\"139.33700561523,1,25,16777215,1487899282,0,fdbedf88,3039184260\">合影弹幕</d>\r\n    <d p=\"150.00100708008,1,25,16777215,1487900271,0,81428588,3039206378\">✈</d>\r\n    <d p=\"48.778999328613,1,25,16777215,1487901000,0,bd801df3,3039224218\">高级弹幕合影</d>\r\n    <d p=\"185.17900085449,1,25,16777215,1487901141,0,bd801df3,3039227810\">合影</d>\r\n    <d p=\"164.58000183105,1,25,15138834,1487901204,0,8d57feea,3039229352\">高级弹幕合影</d>\r\n    <d p=\"85.443000793457,1,25,0,1487903707,0,4ebcf473,3039306618\">2333</d>\r\n    <d p=\"207.39500427246,1,25,0,1487904623,0,8607f68b,3039333742\">红米3s存活确认</d>\r\n    <d p=\"109.66600036621,1,25,16777215,1487907509,0,758369f8,3039430396\">うつとうしい</d>\r\n    <d p=\"147.0359954834,1,25,16777215,1487909815,0,e2f8b30e,3039520404\">????</d>\r\n    <d p=\"152.60400390625,1,25,16707842,1487909882,0,e2f8b30e,3039523354\">233333333333</d>\r\n    <d p=\"48.543998718262,1,25,10079283,1487913173,0,02efc52b,3039715928\">你之间跳动的电光，是我永生不变的信仰。唯我超电磁炮永世长存</d>\r\n    <d p=\"153.04400634766,1,25,16777215,1487929868,0,e176c395,3040465022\">233333333333333</d>\r\n    <d p=\"191.76400756836,1,25,16777215,1487930944,0,ebd2546e,3040529272\">????</d>\r\n    <d p=\"124.19899749756,1,25,16777215,1487932088,0,b744e42c,3040592274\">丫，光头</d>\r\n    <d p=\"117.33300018311,1,25,16777215,1487932168,0,3049631d,3040600594\">手机党的失败</d>\r\n    <d p=\"52.578998565674,1,25,16777215,1487932814,0,82ad90b0,3040635840\">????????????</d>\r\n    <d p=\"145.07899475098,1,25,41194,1487932856,0,82ad90b0,3040642434\">????❤❤❤</d>\r\n    <d p=\"141.32400512695,1,25,16777215,1487940081,0,cc011e38,3041144632\">高级弹幕哈，有个东西叫屏蔽弹幕你知道吗？</d>\r\n    <d p=\"79.124000549316,1,25,16777215,1487941072,0,5583a4ad,3041220924\">搞基弹幕厉害了</d>\r\n    <d p=\"34.029998779297,1,25,16777215,1487954923,0,15c39202,3042274532\">神弹幕合影~</d>\r\n    <d p=\"117.11699676514,1,25,16777215,1487955029,0,15c39202,3042279690\">合影~</d>\r\n    <d p=\"219.75199890137,1,25,16777215,1487955139,0,15c39202,3042285004\">最后合影</d>\r\n    <d p=\"157.16200256348,1,25,16777215,1487985801,0,dc827403,3042840026\">23333</d>\r\n    <d p=\"5.4530000686646,1,25,4294967295,1487994736,0,eabb2be6,3043276960\">百万助攻</d>\r\n    <d p=\"77.390998840332,1,25,4294967295,1487998164,0,85bf1dd0,3043491064\">(｢･ω･)｢嘿</d>\r\n    <d p=\"123.90000152588,1,25,4294967295,1487998142,0,85bf1dd0,3043493958\">(￣ε(#￣)</d>\r\n    <d p=\"183.54800415039,1,25,16777215,1487998366,0,1bae4a6e,3043504660\">表白</d>\r\n    <d p=\"219.71099853516,1,25,4294967295,1487998372,0,85bf1dd0,3043505032\">(*◑∇◑)☞☜(◐∇◐*)</d>\r\n    <d p=\"207.6369934082,1,25,16777215,1487998390,0,1bae4a6e,3043506164\">合影</d>\r\n    <d p=\"82.180000305176,1,25,16777215,1487998753,0,5533f8d3,3043531422\">抖起来</d>\r\n    <d p=\"68.925003051758,1,25,16777215,1487998940,0,205af433,3043544114\">拔剑</d>\r\n    <d p=\"2.7300000190735,1,25,16777215,1487998981,0,9321fa98,3043546830\">百万助攻</d>\r\n    <d p=\"50.290000915527,1,25,16777215,1488006564,0,aa718cac,3044040490\">可怕</d>\r\n    <d p=\"84.001998901367,1,25,16777215,1488007306,0,e903055f,3044087594\">妙</d>\r\n    <d p=\"42.609001159668,1,25,16711935,1488025870,0,76da7b93,3045430444\">全程右下角丶没办法我也是情不自禁啊</d>\r\n    <d p=\"62.878353118896,1,25,16777215,1488026492,0,dc0de5ae,3045482592\">我哩个去</d>\r\n    <d p=\"69.938690185547,1,25,16777215,1488026505,0,dc0de5ae,3045483592\">迷之带感</d>\r\n    <d p=\"217.23901367188,1,25,16777215,1488026661,0,dc0de5ae,3045496772\">厉害厉害</d>\r\n    <d p=\"73.323997497559,1,25,16777215,1488027125,0,69a5d102,3045541944\">干猪</d>\r\n    <d p=\"109.85800170898,1,25,16777215,1488034183,0,686dc957,3046162730\">厉害啦。~</d>\r\n    <d p=\"142.15699768066,1,25,16777215,1488034214,0,686dc957,3046165798\">合影~</d>\r\n    <d p=\"161.6369934082,1,25,16777215,1488034233,0,686dc957,3046167614\">12个小伙伴你们好</d>\r\n    <d p=\"183.27699279785,1,25,16777215,1488034255,0,686dc957,3046169586\">合影~</d>\r\n    <d p=\"20.22500038147,1,25,16777215,1488035068,0,d9d3b491,3046241836\">合影高级弹幕</d>\r\n    <d p=\"147.55299377441,1,25,16777215,1488036542,0,63437fb0,3046374236\">🛫🛫🛫</d>\r\n    <d p=\"89.513999938965,1,25,16777215,1488038925,0,ddc47e63,3046554720\">治好了我的颈椎病</d>\r\n    <d p=\"172.41000366211,1,25,16777215,1488042998,0,9bd35ac7,3046796600\">●﹏●</d>\r\n    <d p=\"205,1,25,6830715,1488090978,0,c333f5b5,3048562476\">高级弹幕就是牛逼啊</d>\r\n    <d p=\"77.468002319336,1,25,16777215,1488091045,0,606b9ddf,3048566994\">合影</d>\r\n    <d p=\"66.208000183105,1,25,16777215,1488105678,0,3c4d2890,3049509026\">高级弹幕合影</d>\r\n    <d p=\"33.568000793457,1,25,16777215,1488105926,0,058116f8,3049523862\">搞基弹幕合影</d>\r\n    <d p=\"72.11799621582,5,25,6830715,1488105982,0,058116f8,3049527270\">和搞基弹幕合影</d>\r\n    <d p=\"179.98199462891,1,25,4294967295,1488119118,0,6e7d248c,3050417702\">✈</d>\r\n    <d p=\"159.66468811035,1,25,16777215,1488120512,0,bb47ca8a,3050524678\">头不知觉的摇了起来</d>\r\n    <d p=\"106.57312774658,1,25,16777215,1488120590,0,bb47ca8a,3050530570\">双手打字以示清白</d>\r\n    <d p=\"183.34700012207,1,25,16777215,1488122378,0,18d152c3,3050661454\">合影合影</d>\r\n    <d p=\"159.3159942627,1,25,16777215,1488123197,0,9e824c4b,3050718890\">bb</d>\r\n    <d p=\"117.86199951172,5,25,16777215,1488157237,0,344c2124,3051333464\">合影</d>\r\n    <d p=\"50.352001190186,1,25,16777215,1488159312,0,93485201,3051367942\">▄█▀█●给跪了</d>\r\n    <d p=\"56.745998382568,1,25,16777215,1488160052,0,dbc78203,3051381446\">弹幕呢。。。</d>\r\n    <d p=\"156.94900512695,1,25,16777215,1488160159,0,ec97405e,3051383370\">2333333333333</d>\r\n    <d p=\"82.480003356934,1,25,4294967295,1488162258,0,6994f0a9,3051424412\">囍</d>\r\n    <d p=\"184.58500671387,1,25,4294967295,1488162360,0,6994f0a9,3051426686\">妙</d>\r\n    <d p=\"51.199001312256,5,25,16777215,1488163288,0,5a8db651,3051447928\">在这里刷炮姐的滚出去</d>\r\n    <d p=\"114.28299713135,5,25,16777215,1488163358,0,5a8db651,3051449510\">合影</d>\r\n    <d p=\"138.20300292969,5,25,16777215,1488163388,0,5a8db651,3051450196\">全程合影</d>\r\n    <d p=\"195.92300415039,5,25,16777215,1488163448,0,5a8db651,3051451708\">合影</d>\r\n    <d p=\"13.869219779968,1,25,16777215,1488163991,0,c31d4957,3051464832\">恭喜百万达成</d>\r\n    <d p=\"196.41299438477,1,25,16777215,1488164408,0,1223b09b,3051475156\">高级弹幕合影中</d>\r\n    <d p=\"36.29700088501,1,25,16777215,1488169863,0,1390b2ed,3051632358\">百万助攻</d>\r\n    <d p=\"31.142000198364,1,25,16777215,1488200497,0,f4173fa7,3052902452\">我从未见过有如此厚颜无耻之人</d>\r\n    <d p=\"12.454999923706,1,25,16777215,1488245279,0,cfbba4ba,3054093550\">我从未见过有如此厚颜无耻之人</d>\r\n    <d p=\"72.217002868652,1,25,16777215,1488245340,0,cfbba4ba,3054094510\">❀</d>\r\n    <d p=\"106.41899871826,1,25,16777215,1488245374,0,cfbba4ba,3054095112\">➷  ❤    </d>\r\n    <d p=\"133.71800231934,1,25,16777215,1488245402,0,cfbba4ba,3054095556\">✈</d>\r\n    <d p=\"0.0060000000521541,1,25,16777215,1488253074,0,660de76b,3054260862\">自己的弹幕</d>\r\n    <d p=\"40.01900100708,1,25,16777215,1488259325,0,fc5dfe6b,3054532252\">弹幕又被清理了</d>\r\n    <d p=\"14.869000434875,1,25,16777215,1488261395,0,76661dc8,3054611794\">合影</d>\r\n    <d p=\"87.829002380371,1,25,16777215,1488261468,0,76661dc8,3054614144\">进了猪哥的KTV</d>\r\n    <d p=\"80.775001525879,1,25,16777215,1488262361,0,48dddb83,3054642692\">喵啊</d>\r\n    <d p=\"199.3390045166,1,25,16777215,1488262480,0,48dddb83,3054646326\">全程高能</d>\r\n    <d p=\"114.88999938965,1,25,15138834,1488281083,0,93485201,3055334452\">▄█▀█●给跪了</d>\r\n    <d p=\"124.31800079346,5,25,15138834,1488281131,0,93485201,3055336960\">▄█▀█●给跪了        </d>\r\n    <d p=\"110.35800170898,1,25,16777215,1488283259,0,2ed8594b,3055449152\">和up主头像毫无违和感</d>\r\n    <d p=\"44.009998321533,1,25,16737996,1488292587,0,b7963be9,3056023776\">好想对你表白</d>\r\n    <d p=\"104.55999755859,1,25,16737996,1488292647,0,b7963be9,3056027838\">好像对你表白</d>\r\n    <d p=\"143.21299743652,1,25,16737996,1488292905,0,b7963be9,3056045516\">好想跟你表白n</d>\r\n    <d p=\"184.84899902344,1,25,52479,1488292947,0,b7963be9,3056048404\">啦啦啦啦啦</d>\r\n    <d p=\"71.837997436523,1,25,4294967295,1488295313,0,68e6477b,3056207394\">合影</d>\r\n    <d p=\"15.416999816895,1,25,0,1488328319,0,76661dc8,3056763812\">2333333333333合影</d>\r\n    <d p=\"13.8109998703,1,25,6697932,1488344098,0,4d8b689a,3057155022\">高级弹幕合影！！！！！</d>\r\n    <d p=\"17.186000823975,1,25,4294967295,1488344549,0,d63d1d63,3057180356\">百万助攻</d>\r\n    <d p=\"158.12199401855,1,25,16777215,1488345482,0,bdbf05c8,3057219632\">这很酷</d>\r\n    <d p=\"32.445999145508,1,25,16777215,1488345891,0,8678019e,3057237364\">哈哈哈哈哈哈哈哈</d>\r\n    <d p=\"146.72200012207,1,25,16777215,1488360776,0,08616316,3057690044\">2333333333333</d>\r\n    <d p=\"0.88322997093201,1,25,16777215,1488362203,0,660de76b,3057746766\">自己发的</d>\r\n    <d p=\"11.649484634399,1,25,16777215,1488362528,0,660de76b,3057760216\">自己的</d>\r\n    <d p=\"6.2622699737549,1,25,16777215,1488363073,0,660de76b,3057784198\">哈哈22333</d>\r\n    <d p=\"3.7298619747162,1,25,16777215,1488363593,0,660de76b,3057808900\">自己发的</d>\r\n    <d p=\"35.562000274658,1,25,16777215,1488381278,0,88921193,3058845904\">恭喜破百万</d>\r\n    <d p=\"125.16699981689,1,25,16777215,1488381611,0,d3ffab67,3058866328\">高级弹幕</d>\r\n    <d p=\"8.5194444656372,1,25,16777215,1488424156,0,660de76b,3059622476\">自己的</d>\r\n    <d p=\"19.912000656128,1,25,4294967295,1488429727,0,2f6ec8cb,3059804218\">百万助攻</d>\r\n    <d p=\"203.42900085449,1,25,16777215,1488442503,0,8b9601dc,3060240652\">向高级弹幕致敬！！</d>\r\n    <d p=\"0,1,25,16777215,1488442560,0,8b9601dc,3060242318\">投币投币</d>\r\n    <d p=\"3.2055270671844,1,25,16777215,1488442687,0,660de76b,3060246006\">自己的</d>\r\n    <d p=\"108.41000366211,5,25,15138834,1488442740,0,3268a63d,3060249718\">合影</d>\r\n    <d p=\"5.2765312194824,1,25,16777215,1488442829,0,660de76b,3060250332\">自己的额</d>\r\n    <d p=\"183.48300170898,5,25,15138834,1488442896,0,3268a63d,3060252336\">合影乾杯 []~（￣▽￣）~*</d>\r\n    <d p=\"196.81199645996,5,25,15138834,1488442921,0,3268a63d,3060253138\">合影╭( ′• o •′ )╭☞就是这个人！</d>\r\n    <d p=\"0.0060000000521541,1,25,16777215,1488443003,0,660de76b,3060255592\">自己的</d>\r\n    <d p=\"20.482999801636,1,25,16777215,1488443149,0,758369f8,3060259848\">搞基不怕说出口</d>\r\n    <d p=\"3.2774219512939,1,25,16777215,1488443182,0,660de76b,3060260906\">自己的</d>\r\n    <d p=\"72.666999816895,1,25,16777215,1488526320,0,35062e19,3062884748\">司马在掏什么</d>\r\n    <d p=\"80.242553710938,1,25,16777215,1488531740,0,27dc66ea,3063078994\">啊啊啊啊啊啊</d>\r\n    <d p=\"97.254280090332,1,25,16777215,1488531757,0,27dc66ea,3063079638\">asdasd</d>\r\n    <d p=\"18.469999313354,1,25,16777215,1488547895,0,3029e76c,3064069210\">2333</d>\r\n    <d p=\"134.0059967041,1,25,4294967295,1488597698,0,3133bc3e,3065797006\">合影</d>\r\n    <d p=\"76.099998474121,1,25,15138834,1488606660,0,391c1ce0,3066375714\">ffffffffff</d>\r\n    <d p=\"181.78100585938,1,25,6830715,1488630568,0,ae522889,3068026946\">哦哦哦哦</d>\r\n    <d p=\"161.41600036621,1,25,41194,1488630794,0,5ef1d47f,3068045432\">好想跟你表白</d>\r\n    <d p=\"179.04100036621,1,25,15772458,1488630892,0,5ef1d47f,3068047698\">跟你表白~</d>\r\n    <d p=\"160.15699768066,1,25,65280,1488631215,0,ffad461f,3068073928\">fffffffffffffffffffffffffff</d>\r\n    <d p=\"146.28799438477,1,25,4294967295,1488631354,0,6eebcf3a,3068091032\">厉害了</d>\r\n    <d p=\"163.88400268555,1,25,16777215,1488634224,0,cec377b7,3068323702\">尼古拉斯赵四</d>\r\n    <d p=\"69.158996582031,1,25,16777215,1488634323,0,8d1499a9,3068332326\">6</d>\r\n    <d p=\"107.90000152588,1,25,16777215,1488638533,0,68b4beb3,3068706238\">合影</d>\r\n    <d p=\"137.13600158691,1,25,16777215,1488638785,0,0eac15ad,3068722150\">★</d>\r\n    <d p=\"36.861000061035,7,36,0,1488679517,0,f2983ad8,3070008760\">[0,0,\"1-1\",4.5,\"表白王司徒\",0,0,0,0,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"80.587997436523,7,100,13421619,1488679561,0,f2983ad8,3070010988\">[0,0,\"1-1\",4.5,\"天依我的~~~\",0,0,0,0,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"13.458999633789,1,25,16777215,1488680894,0,bf883b27,3070084064\">第十遍</d>\r\n    <d p=\"85.698997497559,1,25,16777215,1488680983,0,bf883b27,3070089386\">给你！硬币都给你！</d>\r\n    <d p=\"212.67999267578,1,25,4294967295,1488682776,0,f10bf224,3070195966\">手机党表示存活</d>\r\n    <d p=\"123.8629989624,1,25,4294967295,1488686974,0,9275f2ae,3070464302\">哇</d>\r\n    <d p=\"107.36699676514,5,25,39423,1488694417,0,9398a3eb,3071009786\">搞基弹幕合影</d>\r\n    <d p=\"25.638000488281,1,25,16777215,1488695619,0,8a2484f9,3071094530\">看到天依就进来了</d>\r\n    <d p=\"167.24479675293,1,25,16777215,1488699108,0,61385f58,3071339180\">打飞机</d>\r\n    <d p=\"62.840000152588,1,25,4294967295,1488699963,0,2ded3de5,3071397802\">💕</d>\r\n    <d p=\"124.06800079346,1,25,4294967295,1488700024,0,2ded3de5,3071401912\">💖</d>\r\n    <d p=\"195.05999755859,1,25,4294967295,1488700088,0,2ded3de5,3071406740\">❤️❤️❤️</d>\r\n    <d p=\"78.607002258301,1,25,16777215,1488707086,0,8347d324,3071869660\">蜜汁逗腿</d>\r\n    <d p=\"108.24299621582,5,25,15138834,1488721825,0,aa7f67f7,3072866334\">又被高级弹幕卡退了        </d>\r\n    <d p=\"199.02099609375,1,25,16777215,1488722970,0,ca341dd0,3072951992\">♥♥♥♥♥♥♥</d>\r\n    <d p=\"70.041999816895,1,25,16777215,1488730594,0,12af9f5c,3073482314\">仿佛给</d>\r\n    <d p=\"147.94299316406,4,25,13421619,1488734405,0,eed50ad0,3073633120\">厉害了</d>\r\n    <d p=\"139.62600708008,1,25,16777215,1488734823,0,07eaedf7,3073644568\">我是诸葛瑾我不知道我弟喜欢王司徒</d>\r\n    <d p=\"71.819000244141,1,25,16777215,1488778714,0,6985ad4d,3074445562\">干唔干无无父亲</d>\r\n    <d p=\"70.681999206543,1,25,16777215,1488803066,0,f4173fa7,3075367370\">晋舞团</d>\r\n    <d p=\"0.89399999380112,5,25,13369344,1488806992,0,21f36494,3075603494\">鬼畜区没有福利，只有比利</d>\r\n    <d p=\"64.275001525879,1,25,16777215,1488807758,0,10e5fbc2,3075650948\">终于又有好玩的了</d>\r\n    <d p=\"177.96099853516,1,25,16777215,1488807900,0,10e5fbc2,3075660016\">我靠，我居然跟着节奏垫起来了</d>\r\n    <d p=\"145.12199401855,1,25,16777215,1488870812,0,4dcef66c,3077293768\">👍👍👍👍👍👍</d>\r\n    <d p=\"48.438999176025,1,25,16777215,1488886623,0,82ad90b0,3077906308\">▄█▀█●</d>\r\n    <d p=\"209.18699645996,1,25,6737151,1488889601,0,35fb34b4,3078089022\">天依蓝</d>\r\n    <d p=\"63.743000030518,1,25,15138834,1488929229,0,51ce0c9c,3079289784\">我是Elva萧亚轩，我已经报警了</d>\r\n    <d p=\"67.46199798584,1,25,16777215,1488951832,0,bc09779d,3079868576\">妙啊</d>\r\n    <d p=\"178.57099914551,1,25,16777215,1488951943,0,bc09779d,3079872748\">厉害了我的哥</d>\r\n    <d p=\"47.794998168945,7,100,0,1488981430,0,6f2fdebd,3081170180\">[0,0,\"1-1\",4.5,\"好想和你表白。。。。。\",0,0,0,0,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"210.77099609375,7,100,16724889,1488982112,0,6f2fdebd,3081216380\">[20,0,\"1-1\",5,\"哈哈哈哈 来上车\",10,5,0,0,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"196.77799987793,7,100,65535,1488982416,0,6f2fdebd,3081237764\">[791,449,\"1-1\",7,\"啦啦啦啦啦啦\",10,5,0,0,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"127.31700134277,7,50,13369344,1488982636,0,6f2fdebd,3081253144\">[763,387,\"0.9-1\",7,\"无♂耻♂老♂贼♂想♂干♂嘛\",10,5,0,0,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"1.7845870256424,1,25,16777215,1489029984,0,660de76b,3082325158\">手动阀</d>\r\n    <d p=\"209.2200012207,1,25,15772458,1489035417,0,ee570db0,3082531174\">。</d>\r\n    <d p=\"132.24800109863,1,25,16777215,1489049045,0,aa7f67f7,3083007488\">巨卡</d>\r\n    <d p=\"37.042999267578,1,25,16777215,1489049820,0,1187a743,3083034562\">6</d>\r\n    <d p=\"50.294998168945,1,25,4294967295,1489078105,0,e519384a,3084607418\">助攻一波</d>\r\n    <d p=\"16.407470703125,1,25,16711680,1489114781,0,660de76b,3085091752\">自己发的弹幕测试</d>\r\n    <d p=\"0.29444500803947,1,25,16711680,1489114863,0,660de76b,3085093886\">测试自己的弹幕</d>\r\n    <d p=\"131.43022155762,1,25,16777215,1489119071,0,b0b03dfa,3085219514\">身体不受控制  2333</d>\r\n    <d p=\"76.008842468262,1,25,16777215,1489141968,0,c6380f72,3086207660\">可以配合UP主头像食用</d>\r\n    <d p=\"201.17700195312,1,25,41194,1489142901,0,9234f701,3086262874\">啦啦啦啦</d>\r\n    <d p=\"88.820999145508,1,25,4294967295,1489176682,0,7d099fa2,3088111400\">晋舞团不要走</d>\r\n    <d p=\"149.85899353027,1,25,14811775,1489209520,0,668645e1,3089202598\">合影</d>\r\n    <d p=\"122.297996521,1,25,16777215,1489210466,0,d85b89fe,3089274252\">高级弹幕叼叼叼😂</d>\r\n    <d p=\"191.14399719238,1,25,16777215,1489210629,0,d85b89fe,3089280762\">好像跟你表白</d>\r\n    <d p=\"7.9340000152588,7,80,13382400,1489214842,0,f2983ad8,3089554978\">[0,0,\"1-1\",10,\"我爱你们~~~\",10,10,0,0,500,0,true,\"楷体\",1]</d>\r\n    <d p=\"72.481002807617,1,25,16777215,1489221397,0,d68e7b1e,3089965188\">表白高级弹幕</d>\r\n    <d p=\"107.54100036621,1,25,16777215,1489221432,0,d68e7b1e,3089967570\">情不自禁的抖腿</d>\r\n    <d p=\"177.1609954834,1,25,16777215,1489221502,0,d68e7b1e,3089972084\">何应加以</d>\r\n    <d p=\"77.005996704102,1,25,16777215,1489246490,0,cb39631f,3091998174\">豌豆射手</d>\r\n    <d p=\"135.96699523926,1,25,16777215,1489246550,0,cb39631f,3092003006\">王者之哇</d>\r\n    <d p=\"208.40699768066,1,25,16777215,1489248419,0,dbfffce7,3092151032\">合影</d>\r\n    <d p=\"51.199001312256,1,25,16777215,1489284979,0,c9b79dc2,3093122456\">合影</d>\r\n    <d p=\"73.083999633789,1,25,16777215,1489303730,0,14f9c65f,3094490776\">我是来看高级弹幕的，你们呢</d>\r\n    <d p=\"127.87699890137,1,25,16777215,1489303790,0,14f9c65f,3094495524\">我去，这弹幕玩的真6啊</d>\r\n    <d p=\"128.72200012207,1,25,16777215,1489305832,0,5ab85a8a,3094639324\">合影</d>\r\n    <d p=\"177.84899902344,1,25,16777215,1489308735,0,252c717e,3094842538\">♥。 ฎญชᅣᄉᄍᄑجئبÌÐММИΝΣЙけぉくき♣</d>\r\n    <d p=\"31.483999252319,1,25,41194,1489310279,0,f10cd448,3094953432\">天依蓝</d>\r\n    <d p=\"130.66206359863,1,25,16777215,1489314188,0,f2ba7dd4,3095207604\">被弹幕卡出去几次</d>\r\n    <d p=\"93.974998474121,1,25,16777215,1489314525,0,f942ab53,3095229662\">高级弹幕666</d>\r\n    <d p=\"75.579002380371,1,25,15138834,1489315405,0,a43a359d,3095288228\">厉害了.....</d>\r\n    <d p=\"84.726997375488,1,25,15138834,1489315430,0,a43a359d,3095289318\">♥</d>\r\n    <d p=\"213.50578308105,1,25,16777215,1489325127,0,91ccfbbf,3095986636\">妙啊</d>\r\n    <d p=\"119.24800109863,1,25,4294967295,1489326210,0,f63ac5e3,3096069452\">高级弹幕怎么来的</d>\r\n    <d p=\"128.17999267578,1,25,11890,1489361850,0,d9edc52b,3097136656\">好带感</d>\r\n    <d p=\"155.40100097656,1,25,16777215,1489403676,0,0cb9ee38,3098505808\">↖↙↗↘</d>\r\n    <d p=\"163.1609954834,1,25,16777215,1489403778,0,0cb9ee38,3098507388\">♥♥♥♥♥♥♥</d>\r\n    <d p=\"109.92900085449,1,25,16777215,1489414576,0,df7f27d4,3099189768\">天天过来治疗颈椎病</d>\r\n    <d p=\"9.9029998779297,1,25,4294967295,1489458648,0,0a83d0c9,3100232960\">手机死机重新进来了</d>\r\n    <d p=\"179.83200073242,1,25,16711935,1489476272,0,be6cd9a2,3100858320\">试色</d>\r\n    <d p=\"27.263999938965,7,36,16711935,1468765926,1,d8678bbf,2102487239\">[259,206,\"1-1\",4.5,\"/n  / \\\\/n  |.|　　/n  |.|/n  |:|　　　__/n,_|:|_,　 /　)/n (Oo　　/ _I_/n  +\\\\ \\\\　||- -|         音（Le）区/n 　 \\\\ \\\\||_ @，             /n　　 \\\\/.:.\\\\-\\\\               速来决战！   /n  　　|.:. /-----\\\\        /n  　　|___|::oOo::|/n  　　/　 |:&lt;_T_&gt;:|/n      \\\"\\\"....\\\"\\\"\",0,0,259,206,500,0,true,\"幼圆\",1]</d>\r\n    <d p=\"44.462001800537,7,5,16711680,1468766443,1,d8678bbf,2102539073\">[259,206,\"1-1\",7,\"      ******       ******/n    **********   **********/n  ************* *************/n  ***************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,</d>\r\n    <d p=\"44.546001434326,7,6,16711680,1468766494,1,d8678bbf,2102544161\">[259,206,\"1-1\",7,\"      ******       ******/n    **********   **********/n  ************* *************/n  ***************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,</d>\r\n    <d p=\"47.507999420166,7,7,16711680,1468766608,1,d8678bbf,2102555545\">[259,206,\"1-1\",7,\"      ******       ******/n    **********   **********/n  ************* *************/n  ***************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,</d>\r\n    <d p=\"44.634998321533,7,10,16711935,1468766859,1,d8678bbf,2102580419\">[125,14,\"1-1\",5,\"/n     .--.  .--./n   ._'   |/   :.-,/n  |  `'.-;-.;`  .'/n  _:._/.'.'.'\\\\.`-./n/    \\\\.'.'.'/    //n'-._.;`'-'-';---'  ,  /&gt;/n     /   /|   \\\\'-.  \\\\\\\\/(/n     `--' `-.-'   `\\\\|_.-'/n                    `\\\\\",0,0,174,555,4000,0,true,\"幼圆\",1]</d>\r\n    <d p=\"44.634998321533,7,10,16711935,1468766880,1,d8678bbf,2102582567\">[352,27,\"1-1\",5,\"/n     .--.  .--./n   ._'   |/   :.-,/n  |  `'.-;-.;`  .'/n  _:._/.'.'.'\\\\.`-./n/    \\\\.'.'.'/    //n'-._.;`'-'-';---'  ,  /&gt;/n     /   /|   \\\\'-.  \\\\\\\\/(/n     `--' `-.-'   `\\\\|_.-'/n                    `\\\\\",0,0,348,550,4000,0,true,\"幼圆\",1]</d>\r\n    <d p=\"44.634998321533,7,10,16711935,1468766893,1,d8678bbf,2102583803\">[563,88,\"1-1\",5,\"/n     .--.  .--./n   ._'   |/   :.-,/n  |  `'.-;-.;`  .'/n  _:._/.'.'.'\\\\.`-./n/    \\\\.'.'.'/    //n'-._.;`'-'-';---'  ,  /&gt;/n     /   /|   \\\\'-.  \\\\\\\\/(/n     `--' `-.-'   `\\\\|_.-'/n                    `\\\\\",0,0,543,534,4000,0,true,\"幼圆\",1]</d>\r\n    <d p=\"44.634998321533,7,10,16711935,1468766906,1,d8678bbf,2102585205\">[750,79,\"1-1\",5,\"/n     .--.  .--./n   ._'   |/   :.-,/n  |  `'.-;-.;`  .'/n  _:._/.'.'.'\\\\.`-./n/    \\\\.'.'.'/    //n'-._.;`'-'-';---'  ,  /&gt;/n     /   /|   \\\\'-.  \\\\\\\\/(/n     `--' `-.-'   `\\\\|_.-'/n                    `\\\\\",0,0,726,553,4000,0,true,\"幼圆\",1]</d>\r\n    <d p=\"44.634998321533,7,10,16711935,1468766921,1,d8678bbf,2102586565\">[26,64,\"1-1\",5,\"/n     .--.  .--./n   ._'   |/   :.-,/n  |  `'.-;-.;`  .'/n  _:._/.'.'.'\\\\.`-./n/    \\\\.'.'.'/    //n'-._.;`'-'-';---'  ,  /&gt;/n     /   /|   \\\\'-.  \\\\\\\\/(/n     `--' `-.-'   `\\\\|_.-'/n                    `\\\\\",0,0,27,560,4000,0,true,\"幼圆\",1]</d>\r\n    <d p=\"44.668998718262,7,10,16711935,1468766970,1,d8678bbf,2102591567\">[293,23,\"1-1\",5,\"/n     .--.  .--./n   ._'   |/   :.-,/n  |  `'.-;-.;`  .'/n  _:._/.'.'.'\\\\.`-./n/    \\\\.'.'.'/    //n'-._.;`'-'-';---'  ,  /&gt;/n     /   /|   \\\\'-.  \\\\\\\\/(/n     `--' `-.-'   `\\\\|_.-'/n                    `\\\\\",0,0,280,546,4000,0,true,\"幼圆\",1]</d>\r\n    <d p=\"44.668998718262,7,10,16711935,1468766997,1,d8678bbf,2102594229\">[497,17,\"1-1\",5,\"/n     .--.  .--./n   ._'   |/   :.-,/n  |  `'.-;-.;`  .'/n  _:._/.'.'.'\\\\.`-./n/    \\\\.'.'.'/    //n'-._.;`'-'-';---'  ,  /&gt;/n     /   /|   \\\\'-.  \\\\\\\\/(/n     `--' `-.-'   `\\\\|_.-'/n                    `\\\\\",0,0,536,563,4000,0,true,\"幼圆\",1]</d>\r\n    <d p=\"180.8990020752,7,20,6737151,1468767420,1,d8678bbf,2102639995\">[3,350,\"1-1\",5,\"啦~啦啦啦~啦啦\",0,0,862,350,1500,0,true,\"幼圆\",1]</d>\r\n    <d p=\"182.23100280762,7,20,6737151,1468767479,1,d8678bbf,2102645717\">[862,350,\"1-1\",5,\"la~lalala~lala\",0,0,3,350,1500,0,true,\"幼圆\",1]</d>\r\n    <d p=\"183.94799804688,7,20,6737151,1468767573,1,d8678bbf,2102655013\">[3,350,\"1-1\",4,\"啦~啦啦~啦啦\",0,0,862,350,2000,0,true,\"幼圆\",1]</d>\r\n    <d p=\"185.8809967041,7,20,6737151,1468767609,1,d8678bbf,2102658725\">[862,350,\"1-1\",4,\"la~lala~lala\",0,0,3,350,2000,0,true,\"幼圆\",1]</d>\r\n    <d p=\"194.8809967041,7,20,6737151,1468767719,1,d8678bbf,2102669545\">[3,350,\"1-1\",4,\"啦~啦啦啦~啦啦~啦\",0,0,862,350,2000,0,true,\"幼圆\",1]</d>\r\n    <d p=\"196.7200012207,7,20,6737151,1468767763,1,d8678bbf,2102673639\">[3,350,\"1-1\",4,\"la~lalalala~la\",0,0,862,350,2000,0,true,\"幼圆\",1]</d>\r\n    <d p=\"196.7200012207,7,20,6737151,1468767791,1,d8678bbf,2102676417\">[862,350,\"1-1\",4,\"la~lalala~la\",0,0,3,350,2000,0,true,\"幼圆\",1]</d>\r\n    <d p=\"44.999000549316,7,10,16711680,1468770983,1,d8678bbf,2102991543\">[0,0,\"1-1\",7,\"/n      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,0,</d>\r\n    <d p=\"45.636001586914,7,10,16711680,1468771008,1,d8678bbf,2102993507\">[0,0,\"1-1\",7,\"/n      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,0,</d>\r\n    <d p=\"45.923000335693,7,10,16711680,1468771051,1,d8678bbf,2102996649\">[0,0,\"1-1\",6,\"/n      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,0,</d>\r\n    <d p=\"11.517999649048,7,20,6737151,1468816123,1,d8678bbf,2104796561\">[3,377,\"1-1\",4,\"好像跟你表白~~\",0,0,862,375,1000,0,true,\"宋体\",1]</d>\r\n    <d p=\"44.685001373291,7,10,16711680,1468817234,1,d8678bbf,2104895313\">[650,50,\"1-1\",4,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,6</d>\r\n    <d p=\"44.685001373291,7,3,16711680,1468817257,1,d8678bbf,2104897411\">[650,50,\"1-1\",4,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,6</d>\r\n    <d p=\"44.60599899292,7,5,16711680,1468817783,1,d8678bbf,2104946259\">[0,0,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,0,</d>\r\n    <d p=\"91.571998596191,7,20,16776960,1468819337,1,d8678bbf,2105090329\">[200,0,\"1-1\",4.5,\"孔\",0,0,200,400,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"91.866996765137,7,20,16776960,1468819378,1,d8678bbf,2105093981\">[200,0,\"1-1\",4.5,\"明\",0,0,200,370,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"92.233001708984,7,20,16776960,1468819395,1,d8678bbf,2105095739\">[200,0,\"1-1\",4.5,\"神\",0,0,200,340,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"92.641998291016,7,20,16776960,1468819418,1,d8678bbf,2105097903\">[200,0,\"1-1\",4.5,\"倒\",0,0,200,280,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"92.980003356934,7,20,16776960,1468819444,1,d8678bbf,2105100277\">[200,0,\"1-1\",4.5,\"悬\",0,0,200,250,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"93.185997009277,7,25,65280,1468819528,1,d8678bbf,2105107933\">[370,270,\"1-1\",4.5,\"天Kuangkuang~~\",0,0,150,100,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"94.014999389648,7,25,65535,1468819612,1,d8678bbf,2105115609\">[370,270,\"1-1\",4.5,\"地KuangKuang~~\",0,0,502,372,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"95.052001953125,7,25,16711935,1468819708,1,d8678bbf,2105124451\">[500,400,\"1-1\",4.5,\"$\",0,0,500,100,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"95.348999023438,7,25,16711935,1468819747,1,d8678bbf,2105128189\">[500,400,\"1-1\",4.5,\"#\",0,0,500,130,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"95.643997192383,7,25,16711935,1468819768,1,d8678bbf,2105130045\">[500,400,\"1-1\",4.5,\"&amp;\",0,0,500,160,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"96.76000213623,7,25,16711935,1468819810,1,d8678bbf,2105133869\">[500,400,\"1-1\",4.5,\"1\",0,0,500,220,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"92.148002624512,7,27,16776960,1468820329,1,d8678bbf,2105181009\">[200,0,\"1-1\",4.3,\"♂\",0,0,197,310,500,0,true,\"楷体\",1]</d>\r\n    <d p=\"94.101997375488,7,27,65535,1468820417,1,d8678bbf,2105188753\">[426,250,\"1-1\",4.3,\"地KuangKuang~~\",0,0,551,348,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"14.130999565125,7,36,16711680,1468888055,1,1be79791,2110012333\">[3,300,\"1-1\",4.5,\"我一直很有自信\",0,0,862,300,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"44.692001342773,7,12,16711680,1468893842,1,d8678bbf,2110275999\">[700,50,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n\",0</d>\r\n    <d p=\"44.692001342773,7,5,16711680,1468893868,1,d8678bbf,2110277373\">[700,50,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n\",0</d>\r\n    <d p=\"100.39499664307,7,45,65280,1468894024,1,f4fc1bfd,2110286361\">[0,250,\"0.4-1\",2.5,\"➷/n  ❤/n    /n\",0,0,330,560,1400,0,true,\"幼圆\",1]</d>\r\n    <d p=\"98.358001708984,7,45,16777215,1468894111,1,f4fc1bfd,2110291647\">[0,250,\"0.4-1\",2.5,\"➷/n  ❤/n    /n\",0,0,330,560,1400,0,true,\"幼圆\",1]</d>\r\n    <d p=\"98.849998474121,7,45,16724838,1468894213,1,f4fc1bfd,2110297555\">[0,250,\"0.4-1\",2.5,\"➷/n  ❤/n    /n\",0,0,330,560,1400,0,true,\"幼圆\",1]</d>\r\n    <d p=\"101.7259979248,7,45,255,1468894262,1,f4fc1bfd,2110300271\">[0,250,\"0.4-1\",2.5,\"➷/n  ❤/n    /n\",0,0,330,560,1400,0,true,\"幼圆\",1]</d>\r\n    <d p=\"102.6070022583,7,45,16737792,1468894285,1,f4fc1bfd,2110301735\">[0,250,\"0.4-1\",2.5,\"➷/n  ❤/n    /n\",0,0,330,560,1400,0,true,\"幼圆\",1]</d>\r\n    <d p=\"103.00700378418,7,45,3381606,1468894301,1,f4fc1bfd,2110302601\">[0,250,\"0.4-1\",2.5,\"➷/n  ❤/n    /n\",0,0,330,560,1400,0,true,\"幼圆\",1]</d>\r\n    <d p=\"103.45899963379,7,45,16777011,1468894314,1,f4fc1bfd,2110303393\">[0,250,\"0.4-1\",2.5,\"➷/n  ❤/n    /n\",0,0,330,560,1400,0,true,\"幼圆\",1]</d>\r\n    <d p=\"101.71800231934,7,45,10066227,1468894423,1,f4fc1bfd,2110309815\">[0,250,\"0.4-1\",2.5,\"➷/n  ❤/n    /n\",0,0,330,560,1400,0,true,\"幼圆\",1]</d>\r\n    <d p=\"100.02700042725,7,45,10027008,1468894542,1,f4fc1bfd,2110316915\">[0,250,\"0.4-1\",2.5,\"➷/n  ❤/n    /n\",0,0,330,560,1400,0,true,\"幼圆\",1]</d>\r\n    <d p=\"99.305999755859,7,44,65535,1468896752,1,f4fc1bfd,2110464355\">[0,250,\"0.4-1\",2,\"➷/n  ❤\",0,0,330,560,1500,0,true,\"黑体\",1]</d>\r\n    <d p=\"44.25899887085,7,12,16711680,1468896763,1,d8678bbf,2110465051\">[302,512,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n/n</d>\r\n    <d p=\"100.93299865723,7,44,0,1468896774,1,f4fc1bfd,2110465889\">[0,250,\"0.4-1\",2,\"➷/n  ❤\",0,0,330,560,1500,0,true,\"黑体\",1]</d>\r\n    <d p=\"44.25899887085,7,5,16711680,1468896787,1,d8678bbf,2110466721\">[302,512,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n/n</d>\r\n    <d p=\"44.049999237061,7,12,16711680,1468897045,1,d8678bbf,2110484883\">[372,185,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n\",</d>\r\n    <d p=\"43.863998413086,7,12,16711680,1468897420,1,d8678bbf,2110511367\">[294,506,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n/n</d>\r\n    <d p=\"44.425998687744,7,12,16711680,1468897835,1,d8678bbf,2110540333\">[323,462,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n/n</d>\r\n    <d p=\"43.96900177002,7,7,16711680,1468897858,1,d8678bbf,2110541997\">[323,462,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n/n</d>\r\n    <d p=\"44.459999084473,7,5,16711680,1468897876,1,d8678bbf,2110543343\">[323,462,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n/n</d>\r\n    <d p=\"44.471000671387,7,5,16711680,1468897894,1,d8678bbf,2110544511\">[323,462,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n/n</d>\r\n    <d p=\"44.32799911499,7,12,16711680,1468898540,1,d8678bbf,2110590377\">[349,496,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n/n</d>\r\n    <d p=\"44.56600189209,7,7,16711680,1468898553,1,d8678bbf,2110591411\">[349,496,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n/n</d>\r\n    <d p=\"44.56600189209,7,5,16711680,1468898569,1,d8678bbf,2110592595\">[349,496,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n/n</d>\r\n    <d p=\"100,7,44,16777215,1468902075,1,f4fc1bfd,2110855291\">[340,560,\"1-0.4\",2,\"  ❤/n➹\",0,0,862,100,1500,0,true,\"黑体\",1]</d>\r\n    <d p=\"101,7,44,26367,1468902223,1,f4fc1bfd,2110866895\">[340,560,\"1-0.4\",2,\"  ❤/n➹\",0,0,862,100,1500,0,true,\"黑体\",1]</d>\r\n    <d p=\"101.61699676514,7,44,16776960,1468902269,1,f4fc1bfd,2110870573\">[340,560,\"1-0.4\",2,\"  ❤/n➹\",0,0,862,100,1500,0,true,\"黑体\",1]</d>\r\n    <d p=\"102.22499847412,7,44,16711680,1468902291,1,f4fc1bfd,2110872277\">[340,560,\"1-0.4\",2,\"  ❤/n➹\",0,0,862,100,1500,0,true,\"黑体\",1]</d>\r\n    <d p=\"102.7320022583,7,44,0,1468902316,1,f4fc1bfd,2110874127\">[340,560,\"1-0.4\",2,\"  ❤/n➹\",0,0,862,100,1500,0,true,\"黑体\",1]</d>\r\n    <d p=\"99.511001586914,7,44,16711680,1468902926,1,f4fc1bfd,2110924481\">[340,560,\"1-0.4\",2,\"  ❤/n➹\",0,0,862,100,1500,0,true,\"黑体\",1]</d>\r\n    <d p=\"100.57499694824,7,44,13395507,1468902973,1,f4fc1bfd,2110928473\">[340,560,\"1-0.4\",2,\"  ❤/n➹\",0,0,862,100,1500,0,true,\"黑体\",1]</d>\r\n    <d p=\"103.27799987793,7,44,6737151,1468903002,1,f4fc1bfd,2110931185\">[340,560,\"1-0.4\",2,\"  ❤/n➹\",0,0,862,100,1500,0,true,\"黑体\",1]</d>\r\n    <d p=\"103.92500305176,7,44,16711935,1468903018,1,f4fc1bfd,2110932503\">[340,560,\"1-0.4\",2,\"  ❤/n➹\",0,0,862,100,1500,0,true,\"黑体\",1]</d>\r\n    <d p=\"104.54599761963,7,44,10079385,1468903052,1,f4fc1bfd,2110935513\">[340,560,\"1-0.4\",2,\"  ❤/n➹\",0,0,862,100,1500,0,true,\"黑体\",1]</d>\r\n    <d p=\"58.736999511719,7,10,16711680,1468911002,1,d8678bbf,2111620903\">[258,250,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n\",</d>\r\n    <d p=\"58.736999511719,7,5,16711680,1468911016,1,d8678bbf,2111622041\">[258,250,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n\",</d>\r\n    <d p=\"58.736999511719,7,1,16711680,1468911031,1,d8678bbf,2111623081\">[258,250,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n\",</d>\r\n    <d p=\"44.974998474121,7,1,16711680,1468911147,1,d8678bbf,2111638769\">[258,250,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n\",</d>\r\n    <d p=\"44.974998474121,7,1,16711680,1468911161,1,d8678bbf,2111639961\">[258,250,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n\",</d>\r\n    <d p=\"44.256000518799,7,12,16711680,1468911194,1,d8678bbf,2111642505\">[258,250,\"1-1\",4.5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               */n\",</d>\r\n    <d p=\"113.23999786377,7,90,6711039,1468929639,1,57b7a86c,2113187185\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"113.65599822998,7,90,16777062,1468929676,1,57b7a86c,2113190579\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"113.32499694824,7,90,16737894,1468929734,1,57b7a86c,2113195575\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"112.85299682617,7,90,10066176,1468929768,1,57b7a86c,2113198517\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"44.09400177002,7,10,16711680,1468929793,1,d8678bbf,2113200727\">[845,377,\"1-0.8\",5,\"/n      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",</d>\r\n    <d p=\"114.15200042725,7,90,13382400,1468929805,1,57b7a86c,2113201781\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"44.09400177002,7,7,16711680,1468929807,1,d8678bbf,2113201875\">[845,377,\"1-0.8\",5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,</d>\r\n    <d p=\"114.5299987793,7,90,65280,1468929842,1,57b7a86c,2113205245\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"44.317001342773,7,1,16711680,1468929867,1,d8678bbf,2113207533\">[845,377,\"1-0.8\",5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,</d>\r\n    <d p=\"44.317001342773,7,1,16711680,1468929883,1,d8678bbf,2113208879\">[845,377,\"1-0.8\",5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,</d>\r\n    <d p=\"114.82499694824,7,90,3355443,1468929912,1,57b7a86c,2113211331\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"115.2379989624,7,90,16777215,1468929953,1,57b7a86c,2113214847\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"58.528999328613,7,5,16711680,1468929954,1,d8678bbf,2113215073\">[845,377,\"1-0.8\",5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,</d>\r\n    <d p=\"58.528999328613,7,5,16711680,1468929971,1,d8678bbf,2113216339\">[845,377,\"1-0.8\",5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    ***********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,</d>\r\n    <d p=\"115.66799926758,7,90,65535,1468929976,1,57b7a86c,2113216849\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"115.97899627686,7,90,16738047,1468930027,1,57b7a86c,2113221663\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"180.60200500488,7,30,6737151,1468932266,1,57b7a86c,2113428525\">[0,0,\"0.3-0.3\",3,\"啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦\",90,30,862,0,3000,0,false,\"微软雅黑\",1]</d>\r\n    <d p=\"211.50900268555,7,30,3394815,1468932770,1,d8678bbf,2113476501\">[20,120,\"1-1\",5,\"/n/n/n   ╒──────╮/n   │  ╳ 音乐区 │ /n   │            │ /n   ╞──────╯/n   │/n   │           /n   │/n  ██/n█████/n\",0,0,20,120,5000,0,true,\"黑体\",1]</d>\r\n    <d p=\"211.50900268555,7,15,16711680,1468932818,1,d8678bbf,2113481175\">[862,370,\"1-1\",5,\"                __\\\\--__|_ /nII=======OOOOO[/ ★01 ___|           /n          _____\\\\______|/-----. /n        /____________________|              /n         \\\\◎◎◎◎◎◎◎◎⊙/ /n           ~~~~~~~~~~~~~~~~ \",0,0,1,370,5000,0,true,\"黑体\",1]</d>\r\n    <d p=\"211.50900268555,7,12,65280,1468932887,1,d8678bbf,2113487919\">[862,250,\"1-1\",5,\"                __\\\\--__|_ /nII=======OOOOO[/ ★02__|           /n          _____\\\\______|/-----. /n        /____________________|              /n         \\\\◎◎◎◎◎◎◎◎⊙/ /n           ~~~~~~~~~~~~~~~~ \",0,0,1,250,5000,0,true,\"黑体\",1]</d>\r\n    <d p=\"211.50900268555,7,12,65535,1468932942,1,d8678bbf,2113493301\">[862,150,\"1-1\",5,\"                __\\\\--__|_ /nII=======OOOOO[/ ★01 ___|           /n          _____\\\\______|/-----. /n        /____________________|              /n         \\\\◎◎◎◎◎◎◎◎⊙/ /n           ~~~~~~~~~~~~~~~~ /n同志们，冲啊！\",0,0,1,150,5000,0,true,\"黑体\",1]</d>\r\n    <d p=\"180.33599853516,7,30,6737151,1468933199,1,57b7a86c,2113518027\">[0,0,\"0.2-0.2\",3,\"啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦\",90,30,862,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"211.98199462891,7,15,6750207,1468933234,1,d8678bbf,2113521407\">[862,300,\"1-1\",5,\"                   ▍  ∴/n　　．．．．▍▍．..█▍    ∵ ..../ /n　　◥█▅▅██▅▅██▅▅▅▅▅███◤ /n 　 ．◥███████████████◤/n～～～～◥█████████████◤～～～～～/n～～～～～～～～～～～～～～～～～～～～～～～/n                海军已到位！/n\",0,0,1,300,6000,0,true,\"宋体\",1]</d>\r\n    <d p=\"112.71499633789,7,90,16777215,1468935289,1,82a989f2,2113721409\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"116.98100280762,7,90,16711680,1468935325,1,82a989f2,2113724901\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"116.6309967041,7,90,16776960,1468935360,1,82a989f2,2113728231\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"112.60600280762,7,90,16751001,1468936015,1,82a989f2,2113792743\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"117.43000030518,7,90,39423,1468936073,1,82a989f2,2113798195\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"118.01000213623,7,90,39168,1468936098,1,82a989f2,2113800645\">[862,0,\"0.2-0.2\",3,\"██████████████████████████████████████████\",90,40,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"67.148002624512,7,50,16776960,1468938471,1,d8678bbf,2114031265\">[0,0,\"0.1-0.1\",4.5,\"█████████████████████████████████████████████████████████████████████████████████\",3,15,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"67.148002624512,7,50,255,1468938484,1,d8678bbf,2114032527\">[0,0,\"0.1-0.1\",4.5,\"█████████████████████████████████████████████████████████████████████████████████\",3,15,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"67.606002807617,7,50,16711935,1468938658,1,d8678bbf,2114049329\">[0,0,\"0.1-0.1\",4.4,\"█████████████████████████████████████████████████████████████████████████████████\",3,15,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"67.438003540039,7,50,65535,1468938675,1,d8678bbf,2114050943\">[0,0,\"0.1-0.1\",4.4,\"█████████████████████████████████████████████████████████████████████████████████\",3,15,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"67.918998718262,7,50,16711680,1468938708,1,d8678bbf,2114054891\">[0,0,\"0.1-0.1\",4.4,\"█████████████████████████████████████████████████████████████████████████████████\",3,15,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"68.291999816895,7,50,16750848,1468938724,1,d8678bbf,2114056455\">[0,0,\"0.1-0.1\",4.4,\"█████████████████████████████████████████████████████████████████████████████████\",3,15,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"68.721000671387,7,50,16776960,1468938737,1,d8678bbf,2114057625\">[0,0,\"0.1-0.1\",4.4,\"█████████████████████████████████████████████████████████████████████████████████\",3,15,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"69.080001831055,7,50,255,1468938750,1,d8678bbf,2114058955\">[0,0,\"0.1-0.1\",4.4,\"█████████████████████████████████████████████████████████████████████████████████\",3,15,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"69.004997253418,7,50,0,1468938795,1,d8678bbf,2114063105\">[0,0,\"0.1-0.1\",4.4,\"█████████████████████████████████████████████████████████████████████████████████\",3,15,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"66.644996643066,7,36,16711833,1468939495,1,d8678bbf,2114127921\">[0,0,\"0.2-0.1\",4.5,\"█████████████████████████████████████████████████████████\",5,15,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"66.870002746582,7,36,65535,1468939515,1,d8678bbf,2114129877\">[0,0,\"0.2-0.1\",4.5,\"█████████████████████████████████████████████████████████████████████████\",5,15,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"67.399002075195,7,36,6697881,1468939536,1,d8678bbf,2114131725\">[0,0,\"0.2-0.1\",4.5,\"█████████████████████████████████████████████████████████████████████████\",5,15,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"67.857002258301,7,36,16711680,1468939561,1,d8678bbf,2114133905\">[0,0,\"0.2-0.1\",4.5,\"█████████████████████████████████████████████████████████████████████████\",5,15,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"67.888999938965,7,36,13395558,1468939575,1,d8678bbf,2114135107\">[0,0,\"0.2-0.1\",4.5,\"█████████████████████████████████████████████████████████████████████████\",5,15,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"180.53500366211,7,30,6737151,1468969162,1,57b7a86c,2115124631\">[862,0,\"0.2-0.2\",3,\"啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦\",90,30,0,0,3000,0,false,\"微软雅黑\",1]</d>\r\n    <d p=\"180.8159942627,7,30,6737151,1468972124,1,57b7a86c,2115170355\">[862,0,\"0.2-0.2\",3,\"啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦\",90,30,0,0,3000,0,false,\"黑体\",1]</d>\r\n    <d p=\"212.72700500488,7,20,13421772,1468985746,1,d8678bbf,2115852757\">[858,70,\"1-1\",4,\"/n══════∩═════  /n　　　　　╭╳╮　　　　　　　　　 ◢  /n   ╭▅▆▇□□█▇▆▅▄▃▂▁(╳)█╮  /n　 ╰═▃__▁▁▁▁▁∠════▔▔▔╯　  /n　　　　╙O 　 ╙O/n   空军已部署！\",0,0,3,70,6000,0,true,\"黑体\",1]</d>\r\n    <d p=\"127,7,100,16777215,1468994247,1,57b7a86c,2116582599\">[0,10,\"0.9-0.9\",10,\"✈\",0,0,550,10,10000,0,true,\"黑体\",1]</d>\r\n    <d p=\"136,7,36,16724736,1468997232,1,82a989f2,2116824855\">[80,568,\"0.9-0.9\",0.9,\"➹\",0,0,600,20,900,0,true,\"黑体\",1]</d>\r\n    <d p=\"137,7,20,16724736,1468998201,1,82a989f2,2116904341\">[475,0,\"0.9-0.9\",1,\"     .--.  .--./n   ._'   |/   :.-,/n  |  `'.-;-.;`  .'/n  _:._/.（）'\\\\.`-./n /    \\\\ BOOM！！ //n '-._.;`'-'-';---'  /n     /   /|   \\\\/n     `--' `-.-'   \",0,0,475,0,1000,0,true,\"黑体\",1]</d>\r\n    <d p=\"137,7,40,16750848,1468999107,1,82a989f2,2116975625\">[0,0,\"0.9-0\",5,\"☆\",0,0,0,0,2000,0,true,\"黑体\",1,\"M631,59L633,59L635,58L637,58L639,57L641,56L642,56L643,55L644,55L646,54L647,54L648,54L649,54L650,53L652,53L653,52L654,52L656,52L657,52L658,52L659,52L660,52L661,52L662,52L663,52L664,52L665,52L666,52L667,52L668,52L670,52L671,52L672,52L674,52L675,52L676,52L</d>\r\n    <d p=\"137,7,36,16777062,1468999243,1,82a989f2,2116986675\">[0,0,\"0.9-0\",2,\"☆\",0,0,0,0,2000,0,true,\"黑体\",1,\"M484,43L483,42L482,42L482,41L481,39L479,39L478,38L477,37L476,37L475,35L474,35L473,34L472,34L471,33L469,31L466,31L464,30L462,30L459,29L457,28L455,27L452,26L448,25L446,24L443,22L439,21L433,20L431,20L428,19L425,17L424,17L420,16L419,16L417,16L416,16L415,16L</d>\r\n    <d p=\"137,7,36,65535,1468999594,1,82a989f2,2117014309\">[0,0,\"0.9-0\",2,\"☆\",0,0,0,0,2000,0,true,\"黑体\",1,\"M608,186L608,187L608,189L608,190L608,192L608,193L608,194L608,196L608,197L608,198L608,199L608,201L608,202L608,203L608,204L608,205L608,207L608,208L608,209L608,211L608,212L608,213L608,214L608,215L608,216L608,218L608,219L608,220L608,222L608,223L609,225L609,</d>\r\n    <d p=\"137,7,36,255,1468999702,1,82a989f2,2117022951\">[0,0,\"0.9-0\",2,\"☆\",0,0,0,0,2000,0,true,\"黑体\",1,\"M456,48L453,47L450,46L448,46L446,45L445,45L444,45L444,44L442,43L441,43L440,42L438,42L436,41L434,40L432,39L431,39L430,38L429,38L428,38L426,38L425,38L425,37L424,37L422,37L421,37L420,37L419,37L418,37L417,37L415,37L414,37L413,37L411,37L410,37L409,37L409,36L</d>\r\n    <d p=\"137,7,36,65280,1468999842,1,82a989f2,2117034057\">[0,0,\"0.9-0\",2,\"☆\",0,0,0,0,2000,0,true,\"黑体\",1,\"M631,29L632,29L633,29L634,28L635,27L636,26L638,25L639,24L640,24L641,24L642,24L643,22L644,21L645,21L647,20L649,18L650,17L652,17L653,16L654,16L655,14L656,14L657,14L659,14L661,14L661,13L664,12L666,12L668,12L670,12L673,10L676,9L677,9L679,9L680,9L681,8L682,8</d>\r\n    <d p=\"137,7,36,16777215,1469000119,1,82a989f2,2117055571\">[0,0,\"0.9-0\",2,\"☆\",0,0,0,0,2000,0,true,\"黑体\",1,\"M602,102L602,103L603,104L604,106L604,107L605,108L605,109L605,110L605,111L607,113L608,114L608,115L608,116L609,117L610,120L610,121L610,122L610,124L610,126L611,126L611,127L611,129L612,132L612,133L613,136L614,138L614,139L614,141L614,144L615,146L615,147L615,</d>\r\n    <d p=\"137,7,36,3407820,1469000174,1,82a989f2,2117059741\">[0,0,\"0.9-0\",2,\"☆\",0,0,0,0,2000,0,true,\"黑体\",1,\"M498,28L496,27L495,27L493,26L492,26L491,26L489,25L488,25L485,24L482,24L480,23L478,23L474,22L471,20L470,20L468,20L465,20L464,19L460,19L457,19L456,18L452,18L450,17L448,17L446,17L443,17L441,16L438,16L434,15L430,15L426,15L423,14L419,13L416,13L413,13L408,11L</d>\r\n    <d p=\"11.420000076294,7,80,16711680,1469000603,1,82a989f2,2117093327\">[0,0,\"0.9-0\",1.5,\"3\",0,0,0,0,1500,0,true,\"微软雅黑\",1,\"M29,410L29,409L30,408L30,407L30,406L30,405L31,404L32,403L32,402L32,401L32,400L33,400L33,399L34,398L35,397L35,396L35,395L36,394L36,393L37,391L38,390L40,388L40,387L40,386L40,385L41,383L42,381L43,380L43,379L44,378L45,377L45,376L46,375L46,374L47,373L47,</d>\r\n    <d p=\"11.899000167847,7,80,16711680,1469000632,1,82a989f2,2117095601\">[0,0,\"0.9-0\",1.5,\"2\",0,0,0,0,1500,0,true,\"微软雅黑\",1,\"M29,410L29,409L30,408L30,407L30,406L30,405L31,404L32,403L32,402L32,401L32,400L33,400L33,399L34,398L35,397L35,396L35,395L36,394L36,393L37,391L38,390L40,388L40,387L40,386L40,385L41,383L42,381L43,380L43,379L44,378L45,377L45,376L46,375L46,374L47,373L47,</d>\r\n    <d p=\"12.437000274658,7,80,16711680,1469000650,1,82a989f2,2117096991\">[0,0,\"0.9-0\",1.5,\"1\",0,0,0,0,1500,0,true,\"微软雅黑\",1,\"M29,410L29,409L30,408L30,407L30,406L30,405L31,404L32,403L32,402L32,401L32,400L33,400L33,399L34,398L35,397L35,396L35,395L36,394L36,393L37,391L38,390L40,388L40,387L40,386L40,385L41,383L42,381L43,380L43,379L44,378L45,377L45,376L46,375L46,374L47,373L47,</d>\r\n    <d p=\"12.887999534607,7,80,16711680,1469000684,1,82a989f2,2117099719\">[0,0,\"0.9-0\",1.5,\"GO!!\",0,0,0,0,1500,0,true,\"微软雅黑\",1,\"M29,410L29,409L30,408L30,407L30,406L30,405L31,404L32,403L32,402L32,401L32,400L33,400L33,399L34,398L35,397L35,396L35,395L36,394L36,393L37,391L38,390L40,388L40,387L40,386L40,385L41,383L42,381L43,380L43,379L44,378L45,377L45,376L46,375L46,374L47,373L</d>\r\n    <d p=\"137,7,50,16777215,1469007926,1,82a989f2,2117685549\">[476,95,\"1-0\",2,\"☆\",0,0,28,147,1500,0,true,\"黑体\",0]</d>\r\n    <d p=\"137,7,50,16711680,1469008003,1,82a989f2,2117691895\">[498,139,\"1-0\",2,\"★\",0,0,9,420,1500,0,true,\"黑体\",0]</d>\r\n    <d p=\"137,7,50,65280,1469008063,1,82a989f2,2117696881\">[538,137,\"1-0\",2,\"☆\",0,0,282,539,1500,0,true,\"黑体\",0]</d>\r\n    <d p=\"137,7,50,16776960,1469008102,1,82a989f2,2117700249\">[581,132,\"1-0\",2,\"★\",0,0,636,553,1500,0,true,\"黑体\",0]</d>\r\n    <d p=\"137,7,50,255,1469008124,1,82a989f2,2117702139\">[581,132,\"1-0\",2,\"☆\",0,0,857,493,1500,0,true,\"黑体\",0]</d>\r\n    <d p=\"137,7,50,65535,1469008164,1,82a989f2,2117705469\">[467,56,\"1-0\",2,\"★\",0,0,31,49,1500,0,true,\"黑体\",0]</d>\r\n    <d p=\"137,7,50,16711935,1469008273,1,82a989f2,2117714773\">[636,75,\"1-0\",2,\"☆\",0,0,862,80,1500,0,true,\"黑体\",0]</d>\r\n    <d p=\"137,7,50,13434675,1469008334,1,82a989f2,2117719917\">[475,140,\"1-0\",2,\"☆\",0,0,162,472,1500,0,true,\"黑体\",0]</d>\r\n    <d p=\"137,7,50,16750848,1469008380,1,82a989f2,2117724365\">[606,129,\"1-0\",2,\"★\",0,0,862,347,1500,0,true,\"黑体\",0]</d>\r\n    <d p=\"137,7,50,16777113,1469008462,1,82a989f2,2117731279\">[459,63,\"1-0\",2,\"☆\",0,0,80,0,1500,0,true,\"黑体\",0]</d>\r\n    <d p=\"137,7,50,6710988,1469008506,1,82a989f2,2117734999\">[626,50,\"1-0\",2,\"★\",0,0,862,3,1500,0,true,\"黑体\",0]</d>\r\n    <d p=\"137,7,50,16724940,1469008702,1,82a989f2,2117763687\">[550,156,\"1-0\",2,\"★\",0,0,478,537,1500,0,true,\"黑体\",0]</d>\r\n    <d p=\"137,7,50,0,1469008764,1,82a989f2,2117768749\">[462,117,\"1-0\",2,\"★\",0,0,1,300,1500,0,true,\"黑体\",0]</d>\r\n    <d p=\"5.84600067139,7,255,16711680,1469016459,1,d8678bbf,2118441525\">[0,0,\"0.2-0.1\",4,\"████████████████████████████████████████████████████\",10,22,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"5.87400054932,7,255,65280,1469016478,1,d8678bbf,2118443145\">[0,0,\"0.2-0.1\",4,\"████████████████████████████████████████████████████\",10,22,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"5.19100189209,7,255,255,1469016490,1,d8678bbf,2118444201\">[0,0,\"0.2-0.1\",4,\"████████████████████████████████████████████████████\",10,22,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"5.23500061035,7,255,65535,1469016518,1,d8678bbf,2118446747\">[0,0,\"0.2-0.1\",4,\"████████████████████████████████████████████████████\",10,22,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"5.85600280762,7,255,16711935,1469016551,1,d8678bbf,2118449571\">[0,0,\"0.2-0.1\",4,\"████████████████████████████████████████████████████\",10,22,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"5.78199768066,7,255,16764057,1469016598,1,d8678bbf,2118453813\">[0,0,\"0.2-0.1\",4,\"████████████████████████████████████████████████████\",10,22,0,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"107.38600158691,7,46,16711680,1469164743,1,63e36120,2127119691\">[0,350,\"1-0\",1,\"——哇\",0,0,670,350,100,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"121.46499633789,7,46,16711680,1469164846,1,63e36120,2127129399\">[670,350,\"1-0\",1,\"哇——\",0,0,0,350,100,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"160.67599487305,7,46,65280,1469164898,1,63e36120,2127134183\">[670,350,\"1-0\",1,\"哇——\",0,0,0,350,100,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"216.55000305176,7,20,16711680,1469184770,1,d8678bbf,2128800551\">[1,251,\"1-1\",2,\"     .--.  .--./n   ._'   |/   :.-,/n  |  `'.-;-.;`  .'/n  _:._/.轰！！\\\\.`-./n /    \\\\ BOOM /    //n '-._.;`'-'-';---'   /n     /   /|   \\\\/n     `--' `-.-' \",0,0,1,251,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"217.54299926758,7,20,16711680,1469184813,1,d8678bbf,2128803895\">[227,227,\"1-1\",2,\"/n.\\\"\\\".    .\\\"\\\",/n|  |   /  //n|  |  /  //n|  | /  //n|  |/  ;-._ /n}  ` _/  / ;/n|  /` ) /  //n| /  /_/\\\\_/\\\\/n|/  /      |/n(  ' \\\\ '-  |/n \\\\    `.  //n  |      |/n  |      |    秒杀！\",0,0,227,227,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"11.421999931335,7,150,16711680,1469268858,1,57b7a86c,2133798181\">[69,312,\"0.8-0\",1.8,\"③\",0,0,143,84,1000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"11.843999862671,7,150,16711680,1469268881,1,57b7a86c,2133799899\">[69,312,\"0.8-0\",1.8,\"②\",0,0,143,84,1000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"12.352000236511,7,150,16711680,1469268905,1,57b7a86c,2133801669\">[69,312,\"0.8-0\",1.8,\"①\",0,0,143,84,1000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"12.746999740601,7,75,65280,1469269222,1,57b7a86c,2133826281\">[69,381,\"0.8-0\",1.8,\"Gò !!!\",0,0,143,84,1000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"97.585998535156,7,36,16776960,1469329662,1,1e5f6ada,2137046129\">[435,279,\"1-1\",4.5,\"兲\",0,0,0,0,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"97.585998535156,7,36,16776960,1469329680,1,1e5f6ada,2137047471\">[435,279,\"1-1\",4.5,\"兲\",0,0,850,13,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"97.585998535156,7,36,16776960,1469329693,1,1e5f6ada,2137048331\">[435,279,\"1-1\",4.5,\"兲\",0,0,10,557,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"97.585998535156,7,36,16776960,1469329705,1,1e5f6ada,2137049223\">[435,279,\"1-1\",4.5,\"兲\",0,0,855,558,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"44.654998779297,7,10,16711680,1469950706,1,d8678bbf,2179850843\">[0,0,\"1-1\",5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    *********************** /n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,0,0</d>\r\n    <d p=\"44.654998779297,7,3,16711680,1469950725,1,d8678bbf,2179852687\">[0,0,\"1-1\",6,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    *********************** /n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,0,0</d>\r\n    <d p=\"44.654998779297,7,1,16711680,1469950764,1,d8678bbf,2179856217\">[0,0,\"1-1\",7,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    *********************** /n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,0,0</d>\r\n    <d p=\"44.654998779297,7,1,16711680,1469950804,1,d8678bbf,2179859827\">[0,0,\"1-1\",10,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    *********************** /n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,0,</d>\r\n    <d p=\"44.654998779297,7,1,16711680,1469950819,1,d8678bbf,2179861165\">[0,0,\"1-1\",5,\"      ******       ******/n    **********   **********/n   ************ ************/n   *************************/n    *********************** /n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,0,0</d>\r\n    <d p=\"211.83200073242,7,15,10027263,1470538540,1,246eeaf2,2220429491\">[500,260,\"1-0\",4,\"/n  ,--.      &lt;__)/n  `- |________7/n     |`.      |\\\\/n  .--|. \\\\     |.\\\\--./n /   j \\\\ `.7__j__\\\\  \\\\/n|   o   | (o)____O)  |/n \\\\     /   J  \\\\     //n  `---'        `---'  来！上车！\",0,0,200,260,4000,0,false,\"黑体\",1]</d>\r\n    <d p=\"180,7,80,39423,1470714455,1,246eeaf2,2232357639\">[0,0,\"0.2-0.2\",4,\"▉▉▉▉▉▉▉▉▉▉\",90,30,862,0,2000,0,false,\"幼圆\",1]</d>\r\n    <d p=\"180,7,80,39423,1470714466,1,246eeaf2,2232358605\">[0,0,\"0.2-0.2\",4,\"▉▉▉▉▉▉▉▉▉▉\",90,30,862,0,2000,300,false,\"幼圆\",1]</d>\r\n    <d p=\"180,7,80,39423,1470714480,1,246eeaf2,2232359641\">[0,0,\"0.2-0.2\",4,\"▉▉▉▉▉▉▉▉▉▉\",90,30,862,0,2000,800,false,\"幼圆\",1]</d>\r\n    <d p=\"180,7,80,39423,1470714493,1,246eeaf2,2232360719\">[0,0,\"0.2-0.2\",4,\"▉▉▉▉▉▉▉▉▉▉\",90,30,862,0,2000,1400,false,\"幼圆\",1]</d>\r\n    <d p=\"180,7,80,39423,1470714506,1,246eeaf2,2232361739\">[0,0,\"0.2-0.2\",4,\"▉▉▉▉▉▉▉▉▉▉\",90,30,862,0,2000,2000,false,\"幼圆\",1]</d>\r\n    <d p=\"194.53100585938,7,35,39423,1470984452,1,246eeaf2,2252752025\">[450,0,\"0.5-0.5\",7,\" 啦啦啦啦啦啦啦啦啦啦啦啦啦啦\",90,0,50,0,6000,0,false,\"黑体\",1]</d>\r\n    <d p=\"194.53100585938,7,35,39423,1470984491,1,246eeaf2,2252755547\">[850,0,\"0.5-0.5\",7,\" 啦啦啦啦啦啦啦啦啦啦啦啦啦啦\",90,0,450,0,6000,0,false,\"黑体\",1]</d>\r\n    <d p=\"194.53100585938,7,35,39423,1470984541,1,246eeaf2,2252760009\">[50,0,\"0.5-0.5\",7,\" 啦啦啦啦啦啦啦啦啦啦啦啦啦啦\",90,0,450,0,6000,0,false,\"黑体\",1]</d>\r\n    <d p=\"49.24100112915,7,11,16711680,1471445637,1,d8678bbf,2283493983\">[390,200,\"1-0.5\",3,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,390,200,3000,0,false,\"宋体\",1]</d>\r\n    <d p=\"47.359001159668,7,80,65535,1473471852,1,d89ca222,2390193031\">[250,269,\"1-1\",4.5,\"❀\",0,0,0,0,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"61.131999969482,7,80,65535,1473471868,1,d89ca222,2390193607\">[250,269,\"1-1\",4.5,\"❤\",0,0,0,0,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"44.860000610352,7,8,16711680,1473841457,1,d8678bbf,2404416945\">[420,250,\"0.45-1\",7,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,420,250,600,0,true,\"宋体\",1]</d>\r\n    <d p=\"44.860000610352,7,5,16711680,1473841503,1,d8678bbf,2404418717\">[0,0,\"1-0.5\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,420,250,600,0,true,\"宋体\",1]</d>\r\n    <d p=\"44.860000610352,7,5,16711680,1473841514,1,d8678bbf,2404419165\">[201,2,\"1-0.5\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,420,250,600,0,true,\"宋体\",1]</d>\r\n    <d p=\"44.860000610352,7,5,16711680,1473841531,1,d8678bbf,2404419835\">[802,1,\"1-0.5\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,420,250,600,0,true,\"宋体\",1]</d>\r\n    <d p=\"44.860000610352,7,5,16711680,1473841552,1,d8678bbf,2404420621\">[851,551,\"1-0.5\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,420,250,600,0,true,\"宋体\",1]</d>\r\n    <d p=\"44.860000610352,7,5,16711680,1473841565,1,d8678bbf,2404421121\">[603,546,\"1-0.5\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,420,250,600,0,true,\"宋体\",1]</d>\r\n    <d p=\"44.860000610352,7,5,16711680,1473841581,1,d8678bbf,2404421555\">[251,566,\"1-0.5\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,420,250,600,0,true,\"宋体\",1]</d>\r\n    <d p=\"44.860000610352,7,5,16711680,1473841594,1,d8678bbf,2404422255\">[2,350,\"1-0.5\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *\",0,0,420,250,600,0,true,\"宋体\",1]</d>\r\n    <d p=\"64.057998657227,7,80,10027263,1473862754,1,d89ca222,2405808699\">[117,259,\"1-1\",4.5,\"❀\",0,0,117,259,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"47.060001373291,7,50,16711680,1473937781,1,d89ca222,2409528721\">[129,282,\"1-1\",4.5,\"❤\",0,0,129,282,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"60.111000061035,7,100,3407871,1474005633,1,d89ca222,2413042127\">[654,327,\"1-1\",4.5,\"❀\",0,0,654,327,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"3.066999912262,7,17,16711680,1474478010,1,d8678bbf,2431129215\">[550,120,\"1-1\",4.5,\"●█▀█▄\",0,0,550,120,500,0,true,\"楷体\",1]</d>\r\n    <d p=\"3.6170001029968,7,6,65280,1474478396,1,d8678bbf,2431138187\">[420,120,\"1-1\",4.5,\"    ┃　　专宠　　　┣┓ /n    ┃　　　　　　　┏┛ /n    ┗┓┓┏━┳┓┏┛ /n      ┃┫┫　┃┫┫ /n      ┗┻┛　┗┻┛ \",0,0,420,120,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"52.526000976562,7,7,16711680,1474478728,1,d8678bbf,2431145665\">[0,0,\"1-1\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *   \",0,0,150,25,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"52.442001342773,7,7,16711680,1474478817,1,d8678bbf,2431147681\">[150,25,\"1-1\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *   \",0,0,25,150,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"52.442001342773,7,7,16711680,1474478854,1,d8678bbf,2431148409\">[25,150,\"1-1\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *   \",0,0,150,25,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"52.442001342773,7,7,16711680,1474478872,1,d8678bbf,2431148745\">[150,25,\"1-1\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *   \",0,0,0,0,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"54.115001678467,7,7,16711680,1474479095,1,d8678bbf,2431153555\">[862,0,\"1-1\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *   \",0,0,500,25,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"54.115001678467,7,7,16711680,1474479158,1,d8678bbf,2431154893\">[500,25,\"1-1\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *   \",0,0,600,150,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"54.115001678467,7,7,16711680,1474479174,1,d8678bbf,2431155243\">[600,150,\"1-1\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *   \",0,0,500,25,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"55.111000061035,7,7,16711680,1474479297,1,d8678bbf,2431157821\">[0,568,\"1-1\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *   \",0,0,200,400,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"55.111000061035,7,7,16711680,1474479377,1,d8678bbf,2431159259\">[200,400,\"1-1\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *   \",0,0,50,300,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"55.111000061035,7,7,16711680,1474479401,1,d8678bbf,2431159941\">[50,300,\"1-1\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *   \",0,0,200,400,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"56.113998413086,7,7,16711680,1474479513,1,d8678bbf,2431162069\">[862,568,\"1-1\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *   \",0,0,600,400,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"56.113998413086,7,7,16711680,1474479542,1,d8678bbf,2431162725\">[600,400,\"1-1\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *   \",0,0,500,550,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"56.113998413086,7,7,16711680,1474479562,1,d8678bbf,2431163109\">[500,550,\"1-1\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *   \",0,0,600,400,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"56.113998413086,7,7,16711680,1474479573,1,d8678bbf,2431163331\">[600,400,\"1-1\",4.5,\"      ******       ******/n     *********   *********/n     *********************/n      *******************/n        ***************/n          ***********/n            *******/n              ***/n               *   \",0,0,862,568,500,0,true,\"宋体\",1]</d>\r\n    <d p=\"147.39100646973,7,40,52479,1474595915,1,246eeaf2,2433989875\">[36,43,\"1-0\",5,\"♥/n  ♥/n♥\",0,0,813,521,5000,0,false,\"微软雅黑\",1]</d>\r\n    <d p=\"105.36000061035,7,40,6710886,1475370588,1,d89ca222,2481225339\">[78,324,\"1-1\",4.5,\"❤\",0,0,214,278,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"105.36000061035,7,50,6710886,1475370623,1,d89ca222,2481226433\">[171,317,\"1-1\",4.5,\"❤\",0,0,344,199,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"105.36000061035,7,60,6710886,1475370641,1,d89ca222,2481229627\">[330,244,\"1-1\",4.5,\"❤\",0,0,460,191,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"105.36000061035,7,70,6710886,1475370667,1,d89ca222,2481230795\">[443,241,\"1-1\",4.5,\"❤\",0,0,577,305,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"105.36000061035,7,50,6710886,1475370692,1,d89ca222,2481231849\">[585,315,\"1-1\",4.5,\"❤\",0,0,680,476,500,0,true,\"黑体\",1]</d>\r\n    <d p=\"180.93099975586,7,255,6737151,1477039108,1,d8678bbf,2576919223\">[0,0,\"0.3-0.3\",7,\"啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦\",1,20,0,568,2000,0,true,\"黑体\",1]</d>\r\n    <d p=\"180.93099975586,7,255,6737151,1477039132,1,d8678bbf,2576920285\">[0,0,\"0.3-0.3\",7,\"啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦\",20,1,0,568,4000,0,true,\"黑体\",1]</d>\r\n    <d p=\"180.93099975586,7,255,6737151,1477039144,1,d8678bbf,2576920815\">[0,568,\"0.3-0.3\",7,\"啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦\",20,1,0,0,4000,0,true,\"黑体\",1]</d>\r\n    <d p=\"181.0659942627,7,255,6737151,1477039634,1,d8678bbf,2576942583\">[862,0,\"0.3-0.3\",7,\"██████████████████████████████████████████\",120,35,0,0,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"181.0659942627,7,255,255,1477039645,1,d8678bbf,2576943179\">[0,0,\"0.3-0.3\",7,\"██████████████████████████████████████████\",120,35,862,0,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"181.0659942627,7,255,255,1477039712,1,d8678bbf,2576946371\">[862,0,\"0.3-0.3\",7,\"██████████████████████████████████████████\",90,33,0,0,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"194.65299987793,7,50,6737151,1477286817,1,d8678bbf,2587835211\">[0,0,\"0.4-0.4\",4,\"啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦/n\",10,25,862,0,4000,0,true,\"黑体\",1]</d>\r\n    <d p=\"194.77499389648,7,50,6737151,1477286879,1,d8678bbf,2587837843\">[0,0,\"0.4-0.4\",4,\"啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦/n啦\",10,25,862,0,4000,0,true,\"黑体\",1]</d>\r\n    <d p=\"183.17100524902,7,50,16711680,1480137293,1,974a9a76,2694876675\">[212,256,\"1-1\",4.5,\"/n︿(￣︶￣)︿︿(￣︶￣)︿\",1,1,0,0,1000,1000,true,\"宋体\",1]</d>\r\n    <d p=\"181.11799621582,7,255,6737151,1480671742,1,d8678bbf,2711888403\">[0,0,\"0.5-0.5\",3,\"████████████████████████████████████████████████████████████\",0,25,0,561,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"194.83500671387,7,255,255,1480671929,1,d8678bbf,2711897395\">[862,568,\"0.5-0.5\",3,\"████████████████████████████████████████████████████████\",0,38,0,0,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"194.70799255371,7,255,255,1480671980,1,d8678bbf,2711899023\">[862,568,\"0.5-0.5\",3,\"████████████████████████████████████████████████████████\",0,38,0,0,2500,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"194.70799255371,7,255,6737151,1480672005,1,d8678bbf,2711900085\">[0,0,\"0.5-0.5\",3,\"████████████████████████████████████████████████████████\",0,38,862,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"194.70799255371,7,255,6737151,1480672007,1,d8678bbf,2711900703\">[0,0,\"0.5-0.5\",3,\"████████████████████████████████████████████████████████\",0,38,862,568,4500,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"194.83799743652,7,255,6737151,1481387158,1,d8678bbf,2737782771\">[0,0,\"0.4-0.4\",4.5,\"████████████████████████████████████████████████████████\",0,25,862,568,2000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"195.28900146484,7,255,6737151,1481387180,1,d8678bbf,2737783963\">[0,0,\"0.4-0.4\",4.5,\"████████████████████████████████████████████████████████\",0,25,862,568,4000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"194.71200561523,7,255,255,1481387274,1,d8678bbf,2737788897\">[862,568,\"0.4-0.4\",4.5,\"████████████████████████████████████████████████████████\",0,45,0,0,1000,0,true,\"微软雅黑\",1]</d>\r\n    <d p=\"194.71200561523,7,255,255,1481387287,1,d8678bbf,2737789537\">[862,568,\"0.4-0.4\",4.5,\"████████████████████████████████████████████████████████\",0,45,0,0,3500,0,true,\"微软雅黑\",1]</d>\r\n</i>"
  },
  {
    "path": "Sample/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">0dp</dimen>\n    <dimen name=\"activity_vertical_margin\">0dp</dimen>\n</resources>\n"
  },
  {
    "path": "Sample/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<resources>\r\n\r\n    <string name=\"app_name\">Danmaku Flame Master\r\n    </string>\r\n    <string name=\"action_settings\">Settings\r\n    </string>\r\n    \r\n    <string name=\"hide_danmaku\">hide</string>\r\n    <string name=\"show_danmaku\">show</string>\r\n    <string name=\"pause_danmaku\">pause</string>\r\n    <string name=\"resume_danmaku\">resume</string>\r\n    <string name=\"send_danmaku\">发送一条(纯文字)</string>\r\n    <string name=\"send_danmaku_image_text\">发送一条(图文)</string>\r\n    <string name=\"send_danmakus\">定时发送</string>\r\n    <string name=\"cancel_sending_danmakus\">取消定时</string>\r\n\r\n    <string name=\"release\">release</string>\r\n    <string name=\"rotate\">旋转屏幕</string>\r\n</resources>\r\n"
  },
  {
    "path": "Sample/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!--\n        Base application theme, dependent on API level. This theme is replaced\n        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.\n    -->\n    <style name=\"AppBaseTheme\" parent=\"android:Theme.Light\">\n        <!--\n            Theme customizations available in newer API levels can go in\n            res/values-vXX/styles.xml, while customizations related to\n            backward-compatibility can go here.\n        -->\n    </style>\n\n    <!-- Application theme. -->\n    <style name=\"AppTheme\" parent=\"AppBaseTheme\">\n        <!-- All customizations that are NOT specific to a particular API-level can go here. -->\n    </style>\n\n</resources>\n"
  },
  {
    "path": "build.gradle",
    "content": "buildscript {\r\n    repositories {\r\n        jcenter()\r\n        google()\r\n    }\r\n    dependencies {\r\n        classpath 'com.android.tools.build:gradle:3.0.1'\r\n        classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'\r\n        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7'\r\n    }\r\n}\r\n\r\nallprojects {\r\n    repositories {\r\n        jcenter()\r\n        google()\r\n    }\r\n}"
  },
  {
    "path": "gradle/gradle-bintray-upload.gradle",
    "content": "/*\n * Copyright 2015 Chen Hui <calmer91@gmail.com>\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\napply plugin: 'com.github.dcendents.android-maven'\napply plugin: 'com.jfrog.bintray'\n\ngroup = GROUP\nversion = VERSION_NAME\n\nbintray {\n    user = project.hasProperty('BINTRAY_USER') ? BINTRAY_USER : ''\n    key = project.hasProperty('BINTRAY_APIKEY') ? BINTRAY_APIKEY : ''\n\n    configurations = ['archives']\n\n    dryRun = false\n    publish = true\n\n    pkg {\n        repo = 'maven'\n        name = BINTRAY_POM_NAME\n        desc = POM_DESCRIPTION\n        websiteUrl = POM_URL\n        vcsUrl = POM_SCM_URL\n        licenses = [POM_LICENCE_NAME]\n        labels = ['FFmpeg', 'Android', 'player']\n        publicDownloadNumbers = true\n        version {\n            name = VERSION_NAME\n            gpg {\n                sign = true\n                passphrase = project.hasProperty('GPG_PASSWORD') ? GPG_PASSWORD : ''\n            }\n        }\n    }\n}\n\ninstall {\n    repositories.mavenInstaller {\n        pom.project {\n            name BINTRAY_POM_NAME\n            packaging POM_PACKAGING\n            description POM_DESCRIPTION\n            url POM_URL\n\n            licenses {\n                license {\n                    name POM_LICENCE_NAME\n                    url POM_LICENCE_URL\n                    distribution POM_LICENCE_DIST\n                }\n            }\n\n            scm {\n                url POM_SCM_URL\n                connection POM_SCM_CONNECTION\n                developerConnection POM_SCM_DEV_CONNECTION\n            }\n\n            developers {\n                developer {\n                    id POM_DEVELOPER_ID\n                    name POM_DEVELOPER_NAME\n                    email POM_DEVELOPER_EMAIL\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "gradle/gradle-mvn-push.gradle",
    "content": "/*\n * Copyright (c) 2013 Chris Banes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\napply plugin: 'maven'\napply plugin: 'signing'\n\ndef isReleaseBuild() {\n    return VERSION_NAME.contains(\"SNAPSHOT\") == false\n}\n\ndef getRepositoryUsername() {\n    return hasProperty('SONATYPE_NEXUS_USERNAME') ? SONATYPE_NEXUS_USERNAME : \"\"\n}\n\ndef getRepositoryPassword() {\n    return hasProperty('SONATYPE_NEXUS_PASSWORD') ? SONATYPE_NEXUS_PASSWORD : \"\"\n}\n\nafterEvaluate { project ->\n    uploadArchives {\n        repositories {\n            mavenDeployer {\n                beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }\n\n                pom.groupId = GROUP\n                pom.artifactId = POM_ARTIFACT_ID\n                pom.version = VERSION_NAME\n\n                repository(url: \"https://oss.sonatype.org/service/local/staging/deploy/maven2/\") {\n                    authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())\n                }\n                snapshotRepository(url: \"https://oss.sonatype.org/content/repositories/snapshots/\") {\n                    authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())\n                }\n\n                pom.project {\n                    name POM_NAME\n                    packaging POM_PACKAGING\n                    description POM_DESCRIPTION\n                    url POM_URL\n\n                    scm {\n                        url POM_SCM_URL\n                        connection POM_SCM_CONNECTION\n                        developerConnection POM_SCM_DEV_CONNECTION\n                    }\n\n                    licenses {\n                        license {\n                            name POM_LICENCE_NAME\n                            url POM_LICENCE_URL\n                            distribution POM_LICENCE_DIST\n                        }\n                    }\n\n                    developers {\n                        developer {\n                            id POM_DEVELOPER_ID\n                            name POM_DEVELOPER_NAME\n                            email POM_DEVELOPER_EMAIL\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    signing {\n        required { isReleaseBuild() && gradle.taskGraph.hasTask(\"uploadArchives\") }\n        sign configurations.archives\n    }\n\n    task androidJavadocs(type: Javadoc) {\n        source = android.sourceSets.main.java.srcDirs\n        classpath += project.files(android.getBootClasspath().join(File.pathSeparator))\n        options.encoding = 'UTF-8'\n        if (JavaVersion.current().isJava8Compatible()) {\n            allprojects {\n                tasks.withType(Javadoc) {\n                    options.addStringOption('Xdoclint:none', '-quiet')\n                }\n            }\n        }\n    }\n\n    task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {\n        classifier = 'javadoc'\n        from androidJavadocs.destinationDir\n    }\n\n    task androidSourcesJar(type: Jar) {\n        classifier = 'sources'\n        from android.sourceSets.main.java.sourceFiles\n    }\n\n    artifacts {\n        archives androidSourcesJar\n        archives androidJavadocsJar\n    }\n}"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Tue Feb 06 14:51:25 CST 2018\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.1-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "VERSION_CODE=9025\nVERSION_NAME=0.9.25\nGROUP=com.github.ctiao\nPOM_NAME=dfm\nPOM_ARTIFACT_ID=common\nPOM_PACKAGING=aar\n\nPOM_DESCRIPTION=A Danmaku render library.\nPOM_URL=https://github.com/ctiao/DanmakuFlameMaster\nPOM_SCM_URL=https://github.com/ctiao/DanmakuFlameMaster\nPOM_SCM_CONNECTION=scm:git:git://github.com/ctiao/DanmakuFlameMaster.git\nPOM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/ctiao/DanmakuFlameMaster.git\n\nPOM_LICENCE_NAME=The Apache Software License, Version 2.0\nPOM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt\nPOM_LICENCE_DIST=repo\n\nPOM_DEVELOPER_ID=calmer91\nPOM_DEVELOPER_NAME=Chen Hui\nPOM_DEVELOPER_EMAIL=calmer91@gmail.com"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\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\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\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\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched.\nif $cygwin ; then\n    [ -n \"$JAVA_HOME\" ] && JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\nfi\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\\\"`/\" >&-\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >&-\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\" ] ; 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\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# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\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%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "ndkbitmap-armv5/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "ndkbitmap-armv5/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 25\n    buildToolsVersion \"26.0.2\"\n\n    defaultConfig {\n        minSdkVersion 9\n        targetSdkVersion 25\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    sourceSets.main {\n        jniLibs.srcDirs 'src/main/libs'\n        jni.srcDirs = [] // This prevents the auto generation of Android.mk\n    }\n}\n\nif (rootProject.file('gradle/gradle-mvn-push.gradle').exists()) {\n    apply from: rootProject.file('gradle/gradle-mvn-push.gradle')\n}\nif (rootProject.file('gradle/gradle-bintray-upload.gradle').exists()) {\n    apply from: rootProject.file('gradle/gradle-bintray-upload.gradle')\n}"
  },
  {
    "path": "ndkbitmap-armv5/gradle.properties",
    "content": "POM_NAME=ndkbitmap-armv5\nBINTRAY_POM_NAME=ndkbitmap-armv5\nPOM_ARTIFACT_ID=ndkbitmap-armv5\nPOM_PACKAGING=aar"
  },
  {
    "path": "ndkbitmap-armv5/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /develop/android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "ndkbitmap-armv5/src/androidTest/java/tv/cjump/ndkbitmap_armv5/ApplicationTest.java",
    "content": "package tv.cjump.ndkbitmap_armv5;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}"
  },
  {
    "path": "ndkbitmap-armv5/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"tv.cjump.ndkbitmap_armv5\">\n\n    <application />\n\n</manifest>\n"
  },
  {
    "path": "ndkbitmap-armv5/src/main/java/tv/cjump/ndkbitmap_armv5/Pragma.java",
    "content": "package tv.cjump.ndkbitmap_armv5;\n\npublic class Pragma {\n}\n"
  },
  {
    "path": "ndkbitmap-armv5/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">ndkbitmap-armv5</string>\n</resources>\n"
  },
  {
    "path": "ndkbitmap-armv7a/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "ndkbitmap-armv7a/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 25\n    buildToolsVersion \"26.0.2\"\n\n    defaultConfig {\n        minSdkVersion 9\n        targetSdkVersion 25\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    sourceSets.main {\n        jniLibs.srcDirs 'src/main/libs'\n        jni.srcDirs = [] // This prevents the auto generation of Android.mk\n    }\n}\n\nif (rootProject.file('gradle/gradle-mvn-push.gradle').exists()) {\n    apply from: rootProject.file('gradle/gradle-mvn-push.gradle')\n}\nif (rootProject.file('gradle/gradle-bintray-upload.gradle').exists()) {\n    apply from: rootProject.file('gradle/gradle-bintray-upload.gradle')\n}"
  },
  {
    "path": "ndkbitmap-armv7a/gradle.properties",
    "content": "POM_NAME=ndkbitmap-armv7a\nBINTRAY_POM_NAME=ndkbitmap-armv7a\nPOM_ARTIFACT_ID=ndkbitmap-armv7a\nPOM_PACKAGING=aar"
  },
  {
    "path": "ndkbitmap-armv7a/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /develop/android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "ndkbitmap-armv7a/src/androidTest/java/tv/cjump/ndkbitmap_armv7a/ApplicationTest.java",
    "content": "package tv.cjump.ndkbitmap_armv7a;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}"
  },
  {
    "path": "ndkbitmap-armv7a/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"tv.cjump.ndkbitmap_armv7a\">\n\n    <application />\n\n</manifest>\n"
  },
  {
    "path": "ndkbitmap-armv7a/src/main/java/tv/cjump/ndkbitmap_armv7a/Pragma.java",
    "content": "package tv.cjump.ndkbitmap_armv7a;\n\npublic class Pragma {\n}\n"
  },
  {
    "path": "ndkbitmap-armv7a/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">ndkbitmap-armv7a</string>\n</resources>\n"
  },
  {
    "path": "ndkbitmap-x86/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "ndkbitmap-x86/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 25\n    buildToolsVersion \"26.0.2\"\n\n    defaultConfig {\n        minSdkVersion 9\n        targetSdkVersion 25\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    sourceSets.main {\n        jniLibs.srcDirs 'src/main/libs'\n        jni.srcDirs = [] // This prevents the auto generation of Android.mk\n    }\n}\n\nif (rootProject.file('gradle/gradle-mvn-push.gradle').exists()) {\n    apply from: rootProject.file('gradle/gradle-mvn-push.gradle')\n}\nif (rootProject.file('gradle/gradle-bintray-upload.gradle').exists()) {\n    apply from: rootProject.file('gradle/gradle-bintray-upload.gradle')\n}"
  },
  {
    "path": "ndkbitmap-x86/gradle.properties",
    "content": "POM_NAME=ndkbitmap-armv7a\nBINTRAY_POM_NAME=ndkbitmap-x86\nPOM_ARTIFACT_ID=ndkbitmap-x86\nPOM_PACKAGING=aar"
  },
  {
    "path": "ndkbitmap-x86/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /develop/android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "ndkbitmap-x86/src/androidTest/java/tv/cjump/ndkbitmap_x86/ApplicationTest.java",
    "content": "package tv.cjump.ndkbitmap_x86;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}"
  },
  {
    "path": "ndkbitmap-x86/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"tv.cjump.ndkbitmap_x86\">\n\n    <application />\n\n</manifest>\n"
  },
  {
    "path": "ndkbitmap-x86/src/main/java/tv/cjump/ndkbitmap_x86/Pragma.java",
    "content": "package tv.cjump.ndkbitmap_x86;\n\npublic class Pragma {\n}\n"
  },
  {
    "path": "ndkbitmap-x86/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">ndkbitmap-x86</string>\n</resources>\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':DanmakuFlameMaster', ':Sample', ':ndkbitmap-armv5', ':ndkbitmap-armv7a', ':ndkbitmap-x86'\n"
  }
]