1 加载图片边框和圆角
package ###
import android.content.Context
import android.graphics.*
import androidx.annotation.ColorInt
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import com.bumptech.glide.util.Preconditions
import com.bumptech.glide.util.Util
import java.nio.ByteBuffer
import java.security.MessageDigest
import com.bumptech.glide.load.Key
class RoundBorderTransformation(private val radius: Float, borderWidth: Int, @ColorInt borderColor: Int) :
BitmapTransformation() {
private val borderWidth: Int
private val borderColor: Int
// Bitmap doesn't implement equals, so == and .equals are equivalent here.
override fun transform(
pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int
): Bitmap {
return GlideTransformationUtils.roundedCorners(
pool,
toTransform,
radius,
radius,
radius,
radius,
borderWidth.toFloat(),
borderColor
)
}
override fun equals(o: Any?): Boolean {
if (o is RoundBorderTransformation) {
return (borderWidth == o.borderWidth
&& borderColor == o.borderColor)
}
return false
}
override fun hashCode(): Int {
val hashCode = Util.hashCode(
ID.hashCode(),
Util.hashCode(borderWidth)
)
return Util.hashCode(borderColor, hashCode)
}
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
messageDigest.update(ID_BYTES)
val radiusData = ByteBuffer.allocate(8)
.putFloat(borderWidth.toFloat())
.putInt(borderColor)
.array()
messageDigest.update(radiusData)
}
companion object {
private const val VERSION = 1
private const val ID =
"com.yilahuo.driftbottle.loader.transform.CircleBorderTransformation.$VERSION"
private val ID_BYTES =
ID.toByteArray(Key.CHARSET)
}
/**
* Provide the radii to round the corners of the bitmap.
*/
init {
Preconditions.checkArgument(
borderWidth > 0,
"borderWidth must be more the 0."
)
this.borderWidth = borderWidth
this.borderColor = borderColor
}
}
package ###
import android.graphics.*
import android.os.Build
import androidx.annotation.ColorInt
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
import com.bumptech.glide.util.Synthetic
import java.util.*
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.Condition
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
object GlideTransformationUtils {
const val PAINT_FLAGS =
Paint.DITHER_FLAG or Paint.FILTER_BITMAP_FLAG
private const val CIRCLE_CROP_PAINT_FLAGS =
PAINT_FLAGS or Paint.ANTI_ALIAS_FLAG
private val CIRCLE_CROP_SHAPE_PAINT =
Paint(CIRCLE_CROP_PAINT_FLAGS)
private var CIRCLE_CROP_BITMAP_PAINT: Paint
// See #738.
private val MODELS_REQUIRING_BITMAP_LOCK: Set<String> =
HashSet(
Arrays.asList( // Moto X gen 2
"XT1085",
"XT1092",
"XT1093",
"XT1094",
"XT1095",
"XT1096",
"XT1097",
"XT1098", // Moto G gen 1
"XT1031",
"XT1028",
"XT937C",
"XT1032",
"XT1008",
"XT1033",
"XT1035",
"XT1034",
"XT939G",
"XT1039",
"XT1040",
"XT1042",
"XT1045", // Moto G gen 2
"XT1063",
"XT1064",
"XT1068",
"XT1069",
"XT1072",
"XT1077",
"XT1078",
"XT1079"
)
)
/**
* https://github.com/bumptech/glide/issues/738 On some devices, bitmap drawing is not thread
* safe. This lock only locks for these specific devices. For other types of devices the lock is
* always available and therefore does not impact performance
*/
private val BITMAP_DRAWABLE_LOCK =
if (MODELS_REQUIRING_BITMAP_LOCK.contains(Build.MODEL)) ReentrantLock() else NoLock()
/**
* Creates a bitmap from a source bitmap and rounds the corners, applying a potentially different
* [X, Y] radius to each corner.
*
*
* This method does *NOT* resize the given [Bitmap], it only rounds it's corners.
* To both resize and round the corners of an image, consider [ ][com.bumptech.glide.request.RequestOptions.transform] and/or [ ].
*
* @param inBitmap the source bitmap to use as a basis for the created bitmap.
* @param topLeft top-left radius
* @param topRight top-right radius
* @param bottomRight bottom-right radius
* @param bottomLeft bottom-left radius
* @param borderWidth
* @param borderColor
* @return a [Bitmap] similar to inBitmap but with rounded corners.
*/
@JvmStatic
fun roundedCorners(
pool: BitmapPool,
inBitmap: Bitmap,
topLeft: Float,
topRight: Float,
bottomRight: Float,
bottomLeft: Float,
borderWidth: Float,
borderColor: Int
): Bitmap {
return roundedBorderCorners(
pool,
inBitmap,
borderWidth,
borderColor,
object : DrawRoundedCornerFn {
override fun drawRoundedCorners(canvas: Canvas?, paint: Paint?, rect: RectF?) {
if(paint == null || rect == null) {
return
}
val path = Path()
path.addRoundRect(
rect, floatArrayOf(
topLeft + 4,
topLeft + 4,
topRight + 4,
topRight + 4,
bottomRight + 4,
bottomRight + 4,
bottomLeft + 4,
bottomLeft + 4
),
Path.Direction.CW
)
canvas?.drawPath(path, paint)
}
},
object : DrawRoundedBorderCornerFn {
override fun drawRoundedCorners(canvas: Canvas?, paint: Paint?, rect: RectF?) {
if(paint == null || rect == null) {
return
}
val path = Path()
path.addRoundRect(
rect, floatArrayOf(
topLeft,
topLeft,
topRight,
topRight,
bottomRight,
bottomRight,
bottomLeft,
bottomLeft
),
Path.Direction.CW
)
canvas?.drawPath(path, paint)
}
}
)
}
/**
* 圆角边框
*
* @param pool
* @param inBitmap
* @param borderWidth
* @param borderColor
* @param drawRoundedCornerFn
* @param roundedBorderCornerFn
* @return
*/
private fun roundedBorderCorners(
pool: BitmapPool,
inBitmap: Bitmap,
borderWidth: Float,
@ColorInt borderColor: Int,
drawRoundedCornerFn: DrawRoundedCornerFn,
roundedBorderCornerFn: DrawRoundedBorderCornerFn
): Bitmap {
val halfBorder = borderWidth / 2
// Alpha is required for this transformation.
val safeConfig =
getAlphaSafeConfig(inBitmap)
val toTransform =
getAlphaSafeBitmap(pool, inBitmap)
val result =
pool[toTransform.width, toTransform.height, safeConfig]
result.setHasAlpha(true)
val shader = BitmapShader(
toTransform,
Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP
)
val paint = Paint()
paint.isAntiAlias = true
paint.shader = shader
val rect = RectF(0f, 0f, result.width.toFloat(), result.height.toFloat())
val borderPaint =
Paint(Paint.ANTI_ALIAS_FLAG) //设置边框样式
borderPaint.color = borderColor
borderPaint.style = Paint.Style.STROKE
borderPaint.strokeWidth = borderWidth
borderPaint.isAntiAlias = true
val borderRect = RectF(
halfBorder,
halfBorder,
result.width - halfBorder,
result.height - halfBorder
)
BITMAP_DRAWABLE_LOCK.lock()
try {
val canvas = Canvas(result)
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
drawRoundedCornerFn.drawRoundedCorners(canvas, paint, rect)
roundedBorderCornerFn.drawRoundedCorners(canvas, borderPaint, borderRect)
clear(canvas)
} finally {
BITMAP_DRAWABLE_LOCK.unlock()
}
if (toTransform != inBitmap) {
pool.put(toTransform)
}
return result
}
/**
* Crop the image to a circle and resize to the specified width/height. The circle crop will have
* the same width and height equal to the min-edge of the result image.
*
* @param pool The BitmapPool obtain a bitmap from.
* @param inBitmap The Bitmap to resize.
* @param destWidth The width in pixels of the final Bitmap.
* @param destHeight The height in pixels of the final Bitmap.
* @param borderWidth
* @param borderColor
* @return The resized Bitmap (will be recycled if recycled is not null).
*/
@JvmStatic
fun circleCrop(
pool: BitmapPool,
inBitmap: Bitmap,
destWidth: Int,
destHeight: Int,
borderWidth: Float,
@ColorInt borderColor: Int
): Bitmap {
val destMinEdge = Math.min(destWidth, destHeight)
val radius = destMinEdge / 2f
val srcWidth = inBitmap.width
val srcHeight = inBitmap.height
val scaleX = destMinEdge / srcWidth.toFloat()
val scaleY = destMinEdge / srcHeight.toFloat()
val maxScale = Math.max(scaleX, scaleY)
val scaledWidth = maxScale * srcWidth
val scaledHeight = maxScale * srcHeight
val left = (destMinEdge - scaledWidth) / 2f
val top = (destMinEdge - scaledHeight) / 2f
val destRect = RectF(left, top, left + scaledWidth, top + scaledHeight)
// Alpha is required for this transformation.
val toTransform =
getAlphaSafeBitmap(pool, inBitmap)
val outConfig =
getAlphaSafeConfig(inBitmap)
val result = pool[destMinEdge, destMinEdge, outConfig]
result.setHasAlpha(true)
//设置边框样式
val borderPaint =
Paint(Paint.ANTI_ALIAS_FLAG)
borderPaint.isAntiAlias = true
borderPaint.isFilterBitmap = true
borderPaint.color = borderColor
borderPaint.style = Paint.Style.STROKE
borderPaint.strokeWidth = borderWidth
BITMAP_DRAWABLE_LOCK.lock()
try {
val canvas = Canvas(result)
// Draw a circle
canvas.drawCircle(
radius,
radius,
radius,
CIRCLE_CROP_SHAPE_PAINT
)
// Draw the bitmap in the circle
canvas.drawBitmap(
toTransform,
null,
destRect,
CIRCLE_CROP_BITMAP_PAINT
)
// draw border
canvas.drawCircle(
destRect.centerX(),
destRect.centerY(),
(destRect.width() - borderWidth) / 2,
borderPaint
)
clear(canvas)
} finally {
BITMAP_DRAWABLE_LOCK.unlock()
}
if (toTransform != inBitmap) {
pool.put(toTransform)
}
return result
}
@JvmStatic
fun roundRectCrop(
pool: BitmapPool,
inBitmap: Bitmap,
destWidth: Int,
destHeight: Int,
borderWidth: Float,
@ColorInt borderColor: Int
): Bitmap {
val destMinEdge = Math.min(destWidth, destHeight)
val srcWidth = inBitmap.width
val srcHeight = inBitmap.height
val scaleX = destMinEdge / srcWidth.toFloat()
val scaleY = destMinEdge / srcHeight.toFloat()
val maxScale = Math.max(scaleX, scaleY)
val scaledWidth = maxScale * srcWidth
val scaledHeight = maxScale * srcHeight
val left = (destMinEdge - scaledWidth) / 2f
val top = (destMinEdge - scaledHeight) / 2f
val destRect = RectF(left, top, left + scaledWidth, top + scaledHeight)
// Alpha is required for this transformation.
val toTransform =
getAlphaSafeBitmap(pool, inBitmap)
val outConfig =
getAlphaSafeConfig(inBitmap)
val result = pool[destMinEdge, destMinEdge, outConfig]
result.setHasAlpha(true)
//设置边框样式
val borderPaint =
Paint(Paint.ANTI_ALIAS_FLAG)
borderPaint.isAntiAlias = true
borderPaint.isFilterBitmap = true
borderPaint.color = borderColor
borderPaint.style = Paint.Style.STROKE
borderPaint.strokeWidth = borderWidth
BITMAP_DRAWABLE_LOCK.lock()
try {
val canvas = Canvas(result)
// Draw a roundRect
canvas.drawRoundRect(
destRect,
8f,
8f,
CIRCLE_CROP_SHAPE_PAINT
)
// Draw the bitmap in the roundRect
canvas.drawBitmap(
toTransform,
null,
destRect,
CIRCLE_CROP_BITMAP_PAINT
)
// 绘制圆角边框(替代圆形边框)
if (borderWidth > 0) {
val borderRect = RectF(
borderWidth / 2,
borderWidth / 2,
destMinEdge - borderWidth / 2,
destMinEdge - borderWidth / 2
)
canvas.drawRoundRect(
borderRect,
8f,
8f,
borderPaint
)
}
clear(canvas)
} finally {
BITMAP_DRAWABLE_LOCK.unlock()
}
if (toTransform != inBitmap) {
pool.put(toTransform)
}
return result
}
// Avoids warnings in M+.
private fun clear(canvas: Canvas) {
canvas.setBitmap(null)
}
fun getAlphaSafeConfig(inBitmap: Bitmap): Bitmap.Config {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Avoid short circuiting the sdk check.
if (Bitmap.Config.RGBA_F16 == inBitmap.config) { // NOPMD
return Bitmap.Config.RGBA_F16
}
}
return Bitmap.Config.ARGB_8888
}
fun getAlphaSafeBitmap(
pool: BitmapPool, maybeAlphaSafe: Bitmap
): Bitmap {
val safeConfig =
getAlphaSafeConfig(maybeAlphaSafe)
if (safeConfig == maybeAlphaSafe.config) {
return maybeAlphaSafe
}
val argbBitmap =
pool[maybeAlphaSafe.width, maybeAlphaSafe.height, safeConfig]
Canvas(argbBitmap).drawBitmap(maybeAlphaSafe, 0f, 0f, null /*paint*/)
// We now own this Bitmap. It's our responsibility to replace it in the pool outside this method
// when we're finished with it.
return argbBitmap
}
/**
* Convenience function for drawing a rounded bitmap.
*/
private interface DrawRoundedCornerFn {
fun drawRoundedCorners(
canvas: Canvas?,
paint: Paint?,
rect: RectF?
)
}
private interface DrawRoundedBorderCornerFn {
fun drawRoundedCorners(
canvas: Canvas?,
paint: Paint?,
rect: RectF?
)
}
private class NoLock @Synthetic internal constructor() :
Lock {
override fun lock() {
// do nothing
}
@Throws(InterruptedException::class)
override fun lockInterruptibly() {
// do nothing
}
override fun tryLock(): Boolean {
return true
}
@Throws(InterruptedException::class)
override fun tryLock(
time: Long,
unit: TimeUnit
): Boolean {
return true
}
override fun unlock() {
// do nothing
}
override fun newCondition(): Condition {
throw UnsupportedOperationException("Should not be called")
}
}
init {
CIRCLE_CROP_BITMAP_PAINT = Paint(CIRCLE_CROP_PAINT_FLAGS)
CIRCLE_CROP_BITMAP_PAINT.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
}
}
用法:
Glide.with(it).load(url)
.error(placeHolder)
.placeholder(placeHolder)
.apply(RequestOptions().transform(
CenterCrop(), RoundBorderTransformation(
DimenUtils.dp2px(it, 6f)
.toFloat(),
it.getColor(R.color.red)
)
)