├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── gradle.xml ├── markdown-navigator.xml ├── markdown-navigator │ └── profiles_settings.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── androidlibrary ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── wanglei │ │ └── androidlibrary │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── wanglei │ └── androidlibrary │ └── ExampleUnitTest.java ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── wanglei │ │ └── imageoptimization │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── wanglei │ │ │ └── imageoptimization │ │ │ └── MainActivity.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ ├── cdc.PNG │ │ ├── dewdw.jpeg │ │ ├── g.9.png │ │ ├── h.png │ │ ├── i.JPEG │ │ ├── j.JPG │ │ ├── k.png │ │ ├── qw.jpeg │ │ ├── wece.jpg │ │ └── wew.jpeg │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── wanglei │ └── imageoptimization │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── imageafter ├── h.png ├── h_lossless.png ├── h_lossy.png ├── h_webp.webp ├── h_webp_lossless.webp ├── log.jpg ├── tasks.jpg ├── wece.jpg ├── wece_lossy.jpg └── wece_webp.webp ├── optimization ├── .gitignore ├── build.gradle ├── src │ └── main │ │ ├── groovy │ │ └── com │ │ │ └── wanglei │ │ │ └── optimizer │ │ │ ├── ImageOptimizationConstants.groovy │ │ │ ├── ImageOptimizationExtensions.groovy │ │ │ ├── ImageOptimizationPlugin.groovy │ │ │ ├── ImageOptimizationTask.groovy │ │ │ ├── ImageOptimizationUtils.groovy │ │ │ ├── compressjpg │ │ │ ├── AbstractCompressJpg.groovy │ │ │ ├── LosslessCompressJpg.groovy │ │ │ ├── LossyCompressJpg.groovy │ │ │ ├── factory │ │ │ │ └── CompressJpgFactory.groovy │ │ │ └── inter │ │ │ │ └── ICompressJpg.groovy │ │ │ ├── compresspng │ │ │ ├── AbstractCompressPng.groovy │ │ │ ├── LosslessCompressPng.groovy │ │ │ ├── LossyCompressPng.groovy │ │ │ ├── factory │ │ │ │ └── CompressPngFactory.groovy │ │ │ └── inter │ │ │ │ └── ICompressPng.groovy │ │ │ └── convertwebp │ │ │ ├── AbstractConvertWebp.groovy │ │ │ ├── LosslessConvertWebp.groovy │ │ │ ├── LossyConvertWebp.groovy │ │ │ ├── factory │ │ │ └── ConvertWebpFactory.groovy │ │ │ └── inter │ │ │ └── IConvertWebp.java │ │ └── resources │ │ ├── META-INF │ │ └── gradle-plugins │ │ │ └── com.wanglei.image-optimization.properties │ │ ├── cwebp │ │ ├── cwebp_darwin │ │ ├── cwebp_linux │ │ └── cwebp_win.exe │ │ ├── guetzli │ │ ├── guetzli_darwin │ │ ├── guetzli_linux │ │ └── guetzli_win.exe │ │ ├── pngout │ │ ├── pngout_darwin │ │ ├── pngout_linux │ │ └── pngout_win.exe │ │ └── pngquant │ │ ├── pngquant_darwin │ │ ├── pngquant_linux │ │ └── pngquant_win.exe ├── upload2Jcenter.gradle └── upload2PrivateMaven.gradle ├── orginalimage ├── cdc.PNG ├── dewdw.jpeg ├── g.9.png ├── h.png ├── i.JPEG ├── j.JPG ├── k.png ├── qw.jpeg ├── wece.jpg └── wew.jpeg ├── settings.gradle └── updateLog.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 36 | 37 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ImageOptimization 2 | 3 | 安卓图片优化插件:能有效减少apk安装包大小,支持png/jpg转为webp,支持png图片有损无损压缩,支持jpg有损压缩 4 | 5 | ### 支持的os 6 | 7 | `macOS`、`windows10`上已经测试通过,linux上暂时没测试。 8 | 9 | ### 引入ImageOptimization 10 | 11 | 在Project的build.gradle文件中: 12 | 13 | ``` 14 | buildscript { 15 | repositories { 16 | jcenter() 17 | } 18 | dependencies { 19 | ... 20 | classpath 'com.wanglei.image-optimization:optimization:2.0' 21 | } 22 | } 23 | ``` 24 | 25 | 在你想要优化的module的build.gradle文件中引入插件: 如在多个module使用则每个module都要引入插件 26 | 27 | `apply plugin:'com.wanglei.image-optimization'` 28 | 29 | 同步之后会生成如下任务:双击执行任务即可 30 | 31 | ![](imageafter/tasks.jpg) 32 | 33 | ### 任务生成规则 34 | 35 | imageOptimization_module名字_variant名字 36 | 37 | ### 配置项 38 | 39 | 在module的build.gradle文件中添加配置选项,配置插件优化的策略以及参数: 40 | 41 | ``` 42 | ImageOptimization{ 43 | pluginStrategy = "compress" 44 | convertWebpQuality = 80 45 | jpegCompressQuality = 90 46 | appIconName = "ic_launcher" 47 | appIconRoundName = "ic_launcher_round" 48 | convertWebpType = "lossy" 49 | compressPngType = "lossless" 50 | pngCompressQuality = 79 51 | filter{ 52 | filterDirs = ["mipmap-hdpi","mipmap-xhdpi","mipmap-xxhdpi"] 53 | filterImageNames = ["cdc.PNG","wew.jpeg","c.png"] 54 | } 55 | } 56 | ``` 57 | ImageOptimization支持多项参数配置,灵活度高,可根据项目需要自己配置参数,可配置优先转为webp不能转换的在进行图片的压缩,同样你也可以配置只进行图片的压缩而不转为webp 58 | 59 | ### 配置项参数说明 60 | 61 | | 参数 | 是否必须 |参数配置 |参数说明 | 62 | |--|--|--|--| 63 | | pluginStrategy | 否 | 可配置"webp"或者"compress" | 插件运行策略参数:如配置为"webp"则会将图片优先转为webp格式,如果不能转换或者转换失败或转换为图片更大则进行图片的压缩,如配置为"compress"则只进行图片的压缩不会转为webp,此参数如不配置默认为"compress" | 64 | | convertWebpType | 否 | 可配置"lossy"或者"lossless" | webp转换类型参数:"lossy"转为webp采用有损转换模式,"lossless"转为webp采用无损模式,此参数默认为"lossy" | 65 | | compressPngType | 否 | 可配置"lossy"或者"lossless" | png图片压缩类型参数:"lossy"为压缩png采用有损压缩模式,"lossless"为压缩png采用无损压缩模式,此参数默认为"lossy" | 66 | | pngCompressQuality | 否 | 可配置范围为[0,100] | png图片压缩质量参数:默认为80,建议使用默认即可,此参数为compressPngType为"lossy"时起作用,也就是有损压缩才起作用 | 67 | | convertWebpQuality | 否 | 可配置范围为[0,100] | 图片转为webp的质量参数:默认为75,此参数为谷歌建议的参数,建议不要更改,转换webp有损模式下此参数才起作用 | 68 | | jpegCompressQuality | 否 | 可配置范围为[84,100] | jpg图片压缩质量参数:默认为84 | 69 | | appIconRoundName | 否 | 项目app圆形启动图标的名字,不包括图片扩展名 | 如圆形启动图标为:icon_round.png,这里配置为icon_round | 70 | | appIconName | 是 | 项目app启动图标的名字,不包括图片扩展名 | 如启动图标为:icon.png,这里配置为icon | 71 | | filterDirs | 否 | 不需要压缩或转换的图片目录名 | 如 filterDirs = ["mipmap-hdpi","mipmap-xhdpi"] ,mipmap-hdpi与mipmap-xhdpi目录下图片资源保持原样| 72 | | filterImageNames | 否 | 不需要压缩或转换的图片的名字 | 如 filterImageNames = ["cdc.PNG","wew.jpeg"] ,cdc.PNG与wew.jpeg图片均保持原样,这里要配置完整的图片名包括扩展名并且区分大小写| 73 | 74 | ### 图片压缩效果对比 75 | 76 | #### PNG图片 77 | | 原图 | 有损转为webp | 无损转为webp | lossy有损压缩 | lossless无损压缩 | 78 | |--|--|--|--|--| 79 | | ![](imageafter/h.png) | ![](imageafter/h_webp.webp) | ![](imageafter/h_webp_lossless.webp) | ![](imageafter/h_lossy.png) | ![](imageafter/h_lossless.png) | 80 | | 50.05k | 15.28k | 30.30k | 13.64k | 42.21k | 81 | 82 | #### jpg图片 83 | | 原图 | 有损转为webp | lossy有损压缩 | 84 | |--|--|--| 85 | | ![](imageafter/wece.jpg) | ![](imageafter/wece_webp.webp) | ![](imageafter/wece_lossy.jpg) | 86 | | 7.91k | 2.82k | 5.17k | 87 | 88 | #### 由于GitHub有时不能正常显示webp图片,上图webp图片可能无法正常显示,可到imageafter文件夹中查看对应图片 89 | 90 | ### 额外说明 91 | 92 | #### 插件运行的时候会有相应信息打印出: 93 | 94 | ![](imageafter/log.jpg) 95 | 96 | #### 安卓对webp图片的支持 97 | 98 | 安卓不同sdk版本对webp的支持有所不同,具体如下: 99 | 100 | sdk<14:不支持webp格式图片 101 | 14<= sdk <18:支持webp格式图片,但是不支持有透明通道的 webp格式图片 102 | sdk >=18:有透明通道的webp图片也支持 103 | 104 | 此外如果想以无损方式将png/jpg图片转为webp,需要的sdk版本最小为18。 105 | 106 | **ImageOptimization对以上情况都做了适配,你只需放心使用即可** 107 | 108 | #### .9.png图片的处理 109 | 110 | ImageOptimization插件对.9.png图片不做任何处理,既不转换也不压缩保持原样。 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /androidlibrary/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /androidlibrary/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 28 5 | 6 | defaultConfig { 7 | minSdkVersion 14 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | 21 | } 22 | 23 | dependencies { 24 | implementation fileTree(dir: 'libs', include: ['*.jar']) 25 | implementation 'com.android.support:appcompat-v7:28.0.0' 26 | testImplementation 'junit:junit:4.12' 27 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 28 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 29 | } 30 | //上传的库的名字,是和你Module的名字是一样的!!!所以你的Module叫什么, 31 | // 你的gradle依赖路径就是: groupId:moduleName:publishVersion 32 | //module是android library如下配置上传jcenter, 33 | // 先执行other分组中的instal任务生成pom文件,在执行publishing分组中的bintrayUpload任务上传 34 | group "com.wanglei.image-optimization" 35 | version "1.0" 36 | 37 | 38 | //源代码打包任务 39 | task sourcesJar(type :Jar){ 40 | classifier 'sources' //分类器 区分其他jar包 41 | from android.sourceSets.main.java.srcDirs 42 | } 43 | //创建文档生成任务 44 | task javadoc(type:Javadoc){ 45 | source = android.sourceSets.main.java.srcDirs 46 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 47 | options.encoding "UTF-8" 48 | options.charSet 'UTF-8' 49 | failOnError false 50 | } 51 | 52 | task docJar(type :Jar,dependsOn:javadoc){ 53 | classifier 'doc' //分类器 区分其他jar包 54 | from javadoc.destinationDir 55 | } 56 | //配置 工程工件 jar产出的配置 57 | artifacts{ 58 | archives sourcesJar 59 | archives docJar 60 | } 61 | 62 | 63 | buildscript { 64 | repositories { 65 | jcenter() 66 | } 67 | dependencies { 68 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' 69 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 70 | } 71 | } 72 | apply plugin: 'com.jfrog.bintray' 73 | apply plugin: 'com.github.dcendents.android-maven' 74 | 75 | bintray { 76 | user = 'xxxx' 77 | key = 'apiKey' 78 | configurations = ['archives'] 79 | pkg { 80 | repo = 'AndroidImageOptimization' //仓库名 81 | name = 'optimization' //项目名 82 | licenses = ['Apache-2.0'] 83 | vcsUrl = 'https://github.com/MISAYAWANGLEI/ImageOptimization.git' 84 | version { 85 | desc = 'android image optimizer plugin' 86 | released = new Date() 87 | vcsTag = '1.0' 88 | } 89 | } 90 | 91 | } 92 | 93 | 94 | install { 95 | repositories.mavenInstaller { 96 | pom.project { 97 | licenses { 98 | license { 99 | name 'The Apache Software License, Version 2.0' 100 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 101 | distribution 'repo' 102 | } 103 | } 104 | developers { 105 | developer { 106 | id 'misaya' 107 | name 'wanglei' 108 | email 'wanglei.pado@gmail.com' 109 | } 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /androidlibrary/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /androidlibrary/src/androidTest/java/com/wanglei/androidlibrary/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.wanglei.androidlibrary; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.wanglei.androidlibrary.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /androidlibrary/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /androidlibrary/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | androidLibrary 3 | 4 | -------------------------------------------------------------------------------- /androidlibrary/src/test/java/com/wanglei/androidlibrary/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.wanglei.androidlibrary; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin:'com.wanglei.image-optimization' 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.wanglei.imageoptimization" 7 | minSdkVersion 14 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | ImageOptimization{ 22 | pluginStrategy = "compress" 23 | convertWebpQuality = 80 24 | jpegCompressQuality = 90 25 | appIconName = "ic_launcher" 26 | appIconRoundName = "ic_launcher_round" 27 | convertWebpType = "lossy" 28 | compressPngType = "lossless" 29 | pngCompressQuality = 80 30 | filter{ 31 | //filterDirs = ["mipmap-hdpi","mipmap-xhdpi","mipmap-xxhdpi"] 32 | //filterImageNames = ["cdc.PNG","wew.jpeg","c.png"] 33 | } 34 | } 35 | 36 | dependencies { 37 | implementation fileTree(dir: 'libs', include: ['*.jar']) 38 | implementation 'com.android.support:appcompat-v7:28.0.0' 39 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 40 | testImplementation 'junit:junit:4.12' 41 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 42 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 43 | } 44 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/wanglei/imageoptimization/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.wanglei.imageoptimization; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.wanglei.imageoptimization", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/wanglei/imageoptimization/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.wanglei.imageoptimization; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | 6 | public class MainActivity extends AppCompatActivity { 7 | 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | setContentView(R.layout.activity_main); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/cdc.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/app/src/main/res/mipmap-hdpi/cdc.PNG -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/dewdw.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/app/src/main/res/mipmap-hdpi/dewdw.jpeg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/g.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/app/src/main/res/mipmap-hdpi/g.9.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/app/src/main/res/mipmap-hdpi/h.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/i.JPEG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/app/src/main/res/mipmap-hdpi/i.JPEG -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/j.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/app/src/main/res/mipmap-hdpi/j.JPG -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/app/src/main/res/mipmap-hdpi/k.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/qw.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/app/src/main/res/mipmap-hdpi/qw.jpeg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/wece.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/app/src/main/res/mipmap-hdpi/wece.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/wew.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/app/src/main/res/mipmap-hdpi/wew.jpeg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ImageOptimization 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/wanglei/imageoptimization/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.wanglei.imageoptimization; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | //mavenLocal() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.3.1' 11 | classpath 'com.wanglei.image-optimization:optimization:2.0' 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | jcenter() 19 | 20 | } 21 | } 22 | 23 | task clean(type: Delete) { 24 | delete rootProject.buildDir 25 | } 26 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Feb 20 22:19:29 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /imageafter/h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/imageafter/h.png -------------------------------------------------------------------------------- /imageafter/h_lossless.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/imageafter/h_lossless.png -------------------------------------------------------------------------------- /imageafter/h_lossy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/imageafter/h_lossy.png -------------------------------------------------------------------------------- /imageafter/h_webp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/imageafter/h_webp.webp -------------------------------------------------------------------------------- /imageafter/h_webp_lossless.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/imageafter/h_webp_lossless.webp -------------------------------------------------------------------------------- /imageafter/log.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/imageafter/log.jpg -------------------------------------------------------------------------------- /imageafter/tasks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/imageafter/tasks.jpg -------------------------------------------------------------------------------- /imageafter/wece.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/imageafter/wece.jpg -------------------------------------------------------------------------------- /imageafter/wece_lossy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/imageafter/wece_lossy.jpg -------------------------------------------------------------------------------- /imageafter/wece_webp.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/imageafter/wece_webp.webp -------------------------------------------------------------------------------- /optimization/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /optimization/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | apply plugin: 'groovy' 3 | 4 | dependencies { 5 | compile fileTree(dir: 'libs', include: ['*.jar']) 6 | compile gradleApi() 7 | compile localGroovy() 8 | compile 'com.android.tools.build:gradle:3.3.1' 9 | } 10 | 11 | sourceCompatibility = "1.7" 12 | targetCompatibility = "1.7" 13 | 14 | apply from: 'upload2Jcenter.gradle' 15 | 16 | group "com.wanglei.image-optimization" 17 | version "2.0" 18 | 19 | //apply plugin: 'maven-publish' 20 | // 21 | ////上传本地配置 22 | //publishing{ 23 | // publications{ 24 | // optimizer(MavenPublication){ 25 | // from components.java 26 | // artifactId = "optimization" 27 | // } 28 | // } 29 | //} 30 | 31 | //源代码打包任务 32 | task sourcesJar(type :Jar){ 33 | baseName 'optimization' 34 | classifier 'sources' 35 | from sourceSets.main.allSource 36 | } 37 | //生成文档任务 38 | task docJar(type :Jar,dependsOn:[javadoc,groovydoc]){ 39 | baseName 'optimization' 40 | classifier 'doc' 41 | from javadoc.destinationDir,groovydoc.destinationDir 42 | } 43 | //配置 工程工件 jar产出的配置 44 | artifacts{ 45 | archives sourcesJar 46 | archives docJar 47 | } 48 | 49 | buildscript { 50 | repositories { 51 | jcenter() 52 | } 53 | dependencies { 54 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' 55 | } 56 | } 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/ImageOptimizationConstants.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer 2 | 3 | class ImageOptimizationConstants { 4 | 5 | def static final PNG = ".png" 6 | def static final PNG9 = ".9.png" 7 | def static final JPG = ".jpg" 8 | def static final JPEG = ".jpeg" 9 | def static final TOOLSDIR = "image_tools" 10 | def static final DRAWABLE_FOLDER = "drawable" 11 | def static final MIPMAP_FOLDER = "mipmap" 12 | def static final IMG_EXT_NAME = "ImageOptimization" 13 | def static final IMG_FILTER_EXT_NAME = "filter" 14 | // 15 | def static final defaultJpegCompressQuality = 84//[84,100] 16 | def static final defaultConvertWebpQuality = 75//[0,100] 17 | def static final defaultCompressPngQuality = 80//[0,100] 18 | //转换webp的工具类型 19 | def static final LOSSY = "lossy"//默认,有损模式,可指定压缩质量 20 | def static final LOSSLESS = "lossless"//无损模式 21 | 22 | def static final CWEBP_TOOL_NAME = "cwebp" 23 | def static final LOSSY_PNG_TOOL_NAME = "pngquant" 24 | def static final LOSSLESS_PNG_TOOL_NAME = "pngout" 25 | def static final JPG_TOOL_NAME = "guetzli" 26 | 27 | //插件策略 28 | def static final STRATEGY_WEBP = "webp"//优先转换为webp,不能转换的进行压缩 29 | def static final STRATEGY_ONLIY_COMPRESS = "compress"//只进行压缩,不转为webp 30 | 31 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/ImageOptimizationExtensions.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer 2 | 3 | class ImageOptimizationExtensions{ 4 | 5 | def jpegCompressQuality = ImageOptimizationConstants.defaultJpegCompressQuality 6 | //有损模式指定才有意义,无损模式用不到这个参数 7 | def convertWebpQuality = ImageOptimizationConstants.defaultConvertWebpQuality 8 | def pngCompressQuality = ImageOptimizationConstants.defaultCompressPngQuality 9 | String appIconName //启动图标的图片名字 10 | String appIconRoundName //圆形启动图标的图片名字 11 | def convertWebpType = ImageOptimizationConstants.LOSSY //转换webp的模式,默认有损 12 | def compressPngType = ImageOptimizationConstants.LOSSY //压缩png的模式,默认有损 13 | def pluginStrategy = ImageOptimizationConstants.STRATEGY_ONLIY_COMPRESS //默认插件策略为只压缩图片 14 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/ImageOptimizationPlugin.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer 2 | 3 | import com.android.build.gradle.AppPlugin 4 | import com.android.build.gradle.LibraryPlugin 5 | import com.android.build.gradle.api.BaseVariant 6 | import com.android.sdklib.repository.targets.PlatformTarget 7 | import org.gradle.api.DomainObjectCollection 8 | import org.gradle.api.GradleException 9 | import org.gradle.api.Plugin 10 | import org.gradle.api.Project 11 | 12 | class ImageOptimizationPlugin implements Plugin { 13 | 14 | @Override 15 | void apply(Project project) { 16 | if (project.plugins.hasPlugin(AppPlugin)) { 17 | startApply(project, (DomainObjectCollection) project.android.applicationVariants) 18 | } else if (project.plugins.hasPlugin(LibraryPlugin)) { 19 | startApply(project, (DomainObjectCollection) project.android.libraryVariants) 20 | } else { 21 | throw new GradleException("image-optimization plugin should be used in android module") 22 | } 23 | } 24 | 25 | static void startApply(Project project, DomainObjectCollection variants) { 26 | //创建扩展,外部可以配置的参数 27 | def imgExt = project.extensions.create(ImageOptimizationConstants.IMG_EXT_NAME, ImageOptimizationExtensions) 28 | def imgFilterExt = project.ImageOptimization.extensions.create(ImageOptimizationConstants.IMG_FILTER_EXT_NAME, ImageOptimizationFilterExtensions) 29 | 30 | project.afterEvaluate { 31 | project.logger.error "过滤图片文件夹目录名:${imgFilterExt.filterDirs}" 32 | variants.all { 33 | BaseVariant variant -> 34 | //存放图片文件夹集合 35 | List imgDirs = [] 36 | variant.sourceSets.each { sourceSet -> 37 | sourceSet.resDirectories.each { res -> 38 | if (res.exists()) { 39 | res.eachDir { 40 | //是图片目录 41 | if (it.directory && ImageOptimizationUtils.isImageFolder(it)) { 42 | //过滤用户配置的满足条件的图片 43 | if (imgFilterExt.filterDirs == null || 44 | (imgFilterExt.filterDirs != null 45 | && !imgFilterExt.filterDirs.contains(it.name))){ 46 | imgDirs << it 47 | } 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | if (!imgDirs.empty) { 55 | def task = project.tasks.create("imageOptimization_${project.name.capitalize()}_${variant.name.capitalize()}", ImageOptimizationTask) { 56 | // //获取manifest.xml文件 57 | // def variantOutput = variant.outputs.first() 58 | // if (variantOutput.processManifest.properties['manifestOutputFile'] != null) { 59 | // manifestFile = variantOutput.processManifest.manifestOutputFile 60 | // } else if (variantOutput.processResources.properties['manifestFile'] != null) { 61 | // manifestFile = variantOutput.processResources.manifestFile 62 | // } 63 | //获取minSdk 64 | //variant.mergedFlavor.minSdkVersion.apiLevel 65 | //variant.variantData.variantConfiguration 66 | minSdk = variant.mergeResourcesProvider.get().minSdk 67 | //图片文件夹集合 68 | res = imgDirs 69 | //启动图标名字 70 | appIconName = imgExt.appIconName 71 | appIconRoundName = imgExt.appIconRoundName 72 | // 73 | convertWebpType = imgExt.convertWebpType 74 | compressPngType = imgExt.compressPngType 75 | //配置参数 76 | jpegCompressQuality = imgExt.jpegCompressQuality 77 | convertWebpQuality = imgExt.convertWebpQuality 78 | pngCompressQuality = imgExt.pngCompressQuality 79 | //插件策略,默认只压缩 80 | pluginStrategy = imgExt.pluginStrategy 81 | // 82 | filterImageNames = imgFilterExt.filterImageNames 83 | } 84 | //task.dependsOn variant.outputs.first().processManifest 85 | } 86 | } 87 | } 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/ImageOptimizationTask.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer 2 | 3 | import com.wanglei.optimizer.compressjpg.factory.CompressJpgFactory 4 | import com.wanglei.optimizer.compressjpg.inter.ICompressJpg 5 | import com.wanglei.optimizer.compresspng.factory.CompressPngFactory 6 | import com.wanglei.optimizer.compresspng.inter.ICompressPng 7 | import com.wanglei.optimizer.convertwebp.factory.ConvertWebpFactory 8 | import com.wanglei.optimizer.convertwebp.inter.IConvertWebp 9 | import org.gradle.api.DefaultTask 10 | import org.gradle.api.tasks.Input 11 | import org.gradle.api.tasks.Optional 12 | import org.gradle.api.tasks.TaskAction 13 | 14 | class ImageOptimizationTask extends DefaultTask{ 15 | 16 | def webPTool 17 | def pngTool 18 | def jpgTool 19 | 20 | // @Input 21 | // def manifestFile 22 | 23 | @Input 24 | def minSdk 25 | 26 | @Input 27 | List res 28 | 29 | @Input 30 | @Optional 31 | def jpegCompressQuality = ImageOptimizationConstants.defaultJpegCompressQuality 32 | 33 | @Input 34 | @Optional 35 | def convertWebpQuality = ImageOptimizationConstants.defaultConvertWebpQuality 36 | 37 | @Input 38 | @Optional 39 | def pngCompressQuality = ImageOptimizationConstants.defaultCompressPngQuality 40 | 41 | @Input 42 | String appIconName 43 | 44 | @Input 45 | @Optional 46 | String appIconRoundName 47 | 48 | @Input 49 | @Optional 50 | String convertWebpType = ImageOptimizationConstants.LOSSY 51 | 52 | @Input 53 | @Optional 54 | String compressPngType = ImageOptimizationConstants.LOSSY 55 | 56 | @Input 57 | @Optional 58 | String pluginStrategy = ImageOptimizationConstants.STRATEGY_ONLIY_COMPRESS 59 | 60 | @Input 61 | @Optional 62 | def filterImageNames = null //需要过滤的图片名字集合 63 | 64 | //jpg的压缩目前不支持外部设置,统一有损压缩 65 | String compressJpgType = ImageOptimizationConstants.LOSSY 66 | 67 | // 68 | IConvertWebp convertWebpTool 69 | ICompressPng compressPngTool 70 | ICompressJpg compressJpgTool 71 | 72 | ImageOptimizationTask(){ 73 | group = "WLImageOptimization" 74 | } 75 | 76 | @TaskAction 77 | def run(){ 78 | //检查参数是否合法 79 | ImageOptimizationUtils.checkOptionalParams(jpegCompressQuality,convertWebpQuality,appIconName,convertWebpType,minSdk,compressPngType,pluginStrategy,pngCompressQuality) 80 | //拷贝工具 81 | webPTool = ImageOptimizationUtils.copyTool(project,ImageOptimizationConstants.CWEBP_TOOL_NAME) 82 | if (compressPngType.equalsIgnoreCase(ImageOptimizationConstants.LOSSY)){ 83 | pngTool = ImageOptimizationUtils.copyTool(project,ImageOptimizationConstants.LOSSY_PNG_TOOL_NAME) 84 | }else { 85 | pngTool = ImageOptimizationUtils.copyTool(project,ImageOptimizationConstants.LOSSLESS_PNG_TOOL_NAME) 86 | } 87 | jpgTool = ImageOptimizationUtils.copyTool(project,ImageOptimizationConstants.JPG_TOOL_NAME) 88 | //获取对应工具 89 | convertWebpTool = ConvertWebpFactory.getConvertWebpTool(convertWebpType) 90 | compressPngTool = CompressPngFactory.getCompressPngTool(compressPngType) 91 | compressJpgTool = CompressJpgFactory.getCompressJpgTool(compressJpgType) 92 | // 93 | project.logger.error "=========WL ImageOptimization Start==========" 94 | project.logger.error "pluginStrategy : $pluginStrategy" 95 | project.logger.error "jpegCompressQuality : $jpegCompressQuality" 96 | project.logger.error "pngCompressQuality : $pngCompressQuality" 97 | project.logger.error "convertWebpQuality : $convertWebpQuality" 98 | project.logger.error "webp tool : $webPTool" 99 | project.logger.error "png tool : $pngTool" 100 | project.logger.error "jpg tool : $jpgTool" 101 | project.logger.error "mini sdk : $minSdk" 102 | project.logger.error "convertWebpType : $convertWebpType" 103 | project.logger.error "compressPngType : $compressPngType" 104 | project.logger.error "需要过滤的图片:$filterImageNames" 105 | res.each { 106 | project.logger.error "image dir : $it.absolutePath" 107 | } 108 | // project.logger.error "manifestFile : $manifestFile.absolutePath" 109 | project.logger.error "=========1,parse Manifest.xml==========" 110 | //解析Manifest.xml获取启动图标,启动图标不转为webp 111 | // def ns = new Namespace("http://schemas.android.com/apk/res/android","android") 112 | // def node = new XmlParser().parse(manifestFile) 113 | // Node application = node.application[0] 114 | // icon = application.attributes()[ns.icon]//@mipmap/ic_launcher 115 | // roundIcon = application.attributes()[ns.roundIcon]//@mipmap/ic_launcher_round 116 | // icon = icon.substring(icon.lastIndexOf("/")+1,icon.length())//ic_launcher 117 | // roundIcon = roundIcon.substring(roundIcon.lastIndexOf("/")+1,roundIcon.length())//ic_launcher_round 118 | 119 | project.logger.error "launcher Icon Name:$appIconName" 120 | project.logger.error "launcher Round Icon Name :$appIconRoundName" 121 | 122 | project.logger.error "=========2, parse resDir==========" 123 | //存放png图片 124 | def pngs = [] 125 | //存放jpg图片 126 | def jpgs = [] 127 | //遍历文件夹 128 | res.each{ 129 | if (ImageOptimizationUtils.isImageFolder(it)){ 130 | it.eachFile { 131 | if (ImageOptimizationUtils.isJpgImage(it) && ImageOptimizationUtils.isNotLauncherIcon(it,appIconName,appIconRoundName)){ 132 | if (filterImageNames == null || 133 | (filterImageNames != null && !filterImageNames.contains(it.name))){ 134 | jpgs << it 135 | } 136 | }else if(ImageOptimizationUtils.isPngImage(it) && ImageOptimizationUtils.isNotLauncherIcon(it,appIconName,appIconRoundName)){ 137 | if (filterImageNames == null || 138 | (filterImageNames != null && !filterImageNames.contains(it.name))){ 139 | pngs << it 140 | } 141 | } 142 | } 143 | } 144 | } 145 | 146 | //输出全部图片信息 147 | pngs.each { 148 | project.logger.error "${it.absolutePath}" 149 | } 150 | 151 | jpgs.each { 152 | project.logger.error "${it.absolutePath}" 153 | } 154 | //这里只进行简单的判断 155 | if (pluginStrategy.equalsIgnoreCase(ImageOptimizationConstants.STRATEGY_ONLIY_COMPRESS)){ 156 | project.logger.error "=========3,start compress Image==========" 157 | pngs.each { 158 | compressPng(it) 159 | } 160 | // 161 | jpgs.each { 162 | compressJpg(it) 163 | } 164 | }else { 165 | project.logger.error "=========3,start ConvertImage to Webp==========" 166 | //保存png转webp失败或者转换后图片更大的集合 167 | def pngConvertFailed = [] 168 | //保存jpg转webp失败或者转换后图片更大的集合 169 | def jpgConvertFailed = [] 170 | if (minSdk >= 14 && minSdk < 18){ 171 | def compress = []//保存不能转换的图片,[14,18)之间有透明通道的png图片不能转为webp,18以上均可以 172 | //处理png 如果带透明度 则加入压缩集合,不能转为webp 173 | project.logger.error "=========minSdk >= 14 && minSdk < 18==========" 174 | pngs.each { 175 | if (ImageOptimizationUtils.isTransparent(it)){ 176 | compress << it 177 | }else{ 178 | convertWebp(it,pngConvertFailed) 179 | } 180 | } 181 | compress.each { 182 | compressPng(it) 183 | } 184 | // 185 | jpgs.each { 186 | convertWebp(it,jpgConvertFailed) 187 | } 188 | }else if(minSdk >=18){//18及以上均可转换 189 | project.logger.error "=========minSdk >=18==========" 190 | pngs.each { 191 | convertWebp(it,pngConvertFailed) 192 | } 193 | // 194 | jpgs.each { 195 | convertWebp(it,jpgConvertFailed) 196 | } 197 | }else{//小于14只能压缩操作 198 | project.logger.error "=========minSdk < 14==========" 199 | pngs.each { 200 | compressPng(it) 201 | } 202 | // 203 | jpgs.each { 204 | compressJpg(it) 205 | } 206 | } 207 | project.logger.error "=========4,start compress Failed Image==========" 208 | //输出信息 209 | pngConvertFailed.each { 210 | project.logger.error "${it.absolutePath}" 211 | } 212 | 213 | jpgConvertFailed.each { 214 | project.logger.error "${it.absolutePath}" 215 | } 216 | //转换失败的图片执行压缩操作 217 | pngConvertFailed.each { 218 | compressPng(it) 219 | } 220 | 221 | jpgConvertFailed.each { 222 | compressJpg(it) 223 | } 224 | } 225 | project.logger.error "=========WL ImageOptimization End==========" 226 | } 227 | 228 | 229 | def convertWebp(File file, def convertFailedList){ 230 | convertWebpTool.convertWebp(webPTool,project,file,convertFailedList,convertWebpQuality) 231 | } 232 | 233 | def compressPng(File file){ 234 | compressPngTool.compressPng(pngTool,project,file,pngCompressQuality) 235 | } 236 | 237 | def compressJpg(File file){ 238 | compressJpgTool.compressJpg(jpgTool,project,file,jpegCompressQuality) 239 | } 240 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/ImageOptimizationUtils.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer 2 | 3 | 4 | import org.apache.tools.ant.taskdefs.condition.Os 5 | import org.gradle.api.GradleException 6 | import org.gradle.api.Project 7 | 8 | import javax.imageio.ImageIO 9 | import java.awt.image.BufferedImage 10 | import java.security.InvalidParameterException 11 | 12 | class ImageOptimizationUtils { 13 | 14 | /** 15 | * 判断是否是文件夹 16 | * @param file 17 | * @return 18 | */ 19 | def static isImageFolder(File file) { 20 | return file.name.startsWith(ImageOptimizationConstants.DRAWABLE_FOLDER) || file.name.startsWith(ImageOptimizationConstants.MIPMAP_FOLDER) 21 | } 22 | 23 | def static isPngImage(File file) { 24 | return (file.name.endsWith(ImageOptimizationConstants.PNG) || file.name.endsWith(ImageOptimizationConstants.PNG.toUpperCase())) && 25 | !file.name.endsWith(ImageOptimizationConstants.PNG9) && !file.name.endsWith(ImageOptimizationConstants.PNG9.toUpperCase()) 26 | } 27 | 28 | def static isJpgImage(File file) { 29 | return file.name.endsWith(ImageOptimizationConstants.JPG) || file.name.endsWith(ImageOptimizationConstants.JPG.toUpperCase()) || 30 | file.name.endsWith(ImageOptimizationConstants.JPEG) || file.name.endsWith(ImageOptimizationConstants.JPEG.toUpperCase()) 31 | } 32 | 33 | /** 34 | * 判断图片是否存在透明通道 35 | */ 36 | def static isTransparent(File file) { 37 | BufferedImage image = ImageIO.read(file) 38 | return image.colorModel.hasAlpha() 39 | } 40 | 41 | /** 42 | * 获取图片处理工具 43 | */ 44 | def static copyTool(Project project, String name) { 45 | String toolName 46 | //获得对应平台的工具 47 | if (Os.isFamily(Os.FAMILY_MAC)) { 48 | toolName = "${name}_darwin" 49 | } else if (Os.isFamily(Os.FAMILY_WINDOWS)) { 50 | toolName = "${name}_win.exe" 51 | } else { 52 | toolName = "${name}_linux" 53 | } 54 | def tool = "${project.buildDir}/${ImageOptimizationConstants.TOOLSDIR}/$toolName" 55 | def file = new File(tool) 56 | //resources目录下对应工具拷贝到app/build/tools/目录下 57 | if (!file.exists()) { 58 | file.parentFile.mkdirs() 59 | file.withOutputStream { 60 | def is = ImageOptimizationUtils.class.getResourceAsStream("/$name/$toolName") 61 | it << is.bytes 62 | is.close() 63 | } 64 | } 65 | if (file.exists() && file.setExecutable(true)) { 66 | return file.absolutePath 67 | } 68 | throw new GradleException("$tool :无法执行或者不存在") 69 | } 70 | 71 | def static checkOptionalParams(def defaultJpegCompressQuality, def defaultConvertWebpQuality, String appIconName, String convertWebpType, int minSdk, String compressPngType, String pluginStrategy, int pngCompressQuality) { 72 | if (defaultJpegCompressQuality < 84 || defaultJpegCompressQuality > 100) { 73 | throw new InvalidParameterException("JpegCompressQuality should be [84,100] , default value is 84 ,you set $defaultJpegCompressQuality") 74 | } 75 | if (defaultConvertWebpQuality < 0 || defaultConvertWebpQuality > 100) { 76 | throw new InvalidParameterException("ConvertWebpQuality should be [0,100] , default value is 75 ,you set $defaultConvertWebpQuality") 77 | } 78 | 79 | if (appIconName == null || appIconName.isEmpty()) { 80 | throw new InvalidParameterException("appIconName should not be empty ,you set $appIconName") 81 | } 82 | 83 | if (!convertWebpType.equalsIgnoreCase(ImageOptimizationConstants.LOSSY) 84 | && !convertWebpType.equalsIgnoreCase(ImageOptimizationConstants.LOSSLESS)) { 85 | throw new InvalidParameterException("convertWebpType error. Please use lossy or lossless, default is lossy ,you set $convertWebpType") 86 | } 87 | 88 | if (convertWebpType.equalsIgnoreCase(ImageOptimizationConstants.LOSSLESS) && minSdk < 18) { 89 | throw new InvalidParameterException("convertWebpType lossless type only support minsdk >=18 ,you set $minSdk") 90 | } 91 | 92 | if (!compressPngType.equalsIgnoreCase(ImageOptimizationConstants.LOSSY) 93 | && !compressPngType.equalsIgnoreCase(ImageOptimizationConstants.LOSSLESS)) { 94 | throw new InvalidParameterException("compressPngType error. Please use lossy or lossless, default is lossy ,you set $compressPngType") 95 | } 96 | 97 | if (!pluginStrategy.equalsIgnoreCase(ImageOptimizationConstants.STRATEGY_ONLIY_COMPRESS) 98 | && !pluginStrategy.equalsIgnoreCase(ImageOptimizationConstants.STRATEGY_WEBP)) { 99 | throw new InvalidParameterException("pluginStrategy error. Please use webp or compress, default is compress ,you set $pluginStrategy") 100 | } 101 | 102 | if (pngCompressQuality < 0 || pngCompressQuality > 100){ 103 | throw new InvalidParameterException("pngCompressQuality should be [0,100] , default value is 80 ,you set $pngCompressQuality") 104 | } 105 | } 106 | 107 | //排除启动图标 108 | def static isNotLauncherIcon(File file, String appIconName, String appIconRoundName) { 109 | return (appIconRoundName == null || appIconRoundName.isEmpty()) ? 110 | (file.name != "${appIconName}.png") 111 | : (file.name != "${appIconName}.png" && file.name != "${appIconRoundName}.png") 112 | } 113 | 114 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/compressjpg/AbstractCompressJpg.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer.compressjpg 2 | 3 | import com.wanglei.optimizer.compressjpg.inter.ICompressJpg 4 | 5 | abstract class AbstractCompressJpg implements ICompressJpg{ 6 | 7 | @Override 8 | void compressJpg(Object jpgTool, Object project, Object file, Object compressJpgQuality) { 9 | long originalSize = file.length() 10 | def out = new File(file.parent,"temp-${file.name}") 11 | def result =commandLine(jpgTool,file,out,compressJpgQuality) .execute() 12 | result.waitForProcessOutput() 13 | if (result.exitValue() == 0){ 14 | long compressedSize = out.length() 15 | float rate = 1.0f * (originalSize - compressedSize) / originalSize * 100 16 | file.delete() 17 | out.renameTo(file) 18 | project.logger.error "compress jpg image ${file.absolutePath} success from ${originalSize}B down to ${compressedSize}B, ${rate}% saved!" 19 | }else{ 20 | project.logger.error "compress jpg image ${file.absolutePath} error : ${result.err.text}" 21 | } 22 | } 23 | 24 | abstract String commandLine(jpgTool,inFile,outFile,compressJpgQuality) 25 | 26 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/compressjpg/LosslessCompressJpg.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer.compressjpg 2 | 3 | class LosslessCompressJpg extends AbstractCompressJpg{ 4 | 5 | 6 | @Override 7 | String commandLine(Object jpgTool, Object file, Object out, Object jpegCompressQuality) { 8 | return null 9 | } 10 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/compressjpg/LossyCompressJpg.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer.compressjpg 2 | 3 | class LossyCompressJpg extends AbstractCompressJpg{ 4 | 5 | 6 | @Override 7 | String commandLine(Object jpgTool, Object file, Object out, Object jpegCompressQuality) { 8 | return "${jpgTool} --quality ${jpegCompressQuality} ${file.absolutePath} ${out.absolutePath}" 9 | } 10 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/compressjpg/factory/CompressJpgFactory.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer.compressjpg.factory 2 | 3 | import com.wanglei.optimizer.ImageOptimizationConstants 4 | import com.wanglei.optimizer.compressjpg.LosslessCompressJpg 5 | import com.wanglei.optimizer.compressjpg.LossyCompressJpg 6 | import com.wanglei.optimizer.compressjpg.inter.ICompressJpg 7 | 8 | class CompressJpgFactory { 9 | 10 | private CompressJpgFactory() {} 11 | 12 | static ICompressJpg getCompressJpgTool(String type) { 13 | if (ImageOptimizationConstants.LOSSY .equalsIgnoreCase(type)) { 14 | return new LossyCompressJpg() 15 | } else if (ImageOptimizationConstants.LOSSLESS .equalsIgnoreCase(type)) { 16 | return new LosslessCompressJpg() 17 | } else { 18 | throw new IllegalArgumentException("compressJpgType error. Please use lossy or lossless, default is lossy") 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/compressjpg/inter/ICompressJpg.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer.compressjpg.inter 2 | 3 | interface ICompressJpg { 4 | 5 | void compressJpg(jpgTool, project, file, compressJpgQuality) 6 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/compresspng/AbstractCompressPng.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer.compresspng 2 | 3 | import com.wanglei.optimizer.compresspng.inter.ICompressPng 4 | 5 | abstract class AbstractCompressPng implements ICompressPng{ 6 | 7 | @Override 8 | void compressPng(Object pngTool, Object project, Object file, Object compressPngQuality) { 9 | long originalSize = file.length() 10 | def result = commandLine(pngTool,file,compressPngQuality).execute() 11 | result.waitForProcessOutput() 12 | if (result.exitValue() == 0){ 13 | long compressedSize = new File(file.absolutePath).length() 14 | float rate = 1.0f * (originalSize - compressedSize) / originalSize * 100 15 | project.logger.error "compress png image ${file.absolutePath} success from ${originalSize}B down to ${compressedSize}B, ${rate}% saved!" 16 | }else if (result.exitValue() == 98) {//有损压缩已经被压缩过则放弃压缩 17 | project.logger.error "Lossy Type skip compress ${file.absolutePath} has been compressed" 18 | }else if (result.exitValue() == 2) {//无损压缩已经被压缩过则放弃压缩 19 | project.logger.error "Lossless Type skip compress ${file.absolutePath} has been compressed" 20 | } else{ 21 | project.logger.error "compress png image ${file.absolutePath} error : ${result.err.text}" 22 | } 23 | } 24 | 25 | abstract String commandLine(pngTool,inFile,compressPngQuality) 26 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/compresspng/LosslessCompressPng.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer.compresspng 2 | 3 | 4 | class LosslessCompressPng extends AbstractCompressPng { 5 | 6 | @Override 7 | String commandLine(Object pngTool, Object file, Object compressPngQuality) { 8 | return "${pngTool} ${file.absolutePath}" 9 | } 10 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/compresspng/LossyCompressPng.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer.compresspng 2 | 3 | class LossyCompressPng extends AbstractCompressPng{ 4 | 5 | @Override 6 | String commandLine(Object pngTool, Object file, Object pngCompressQuality) { 7 | def suffix = file.name.substring(file.name.lastIndexOf("."), file.name.length()) 8 | //--quality min-max min and max are numbers in range 0 (worst) to 100 (perfect) 9 | //--speed N, -sN Speed/quality trade-off from 1 (brute-force) to 10 (fastest). 10 | //The default is 3. Speed 10 has 5% lower quality, but is 8 times faster than the default. 11 | return "${pngTool} -f --skip-if-larger --ext $suffix --quality ${pngCompressQuality}-${pngCompressQuality} --speed 1 ${file.absolutePath}" 12 | } 13 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/compresspng/factory/CompressPngFactory.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer.compresspng.factory 2 | 3 | import com.wanglei.optimizer.ImageOptimizationConstants 4 | import com.wanglei.optimizer.compresspng.LosslessCompressPng 5 | import com.wanglei.optimizer.compresspng.LossyCompressPng 6 | import com.wanglei.optimizer.compresspng.inter.ICompressPng 7 | 8 | class CompressPngFactory { 9 | 10 | private CompressPngFactory() {} 11 | 12 | static ICompressPng getCompressPngTool(String type) { 13 | if (ImageOptimizationConstants.LOSSY .equalsIgnoreCase(type)) { 14 | return new LossyCompressPng() 15 | } else if (ImageOptimizationConstants.LOSSLESS .equalsIgnoreCase(type)) { 16 | return new LosslessCompressPng() 17 | } else { 18 | throw new IllegalArgumentException("compressPngType error. Please use lossy or lossless, default is lossy") 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/compresspng/inter/ICompressPng.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer.compresspng.inter 2 | 3 | interface ICompressPng { 4 | 5 | void compressPng(pngTool, project, file, compressPngQuality) 6 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/convertwebp/AbstractConvertWebp.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer.convertwebp 2 | 3 | import com.wanglei.optimizer.convertwebp.inter.IConvertWebp 4 | import org.gradle.api.Project 5 | 6 | abstract class AbstractConvertWebp implements IConvertWebp { 7 | 8 | @Override 9 | void convertWebp(String webPTool, Project project, File file, List convertFailedList, int convertWebpQuality) { 10 | 11 | def name = file.name.substring(0,file.name.lastIndexOf(".")) 12 | def outPutFile = new File(file.parent, "${name}.webp") 13 | // 14 | def result = commandLine(webPTool,file,outPutFile,convertWebpQuality).execute() 15 | result.waitForProcessOutput() 16 | if (result.exitValue() == 0){ 17 | def orgFileLen = file.length()//没有转换前图片大小 18 | def outPutFileLen = outPutFile.length()//转为webp后图片大小 19 | if(orgFileLen > outPutFileLen){ 20 | file.delete() 21 | project.logger.error "convert webp ${file.absolutePath} success" 22 | }else { 23 | convertFailedList << file 24 | outPutFile.delete() 25 | project.logger.error "convert webp ${file.absolutePath} success but bigger than orgFile" 26 | } 27 | }else{ 28 | convertFailedList << file 29 | project.logger.error "convert webp ${file.absolutePath} error" 30 | } 31 | } 32 | 33 | abstract String commandLine(String webPTool,File inFile,File outFile,int convertWebpQuality) 34 | 35 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/convertwebp/LosslessConvertWebp.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer.convertwebp 2 | 3 | 4 | class LosslessConvertWebp extends AbstractConvertWebp{ 5 | 6 | @Override 7 | String commandLine(String webPTool, File inFile, File outFile, int convertWebpQuality) { 8 | return "$webPTool -lossless ${inFile.absolutePath} -o ${outFile.absolutePath}" 9 | } 10 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/convertwebp/LossyConvertWebp.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer.convertwebp 2 | 3 | 4 | class LossyConvertWebp extends AbstractConvertWebp { 5 | 6 | @Override 7 | String commandLine(String webPTool,File inFile, File outFile, int convertWebpQuality) { 8 | return "$webPTool -q ${convertWebpQuality} ${inFile.absolutePath} -o ${outFile.absolutePath}" 9 | } 10 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/convertwebp/factory/ConvertWebpFactory.groovy: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer.convertwebp.factory 2 | 3 | import com.wanglei.optimizer.ImageOptimizationConstants 4 | import com.wanglei.optimizer.convertwebp.LosslessConvertWebp 5 | import com.wanglei.optimizer.convertwebp.LossyConvertWebp 6 | import com.wanglei.optimizer.convertwebp.inter.IConvertWebp 7 | 8 | 9 | class ConvertWebpFactory{ 10 | 11 | private ConvertWebpFactory() {} 12 | 13 | static IConvertWebp getConvertWebpTool(String type) { 14 | if (ImageOptimizationConstants.LOSSY .equalsIgnoreCase(type)) { 15 | return new LossyConvertWebp() 16 | } else if (ImageOptimizationConstants.LOSSLESS .equalsIgnoreCase(type)) { 17 | return new LosslessConvertWebp() 18 | } else { 19 | throw new IllegalArgumentException("convertWebpType error. Please use lossy or lossless, default is lossy") 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /optimization/src/main/groovy/com/wanglei/optimizer/convertwebp/inter/IConvertWebp.java: -------------------------------------------------------------------------------- 1 | package com.wanglei.optimizer.convertwebp.inter; 2 | 3 | import org.gradle.api.Project; 4 | 5 | import java.io.File; 6 | import java.util.List; 7 | 8 | public interface IConvertWebp { 9 | 10 | void convertWebp(String webPTool, Project project, File file, List convertFailedList, int convertWebpQuality); 11 | } 12 | -------------------------------------------------------------------------------- /optimization/src/main/resources/META-INF/gradle-plugins/com.wanglei.image-optimization.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.wanglei.optimizer.ImageOptimizationPlugin -------------------------------------------------------------------------------- /optimization/src/main/resources/cwebp/cwebp_darwin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/optimization/src/main/resources/cwebp/cwebp_darwin -------------------------------------------------------------------------------- /optimization/src/main/resources/cwebp/cwebp_linux: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/optimization/src/main/resources/cwebp/cwebp_linux -------------------------------------------------------------------------------- /optimization/src/main/resources/cwebp/cwebp_win.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/optimization/src/main/resources/cwebp/cwebp_win.exe -------------------------------------------------------------------------------- /optimization/src/main/resources/guetzli/guetzli_darwin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/optimization/src/main/resources/guetzli/guetzli_darwin -------------------------------------------------------------------------------- /optimization/src/main/resources/guetzli/guetzli_linux: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/optimization/src/main/resources/guetzli/guetzli_linux -------------------------------------------------------------------------------- /optimization/src/main/resources/guetzli/guetzli_win.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/optimization/src/main/resources/guetzli/guetzli_win.exe -------------------------------------------------------------------------------- /optimization/src/main/resources/pngout/pngout_darwin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/optimization/src/main/resources/pngout/pngout_darwin -------------------------------------------------------------------------------- /optimization/src/main/resources/pngout/pngout_linux: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/optimization/src/main/resources/pngout/pngout_linux -------------------------------------------------------------------------------- /optimization/src/main/resources/pngout/pngout_win.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/optimization/src/main/resources/pngout/pngout_win.exe -------------------------------------------------------------------------------- /optimization/src/main/resources/pngquant/pngquant_darwin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/optimization/src/main/resources/pngquant/pngquant_darwin -------------------------------------------------------------------------------- /optimization/src/main/resources/pngquant/pngquant_linux: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/optimization/src/main/resources/pngquant/pngquant_linux -------------------------------------------------------------------------------- /optimization/src/main/resources/pngquant/pngquant_win.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/optimization/src/main/resources/pngquant/pngquant_win.exe -------------------------------------------------------------------------------- /optimization/upload2Jcenter.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.jfrog.bintray' 2 | apply plugin: 'maven-publish' 3 | //发布到jcenter的配置 4 | bintray { 5 | user = 'xxxxxx' 6 | key = 'xxxxxxxxxxxxxxxx' 7 | publications = ['plugin'] 8 | pkg { 9 | repo = 'AndroidImageOptimization' //仓库名 10 | name = 'optimization' //项目名 11 | licenses = ['Apache-2.0'] 12 | vcsUrl = 'https://github.com/MISAYAWANGLEI/ImageOptimization.git' 13 | version { 14 | desc = 'optimizer android image resource' 15 | released = new Date() 16 | vcsTag = '1.0'//版本控制的版本号 17 | //attributes = ['gradle-plugin': 'xxxxx'] 18 | } 19 | } 20 | } 21 | 22 | publishing{ 23 | publications{ 24 | plugin(MavenPublication){ 25 | from components.java 26 | artifactId 'optimization' 27 | artifact sourcesJar 28 | artifact docJar 29 | // 30 | pom.withXml{ 31 | def root = asNode() 32 | def licensesNode = root.appendNode('licenses').appendNode('license') 33 | licensesNode.appendNode('name','Apache License, Version 2.0') 34 | licensesNode.appendNode('url','https://www.apache.org/licenses/LICENSE-2.0.txt') 35 | licensesNode.appendNode('distribution','repo') 36 | licensesNode.appendNode('comments','A business-friendly OSS license') 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /optimization/upload2PrivateMaven.gradle: -------------------------------------------------------------------------------- 1 | //私服地址:http://www.sonatype.org/nexus/ 可用于公司搭建自己的私服 2 | //发布到maven私服配置 3 | apply plugin: 'maven-publish' 4 | 5 | publishing{ 6 | publications{ 7 | plugin(MavenPublication){ 8 | from components.java 9 | artifactId 'optimization' 10 | artifact sourcesJar 11 | artifact docJar 12 | // 13 | pom.withXml{ 14 | def root = asNode() 15 | def licensesNode = root.appendNode('licenses').appendNode('license') 16 | licensesNode.appendNode('name','Apache License, Version 2.0') 17 | licensesNode.appendNode('url','https://www.apache.org/licenses/LICENSE-2.0.txt') 18 | licensesNode.appendNode('distribution','repo') 19 | licensesNode.appendNode('comments','A business-friendly OSS license') 20 | } 21 | } 22 | } 23 | //配置上传仓库信息 24 | repositories{ 25 | maven{ 26 | name 'xxx'//私服仓库名字 27 | url 'http://localhost:8081/repository/xxx/'//私服地址 28 | credentials{ 29 | username = 'wanglei'//用户名 30 | password = '123456'//密码 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /orginalimage/cdc.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/orginalimage/cdc.PNG -------------------------------------------------------------------------------- /orginalimage/dewdw.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/orginalimage/dewdw.jpeg -------------------------------------------------------------------------------- /orginalimage/g.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/orginalimage/g.9.png -------------------------------------------------------------------------------- /orginalimage/h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/orginalimage/h.png -------------------------------------------------------------------------------- /orginalimage/i.JPEG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/orginalimage/i.JPEG -------------------------------------------------------------------------------- /orginalimage/j.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/orginalimage/j.JPG -------------------------------------------------------------------------------- /orginalimage/k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/orginalimage/k.png -------------------------------------------------------------------------------- /orginalimage/qw.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/orginalimage/qw.jpeg -------------------------------------------------------------------------------- /orginalimage/wece.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/orginalimage/wece.jpg -------------------------------------------------------------------------------- /orginalimage/wew.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MISAYAWANGLEI/ImageOptimization/387cbe23aa728249983aa343921b09ae550d9258/orginalimage/wew.jpeg -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':optimization', ':androidlibrary' 2 | -------------------------------------------------------------------------------- /updateLog.md: -------------------------------------------------------------------------------- 1 | >v1.0.0 2 | 3 | - 支持png/jpg图片有损或者无损转为webp 4 | - 支持png图片的有损或者无损压缩 5 | - 支持jpg图片的有损压缩 6 | 7 | 如选择优先转为webp图片,那么插件会将png/jpg图片优先转为webp图片 8 | ,不能转换或者转换失败或者转换后图片更大则执行压缩图片的操作。 9 | 10 | 如选择压缩图片模式,那么插件只会对png/jpg图片进行压缩 11 | 12 | >v2.0.0 13 | 14 | - 增加过滤图片不转换不压缩的功能,图片保持原样 --------------------------------------------------------------------------------