diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d06b28..adb19a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ Change Log ========== +Version 1.4.0 *(2016-02-28)* +---------------------------- + +fix Issue [#29](https://github.com/wasabeef/glide-transformations/issues/29) + Use FastBlur as a fallback upon RenderScript failure. + Version 1.3.1 *(2015-11-27)* ---------------------------- diff --git a/README.md b/README.md index f87509b..2140339 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ repositories { } dependencies { - compile 'jp.wasabeef:glide-transformations:1.3.1' + compile 'jp.wasabeef:glide-transformations:1.4.0' // If you want to use the GPU Filters compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.3.0' } @@ -97,10 +97,6 @@ android { `SwirlFilterTransformation`, `BrightnessFilterTransformation`, `KuwaharaFilterTransformation` `VignetteFilterTransformation` -### Proguard -``` --dontwarn jp.co.cyberagent.android.gpuimage.** -``` Applications using Glide Transformations --- diff --git a/build.gradle b/build.gradle index 1c6b81d..d4bf2f1 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0-alpha1' + classpath 'com.android.tools.build:gradle:2.0.0-beta6' classpath 'com.novoda:bintray-release:0.3.4' // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle.properties b/gradle.properties index 533c754..b0550a2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.3.1 +VERSION_NAME=1.4.0 GROUP=jp.wasabeef ARTIFACT_ID=glide-transformations diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 07fc193..0ecc9c0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.11-all.zip diff --git a/transformations/build.gradle b/transformations/build.gradle index 3dddb8c..8955d0a 100644 --- a/transformations/build.gradle +++ b/transformations/build.gradle @@ -14,6 +14,8 @@ android { // Warning:Renderscript support mode is not currently supported with renderscript target 21+ renderscriptTargetApi RENDERSCRIPT_TARGET_API as int renderscriptSupportModeEnabled true + + consumerProguardFiles 'proguard-rules.txt' } } @@ -22,6 +24,26 @@ dependencies { provided "jp.co.cyberagent.android.gpuimage:gpuimage-library:${GPUIMAGE_VERSION}" } +task androidJavadocs(type: Javadoc) { + source = android.sourceSets.main.java.srcDirs + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) +} + +task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { + classifier = 'javadoc' + from androidJavadocs.destinationDir +} + +task androidSourcesJar(type: Jar) { + classifier = 'sources' + from android.sourceSets.main.java.srcDirs +} + +artifacts { + archives androidSourcesJar + archives androidJavadocsJar +} + publish { userOrg = POM_DEVELOPER_ID groupId = GROUP diff --git a/transformations/proguard-rules.txt b/transformations/proguard-rules.txt new file mode 100644 index 0000000..3fbf1d4 --- /dev/null +++ b/transformations/proguard-rules.txt @@ -0,0 +1,7 @@ +-keepclasseswithmembernames class * { + native ; +} + +-keep class android.support.v8.renderscript.** { *; } + +-dontwarn jp.co.cyberagent.android.gpuimage.** \ No newline at end of file diff --git a/transformations/src/main/java/jp/wasabeef/glide/transformations/BlurTransformation.java b/transformations/src/main/java/jp/wasabeef/glide/transformations/BlurTransformation.java index e3e8124..0690bbe 100644 --- a/transformations/src/main/java/jp/wasabeef/glide/transformations/BlurTransformation.java +++ b/transformations/src/main/java/jp/wasabeef/glide/transformations/BlurTransformation.java @@ -22,6 +22,7 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.support.v8.renderscript.Allocation; import android.support.v8.renderscript.Element; +import android.support.v8.renderscript.RSRuntimeException; import android.support.v8.renderscript.RenderScript; import android.support.v8.renderscript.ScriptIntrinsicBlur; import com.bumptech.glide.Glide; @@ -29,6 +30,7 @@ import com.bumptech.glide.load.Transformation; import com.bumptech.glide.load.engine.Resource; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; import com.bumptech.glide.load.resource.bitmap.BitmapResource; +import jp.wasabeef.glide.transformations.internal.FastBlur; public class BlurTransformation implements Transformation { @@ -88,18 +90,26 @@ public class BlurTransformation implements Transformation { paint.setFlags(Paint.FILTER_BITMAP_FLAG); canvas.drawBitmap(source, 0, 0, paint); - RenderScript rs = RenderScript.create(mContext); - Allocation input = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE, - Allocation.USAGE_SCRIPT); - Allocation output = Allocation.createTyped(rs, input.getType()); - ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); + RenderScript rs = null; + try { + rs = RenderScript.create(mContext); + Allocation input = + Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE, + Allocation.USAGE_SCRIPT); + Allocation output = Allocation.createTyped(rs, input.getType()); + ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); - blur.setInput(input); - blur.setRadius(mRadius); - blur.forEach(output); - output.copyTo(bitmap); - - rs.destroy(); + blur.setInput(input); + blur.setRadius(mRadius); + blur.forEach(output); + output.copyTo(bitmap); + } catch (RSRuntimeException e) { + bitmap = FastBlur.doBlur(bitmap, mRadius, true); + } finally { + if (rs != null) { + rs.destroy(); + } + } return BitmapResource.obtain(bitmap, mBitmapPool); } diff --git a/transformations/src/main/java/jp/wasabeef/glide/transformations/internal/FastBlur.java b/transformations/src/main/java/jp/wasabeef/glide/transformations/internal/FastBlur.java new file mode 100644 index 0000000..f8abca9 --- /dev/null +++ b/transformations/src/main/java/jp/wasabeef/glide/transformations/internal/FastBlur.java @@ -0,0 +1,257 @@ +package jp.wasabeef.glide.transformations.internal; + +import android.graphics.Bitmap; + +/** + * Copyright (C) 2015 Wasabeef + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class FastBlur { + + public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) { + + // Stack Blur v1.0 from + // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html + // + // Java Author: Mario Klingemann + // http://incubator.quasimondo.com + // created Feburary 29, 2004 + // Android port : Yahel Bouaziz + // http://www.kayenko.com + // ported april 5th, 2012 + + // This is a compromise between Gaussian Blur and Box blur + // It creates much better looking blurs than Box Blur, but is + // 7x faster than my Gaussian Blur implementation. + // + // I called it Stack Blur because this describes best how this + // filter works internally: it creates a kind of moving stack + // of colors whilst scanning through the image. Thereby it + // just has to add one new block of color to the right side + // of the stack and remove the leftmost color. The remaining + // colors on the topmost layer of the stack are either added on + // or reduced by one, depending on if they are on the right or + // on the left side of the stack. + // + // If you are using this algorithm in your code please add + // the following line: + // + // Stack Blur Algorithm by Mario Klingemann + + Bitmap bitmap; + if (canReuseInBitmap) { + bitmap = sentBitmap; + } else { + bitmap = sentBitmap.copy(sentBitmap.getConfig(), true); + } + + if (radius < 1) { + return (null); + } + + int w = bitmap.getWidth(); + int h = bitmap.getHeight(); + + int[] pix = new int[w * h]; + bitmap.getPixels(pix, 0, w, 0, 0, w, h); + + int wm = w - 1; + int hm = h - 1; + int wh = w * h; + int div = radius + radius + 1; + + int r[] = new int[wh]; + int g[] = new int[wh]; + int b[] = new int[wh]; + int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; + int vmin[] = new int[Math.max(w, h)]; + + int divsum = (div + 1) >> 1; + divsum *= divsum; + int dv[] = new int[256 * divsum]; + for (i = 0; i < 256 * divsum; i++) { + dv[i] = (i / divsum); + } + + yw = yi = 0; + + int[][] stack = new int[div][3]; + int stackpointer; + int stackstart; + int[] sir; + int rbs; + int r1 = radius + 1; + int routsum, goutsum, boutsum; + int rinsum, ginsum, binsum; + + for (y = 0; y < h; y++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + for (i = -radius; i <= radius; i++) { + p = pix[yi + Math.min(wm, Math.max(i, 0))]; + sir = stack[i + radius]; + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + rbs = r1 - Math.abs(i); + rsum += sir[0] * rbs; + gsum += sir[1] * rbs; + bsum += sir[2] * rbs; + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + } + stackpointer = radius; + + for (x = 0; x < w; x++) { + + r[yi] = dv[rsum]; + g[yi] = dv[gsum]; + b[yi] = dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (y == 0) { + vmin[x] = Math.min(x + radius + 1, wm); + } + p = pix[yw + vmin[x]]; + + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[(stackpointer) % div]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi++; + } + yw += w; + } + for (x = 0; x < w; x++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + yp = -radius * w; + for (i = -radius; i <= radius; i++) { + yi = Math.max(0, yp) + x; + + sir = stack[i + radius]; + + sir[0] = r[yi]; + sir[1] = g[yi]; + sir[2] = b[yi]; + + rbs = r1 - Math.abs(i); + + rsum += r[yi] * rbs; + gsum += g[yi] * rbs; + bsum += b[yi] * rbs; + + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + + if (i < hm) { + yp += w; + } + } + yi = x; + stackpointer = radius; + for (y = 0; y < h; y++) { + // Preserve alpha channel: ( 0xff000000 & pix[yi] ) + pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (x == 0) { + vmin[y] = Math.min(y + r1, hm) * w; + } + p = x + vmin[y]; + + sir[0] = r[p]; + sir[1] = g[p]; + sir[2] = b[p]; + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[stackpointer]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi += w; + } + } + + bitmap.setPixels(pix, 0, w, 0, 0, w, h); + + return (bitmap); + } +} \ No newline at end of file