Chỉnh sửa độ trong suốt và độ tương phản của hình ảnh thông qua ColorMatrix
Bài đăng này đã không được cập nhật trong 5 năm
Hiện này các ứng dụng chỉnh sửa hình ảnh khá phổ biến trong android . Cũng có rất nhiều các thư viện hỗ trợ chúng ta phát triển những chức năng liên quan đến chỉnh sửa một hình ảnh thông qua ứng dụng android. Vì vậy trong bài viết chúng ta sẽ tìm hiểu cách xây dựng một tính năng của chỉnh sủa ảnh đó là thay đổi độ trong suốt cũng như độ tương phản của hình ảnh thông qua ColorMatrix và ColorMatrixColorFilter
Để hiểu rõ hơn chúng tao tìm hiểu một số điều căn bản về ColorMatrix nhé .
ColorMatrix là gì?
ColorMatrix là ma trận 4x5 để chuyển đổi các thành phần màu và alpha của Bitmap. Ma trận có thể được truyền dưới dạng một mảng và được xử lý như sau:
[a, b, c, d, e,
f, g, h, i, j,
k, l, m, n, o,
p, q, r, s, t]
Khi áp dụng cho một màu [R, G, B, A], màu kết quả được tính là:
R '= a * R + b * G + c * B + d * A + e;
G '= f * R + g * G + h * B + i * A + j;
B '= k * R + l * G + m * B + n * A + o;
A '= p * R + q * G + r * B + s * A + t;
Màu kết quả [R’, G’, B’, A’] đó sau đó có mỗi kênh được kẹp 0vào 255 phạm vi.
ColorMatrix mẫu bên dưới đảo ngược các màu đến bằng cách chia tỷ lệ từng kênh theo -1, sau đó dịch chuyển kết quả lên 255 để duy trì trong không gian màu tiêu chuẩn.
[-1, 0, 0, 0, 255,
0, -1, 0, 0, 255,
0, 0, -1, 0, 255,
0, 0, 0, 1, 0]
Hãy bắt đầu viết code
Chuẩn bị cho phương thức chỉnh sửa
Trước tiên ta tạo một file kotlin với tên là AdjustImageView.kt
class AdjustImageView {
companion object {
private val DEFAULT_VALUE = 0f
private val ONE_VALUE = 1f
fun changeBitmapImageView(
bitmap: Bitmap,
brightness: Float,
contrast: Float,
saturation: Float
): Bitmap {
//Matrix: 4x5
val colorMatrix = ColorMatrix(
floatArrayOf(
contrast, saturation, saturation, DEFAULT_VALUE, brightness,
DEFAULT_VALUE, contrast, DEFAULT_VALUE, DEFAULT_VALUE, brightness,
DEFAULT_VALUE, DEFAULT_VALUE, contrast, DEFAULT_VALUE, brightness,
DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, ONE_VALUE, DEFAULT_VALUE
)
)
val btp = Bitmap.createBitmap(bitmap.width, bitmap.height, bitmap.config)
val canvas = Canvas(btp)
val paint = Paint()
paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
canvas.drawBitmap(bitmap, DEFAULT_VALUE, DEFAULT_VALUE, paint)
return btp
}
}
}
Giải thích : Ở đây ta viết một phương thức là changeBitmapImageView() phương thức này với đối số truyền vào là một bitmap , brightness (chỉ số độ sáng ) , contrast ( chỉ số độ tương phản ) , saturation ( chỉ số bão hòa đỏ) và phương thức này sẽ trả về một bitmap để tao cập nhật lại imageview
Tiếp theo dựa vào các kiến thức cơ bản về ColorMatrix ta tạo một ma trận 4x5 với các chỉ số mặc định cũng như vị trí của các tham số truyền vô để có được một ma trận chuyển đổi màu cho hình ảnh
Chúng ta tạo một hai đối tượng Canvas và Paint để sẽ và tô màu hình ảnh ở dạng bitmap với các thuộc tính như :
paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
canvas.drawBitmap(bitmap, DEFAULT_VALUE, DEFAULT_VALUE, paint)
Tạo mã nguồn cho lớp giao diện
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<TextView
android:id="@+id/textViewCancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_16"
android:text="Cancel"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textViewSave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_16"
android:text="Save"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/imageViewAdjust"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="@dimen/dp_8"
app:layout_constraintBottom_toTopOf="@id/constraintLayout"
app:layout_constraintTop_toBottomOf="@id/textViewCancel" />
<android.support.constraint.ConstraintLayout
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_120"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/imageViewAdjust">
<TextView
android:id="@+id/textViewBrightness"
android:layout_width="@dimen/dp_100"
android:layout_height="wrap_content"
android:text="Brightness"
app:layout_constraintBottom_toTopOf="@+id/textViewContrast"
app:layout_constraintEnd_toEndOf="@+id/textViewContrast"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/textViewContrast"
app:layout_constraintTop_toTopOf="parent" />
<SeekBar
android:id="@+id/seekBarBrightness"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_8"
android:layout_marginStart="@dimen/dp_8"
android:max="200"
android:progress="100"
app:layout_constraintBottom_toBottomOf="@+id/textViewBrightness"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textViewBrightness"
app:layout_constraintTop_toTopOf="@+id/textViewBrightness" />
<TextView
android:id="@+id/textViewContrast"
android:layout_width="@dimen/dp_100"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dp_8"
android:layout_marginStart="@dimen/dp_8"
android:layout_marginTop="@dimen/dp_8"
android:text="Contrast"
app:layout_constraintBottom_toTopOf="@+id/textViewSaturation"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewBrightness" />
<SeekBar
android:id="@+id/seekBarContrast"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_8"
android:layout_marginStart="@dimen/dp_8"
android:max="255"
android:progress="125"
app:layout_constraintBottom_toBottomOf="@+id/textViewContrast"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textViewContrast"
app:layout_constraintTop_toTopOf="@+id/textViewContrast" />
<TextView
android:id="@+id/textViewSaturation"
android:layout_width="@dimen/dp_100"
android:layout_height="wrap_content"
android:text="Saturation"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/textViewContrast"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/textViewContrast"
app:layout_constraintTop_toBottomOf="@+id/textViewContrast" />
<SeekBar
android:id="@+id/seekBarSaturation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dp_8"
android:layout_marginEnd="@dimen/dp_8"
android:layout_marginStart="@dimen/dp_8"
android:max="30"
android:progress="0"
app:layout_constraintBottom_toBottomOf="@+id/textViewSaturation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textViewSaturation"
app:layout_constraintTop_toTopOf="@+id/textViewSaturation" />
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
Chúng ta bắt đầu viết code cho Mainactivity.kt
class MainActivity : AppCompatActivity(), SeekBar.OnSeekBarChangeListener {
var bitmap: Bitmap? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bitmap = (imageViewAdjust.drawable as BitmapDrawable).bitmap
initView()
}
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
val brightness = seekBarBrightness.progress - BRIGHTNESS
val contrast = seekBarContrast.progress / CONTRAST
val saturation = seekBarSaturation.progress * STATURATION
imageViewAdjust.setImageBitmap(
AdjustImageView.changeBitmapImageView(
bitmap!!,
brightness,
contrast,
saturation
)
)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
}
private fun initView() {
imageViewAdjust.setImageBitmap(bitmap)
seekBarBrightness.setOnSeekBarChangeListener(this)
seekBarContrast.setOnSeekBarChangeListener(this)
seekBarSaturation.setOnSeekBarChangeListener(this)
}
companion object {
private val BRIGHTNESS = 100f
private val CONTRAST = 170f
private val STATURATION = .10f
}
}
Ở đây chúng tạo một biến bitmap và gắn nó bằng bitmap của imageview để chuyền vào phương thức để chỉnh sửa hình ảnh . Đồng thời cũng implement SeekBar.OnSeekBarChangeListener để bắt event lấy được các chỉ số độ sáng , độ tương phản của seekBar . Sau khi có được bitmap chỉnh sủa thì ta có thể cập nhật lại ImageView với câu lệnh sau :
imageViewAdjust.setImageBitmap(
AdjustImageView.changeBitmapImageView(
bitmap!!,
brightness,
contrast,
saturation
)
)
Chạy chương trình và xem thành quả nào:
STATURATION
CONTRAST
BRIGHTNESS
Chúc các bạn thành công.
All rights reserved