Android 保留一张位图作为改变色调、对比度、饱和度等的来源
Android keep one Bitmap as source for changing hue, contrast, saturation etc
我正在我的 android 应用程序中开发一项功能,用户可以使用 SeekBar 修改位图的饱和度、色调、对比度等。我为每个处理写了不同的方法。这单独工作正常,但如果我更改位图的色调并在调用任何其他方法(例如饱和度)之前,我想保留具有应用色调进度的位图等等。如果进度设置为默认,用户应该能够看到原始位图。
例如,
如果我将位图的色调值更改为 200。现在如果我想编辑饱和度,新的饱和度位图应该应用色调。如果我将饱和度和色调进度重置为默认值,那么用户应该能够看到原始位图颜色。
我尝试了很多方法来实现这种行为,例如在每次方法调用之前复制像素,但这没有帮助。目前,我只是在用户单击每种位图处理类型时将修改后的位图的像素复制到原始位图,但是如果我重置所有值,这不会将位图更改为原始颜色。请看我的代码。
希望这有助于理解问题。
private Bitmap wallpaper, modifiedWallpaper;
private ImageView imageView;
private ImageButton contrast;
private ImageButton hue;
private ImageButton saturation;
private float contrastProgress;
private float hueProgress;
private float satProgress;
private boolean isBlur, isBright, isContrast;
private boolean isHue, isSaturation, isRGB;
private TextView currentMode;
private SeekBar seekBar;
private Paint paint;
private Canvas canvas;
private CardView editor;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_customize_wallpaper, container, false);
imageView = view.findViewById(R.id.wallpaper);
rgbContainer = view.findViewById(R.id.rgb);
close = view.findViewById(R.id.close);
currentMode = view.findViewById(R.id.current_mode);
seekBar = view.findViewById(R.id.seekBar);
seekBar.setOnSeekBarChangeListener(seekBarChangeListener);
contrast = view.findViewById(R.id.contrast);
hue = view.findViewById(R.id.hue);
saturation = view.findViewById(R.id.saturation);
contrast.setOnClickListener(this);
hue.setOnClickListener(this);
saturation.setOnClickListener(this);
satProgress = 256f;
loadWallpaper(final String wallpaperUrl);
return view;
}
private void loadWallpaper(final String wallpaperUrl) {
Picasso.get().load(Constants.DOMAIN + wallpaperUrl).into(new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
wallpaper = bitmap; //original loaded
modifiedWallpaper = wallpaper.copy(Bitmap.Config.ARGB_8888, true);
imageView.setImageBitmap(wallpaper);
}
@Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.contrast : {
wallpaper = modifiedWallpaper.copy(Bitmap.Config.ARGB_8888, true);
isBlur = false;
isBright = false;
isContrast = true;
isHue = false;
isSaturation = false;
isRGB = false;
seekBar.setVisibility(View.VISIBLE);
rgbContainer.setVisibility(View.GONE);
changeIconTint();
seekBar.setOnSeekBarChangeListener(null);
seekBar.setMax(90);
seekBar.setProgress((int) contrastProgress);
seekBar.setOnSeekBarChangeListener(seekBarChangeListener);
currentMode.setText("Contrast");
break;
}
case R.id.hue : {
wallpaper = modifiedWallpaper.copy(Bitmap.Config.ARGB_8888, true);
isBlur = false;
isBright = false;
isContrast = false;
isHue = true;
isSaturation = false;
isRGB = false;
seekBar.setVisibility(View.VISIBLE);
rgbContainer.setVisibility(View.GONE);
changeIconTint();
seekBar.setOnSeekBarChangeListener(null);
seekBar.setMax(180);
seekBar.setProgress((int) hueProgress);
seekBar.setOnSeekBarChangeListener(seekBarChangeListener);
currentMode.setText("Hue");
break;
}
case R.id.saturation : {
wallpaper = modifiedWallpaper.copy(Bitmap.Config.ARGB_8888, true);
isBlur = false;
isBright = false;
isContrast = false;
isHue = false;
isSaturation = true;
isRGB = false;
seekBar.setVisibility(View.VISIBLE);
rgbContainer.setVisibility(View.GONE);
changeIconTint();
seekBar.setOnSeekBarChangeListener(null);
seekBar.setMax(512);
seekBar.setProgress((int) satProgress);
seekBar.setOnSeekBarChangeListener(seekBarChangeListener);
currentMode.setText("Saturation");
break;
}
}
}
private void changeContrast(float contrastProgress) {
float cb = this.contrastProgress == 0 ? 0 : -(this.contrastProgress / 1.8f) * 5;
ColorMatrix cm = new ColorMatrix(new float[]
{
contrastProgress, 0, 0, 0, cb,
0, contrastProgress, 0, 0, cb,
0, 0, contrastProgress, 0, cb,
0, 0, 0, contrastProgress, 0
});
paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(cm));
modifiedWallpaper = wallpaper.copy(Bitmap.Config.ARGB_8888, true);
canvas = new Canvas(modifiedWallpaper);
canvas.drawBitmap(modifiedWallpaper, 0, 0, paint);
imageView.setImageBitmap(modifiedWallpaper);
}
private void changeHue() {
ColorMatrix cm = new ColorMatrix();
float hueProgress = cleanValue() / 90f * (float) Math.PI;
if (hueProgress == 0){
return;
}
float cosVal = (float) Math.cos(hueProgress);
float sinVal = (float) Math.sin(hueProgress);
float lumR = 0.213f;
float lumG = 0.715f;
float lumB = 0.072f;
float[] mat = new float[]
{//logic };
cm.postConcat(new ColorMatrix(mat));
paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(cm));
modifiedWallpaper = wallpaper.copy(Bitmap.Config.ARGB_8888, true);
canvas = new Canvas(modifiedWallpaper);
canvas.drawBitmap(modifiedWallpaper, 0, 0, paint);
imageView.setImageBitmap(modifiedWallpaper);
}
private void changeSaturation() {
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(satProgress / 256);
paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
modifiedWallpaper = wallpaper.copy(Bitmap.Config.ARGB_8888, true);
canvas = new Canvas(modifiedWallpaper);
canvas.drawBitmap(modifiedWallpaper, 0, 0, paint);
imageView.setImageBitmap(modifiedWallpaper);
}
private SeekBar.OnSeekBarChangeListener seekBarChangeListener = new SeekBar.OnSeekBarChangeListener()
{
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if (isContrast) {
contrastProgress = seekBar.getProgress();
Log.d("Contrast", "" + (int) contrastProgress);
changeContrast((float) (seekBar.getProgress() + 10) / 10);
return;
}
if (isHue) {
hueProgress = seekBar.getProgress();
Log.d("Hue", "" + (int) hueProgress);
changeHue();
return;
}
if (isSaturation) {
satProgress = seekBar.getProgress();
Log.d("Saturation", "" + (int) satProgress);
changeSaturation();
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
};
您在应用过滤器时没有保存状态。您创建了一个 new Paint
和一个 new ColorMatrix
和一个 new Bitmap
。这就是为什么一个过滤器会覆盖前一个过滤器的原因。
有几种方法可以解决这个问题。我认为第一步是将 "filtering" 与 "drawing" 功能分开。示例:代码中的 changeSaturation()
不仅会改变饱和度,还会绘制到屏幕上。
以下是我的做法:
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if (isContrast) contrast = (float) (seekBar.getProgress() + 10) / 10
if (isHue) hue = seekBar.getProgress();
if (isSaturation) saturation = seekBar.getProgress();
ColorMatrix matrix = calculateColorMatrix(contrast, hue, saturation);
drawBitmap(matrix);
}
private void drawBitmap(ColorMatrix colorMatrix) {
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
modifiedWallpaper = wallpaper.copy(Bitmap.Config.ARGB_8888, true);
canvas = new Canvas(modifiedWallpaper);
canvas.drawBitmap(modifiedWallpaper, 0, 0, paint);
imageView.setImageBitmap(modifiedWallpaper);
}
private ColorMatrix calculateColorMatrix(float contrast, hue, saturation) {
ColorMatrix colorMatrix = new ColorMatrix();
// saturation
colorMatrix.setSaturation(satProgress / 256);
// hue
float cosVal = (float) Math.cos(hueProgress);
float sinVal = (float) Math.sin(hueProgress);
float lumR = 0.213f;
// logic...
colorMatrix.postConcat(new ColorMatrix(mat));
//contrast
// logic...
colorMatrix.postConcat(new ColorMatrix(new float[]
{
contrastProgress, 0, 0, 0, cb,
0, contrastProgress, 0, 0, cb,
0, 0, contrastProgress, 0, cb,
0, 0, 0, contrastProgress, 0
}));
return colorMatrix;
}
这只是其中一种方法。可能是最简单的一个。如果您不想在滑块每次移动时都创建所有过滤器,您可以保留三个不同的 matrix
每个过滤器一个,每次只修改其中一个。
ColorMatrix hue = new ColorMatrix();
ColorMatrix sat = new ColorMatrix();
ColorMatrix con = new ColorMatrix();
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if (isContrast) {
con = //logic to create contrast matrix
}
if (isHue) {
hue //logic to create hue matrix
}
if (isSaturation) {
//...
sat.setSaturation(...)
}
ColorMatrix matrix = hue.postConcat(sat).postConcat(con);
drawBitmap(matrix);
}
我自己没有测试过这段代码,您可能需要进行一些更改,但我希望这能让您走上正确的方向!
我正在我的 android 应用程序中开发一项功能,用户可以使用 SeekBar 修改位图的饱和度、色调、对比度等。我为每个处理写了不同的方法。这单独工作正常,但如果我更改位图的色调并在调用任何其他方法(例如饱和度)之前,我想保留具有应用色调进度的位图等等。如果进度设置为默认,用户应该能够看到原始位图。
例如, 如果我将位图的色调值更改为 200。现在如果我想编辑饱和度,新的饱和度位图应该应用色调。如果我将饱和度和色调进度重置为默认值,那么用户应该能够看到原始位图颜色。
我尝试了很多方法来实现这种行为,例如在每次方法调用之前复制像素,但这没有帮助。目前,我只是在用户单击每种位图处理类型时将修改后的位图的像素复制到原始位图,但是如果我重置所有值,这不会将位图更改为原始颜色。请看我的代码。
希望这有助于理解问题。
private Bitmap wallpaper, modifiedWallpaper;
private ImageView imageView;
private ImageButton contrast;
private ImageButton hue;
private ImageButton saturation;
private float contrastProgress;
private float hueProgress;
private float satProgress;
private boolean isBlur, isBright, isContrast;
private boolean isHue, isSaturation, isRGB;
private TextView currentMode;
private SeekBar seekBar;
private Paint paint;
private Canvas canvas;
private CardView editor;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_customize_wallpaper, container, false);
imageView = view.findViewById(R.id.wallpaper);
rgbContainer = view.findViewById(R.id.rgb);
close = view.findViewById(R.id.close);
currentMode = view.findViewById(R.id.current_mode);
seekBar = view.findViewById(R.id.seekBar);
seekBar.setOnSeekBarChangeListener(seekBarChangeListener);
contrast = view.findViewById(R.id.contrast);
hue = view.findViewById(R.id.hue);
saturation = view.findViewById(R.id.saturation);
contrast.setOnClickListener(this);
hue.setOnClickListener(this);
saturation.setOnClickListener(this);
satProgress = 256f;
loadWallpaper(final String wallpaperUrl);
return view;
}
private void loadWallpaper(final String wallpaperUrl) {
Picasso.get().load(Constants.DOMAIN + wallpaperUrl).into(new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
wallpaper = bitmap; //original loaded
modifiedWallpaper = wallpaper.copy(Bitmap.Config.ARGB_8888, true);
imageView.setImageBitmap(wallpaper);
}
@Override
public void onBitmapFailed(Exception e, Drawable errorDrawable) {
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.contrast : {
wallpaper = modifiedWallpaper.copy(Bitmap.Config.ARGB_8888, true);
isBlur = false;
isBright = false;
isContrast = true;
isHue = false;
isSaturation = false;
isRGB = false;
seekBar.setVisibility(View.VISIBLE);
rgbContainer.setVisibility(View.GONE);
changeIconTint();
seekBar.setOnSeekBarChangeListener(null);
seekBar.setMax(90);
seekBar.setProgress((int) contrastProgress);
seekBar.setOnSeekBarChangeListener(seekBarChangeListener);
currentMode.setText("Contrast");
break;
}
case R.id.hue : {
wallpaper = modifiedWallpaper.copy(Bitmap.Config.ARGB_8888, true);
isBlur = false;
isBright = false;
isContrast = false;
isHue = true;
isSaturation = false;
isRGB = false;
seekBar.setVisibility(View.VISIBLE);
rgbContainer.setVisibility(View.GONE);
changeIconTint();
seekBar.setOnSeekBarChangeListener(null);
seekBar.setMax(180);
seekBar.setProgress((int) hueProgress);
seekBar.setOnSeekBarChangeListener(seekBarChangeListener);
currentMode.setText("Hue");
break;
}
case R.id.saturation : {
wallpaper = modifiedWallpaper.copy(Bitmap.Config.ARGB_8888, true);
isBlur = false;
isBright = false;
isContrast = false;
isHue = false;
isSaturation = true;
isRGB = false;
seekBar.setVisibility(View.VISIBLE);
rgbContainer.setVisibility(View.GONE);
changeIconTint();
seekBar.setOnSeekBarChangeListener(null);
seekBar.setMax(512);
seekBar.setProgress((int) satProgress);
seekBar.setOnSeekBarChangeListener(seekBarChangeListener);
currentMode.setText("Saturation");
break;
}
}
}
private void changeContrast(float contrastProgress) {
float cb = this.contrastProgress == 0 ? 0 : -(this.contrastProgress / 1.8f) * 5;
ColorMatrix cm = new ColorMatrix(new float[]
{
contrastProgress, 0, 0, 0, cb,
0, contrastProgress, 0, 0, cb,
0, 0, contrastProgress, 0, cb,
0, 0, 0, contrastProgress, 0
});
paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(cm));
modifiedWallpaper = wallpaper.copy(Bitmap.Config.ARGB_8888, true);
canvas = new Canvas(modifiedWallpaper);
canvas.drawBitmap(modifiedWallpaper, 0, 0, paint);
imageView.setImageBitmap(modifiedWallpaper);
}
private void changeHue() {
ColorMatrix cm = new ColorMatrix();
float hueProgress = cleanValue() / 90f * (float) Math.PI;
if (hueProgress == 0){
return;
}
float cosVal = (float) Math.cos(hueProgress);
float sinVal = (float) Math.sin(hueProgress);
float lumR = 0.213f;
float lumG = 0.715f;
float lumB = 0.072f;
float[] mat = new float[]
{//logic };
cm.postConcat(new ColorMatrix(mat));
paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(cm));
modifiedWallpaper = wallpaper.copy(Bitmap.Config.ARGB_8888, true);
canvas = new Canvas(modifiedWallpaper);
canvas.drawBitmap(modifiedWallpaper, 0, 0, paint);
imageView.setImageBitmap(modifiedWallpaper);
}
private void changeSaturation() {
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(satProgress / 256);
paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
modifiedWallpaper = wallpaper.copy(Bitmap.Config.ARGB_8888, true);
canvas = new Canvas(modifiedWallpaper);
canvas.drawBitmap(modifiedWallpaper, 0, 0, paint);
imageView.setImageBitmap(modifiedWallpaper);
}
private SeekBar.OnSeekBarChangeListener seekBarChangeListener = new SeekBar.OnSeekBarChangeListener()
{
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if (isContrast) {
contrastProgress = seekBar.getProgress();
Log.d("Contrast", "" + (int) contrastProgress);
changeContrast((float) (seekBar.getProgress() + 10) / 10);
return;
}
if (isHue) {
hueProgress = seekBar.getProgress();
Log.d("Hue", "" + (int) hueProgress);
changeHue();
return;
}
if (isSaturation) {
satProgress = seekBar.getProgress();
Log.d("Saturation", "" + (int) satProgress);
changeSaturation();
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
};
您在应用过滤器时没有保存状态。您创建了一个 new Paint
和一个 new ColorMatrix
和一个 new Bitmap
。这就是为什么一个过滤器会覆盖前一个过滤器的原因。
有几种方法可以解决这个问题。我认为第一步是将 "filtering" 与 "drawing" 功能分开。示例:代码中的 changeSaturation()
不仅会改变饱和度,还会绘制到屏幕上。
以下是我的做法:
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if (isContrast) contrast = (float) (seekBar.getProgress() + 10) / 10
if (isHue) hue = seekBar.getProgress();
if (isSaturation) saturation = seekBar.getProgress();
ColorMatrix matrix = calculateColorMatrix(contrast, hue, saturation);
drawBitmap(matrix);
}
private void drawBitmap(ColorMatrix colorMatrix) {
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
modifiedWallpaper = wallpaper.copy(Bitmap.Config.ARGB_8888, true);
canvas = new Canvas(modifiedWallpaper);
canvas.drawBitmap(modifiedWallpaper, 0, 0, paint);
imageView.setImageBitmap(modifiedWallpaper);
}
private ColorMatrix calculateColorMatrix(float contrast, hue, saturation) {
ColorMatrix colorMatrix = new ColorMatrix();
// saturation
colorMatrix.setSaturation(satProgress / 256);
// hue
float cosVal = (float) Math.cos(hueProgress);
float sinVal = (float) Math.sin(hueProgress);
float lumR = 0.213f;
// logic...
colorMatrix.postConcat(new ColorMatrix(mat));
//contrast
// logic...
colorMatrix.postConcat(new ColorMatrix(new float[]
{
contrastProgress, 0, 0, 0, cb,
0, contrastProgress, 0, 0, cb,
0, 0, contrastProgress, 0, cb,
0, 0, 0, contrastProgress, 0
}));
return colorMatrix;
}
这只是其中一种方法。可能是最简单的一个。如果您不想在滑块每次移动时都创建所有过滤器,您可以保留三个不同的 matrix
每个过滤器一个,每次只修改其中一个。
ColorMatrix hue = new ColorMatrix();
ColorMatrix sat = new ColorMatrix();
ColorMatrix con = new ColorMatrix();
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
if (isContrast) {
con = //logic to create contrast matrix
}
if (isHue) {
hue //logic to create hue matrix
}
if (isSaturation) {
//...
sat.setSaturation(...)
}
ColorMatrix matrix = hue.postConcat(sat).postConcat(con);
drawBitmap(matrix);
}
我自己没有测试过这段代码,您可能需要进行一些更改,但我希望这能让您走上正确的方向!