编辑:
我将做一些假设。根据您引用的链接,我假设您想要做类似的事情,您可以使用右侧的垂直滑块等滑块控件实时更改“枢轴”颜色。此外,我假设您想在红色/绿色/蓝色之间切换作为枢轴颜色。
以下是提高绩效的方法:
- 为颜色分配一次
int
数组并重用该数组。
- 分配一次位图并重复使用该位图。
- 始终将位图设置为 256 x 256,并在绘制时将位图缩放到正确的大小。这样每次计算都很重要;没有重复的像素。
考虑到所有这些事情,这里是对例程的重写:
private void changeColor(int w, int h, int[] pixels, char pivotColor, int pivotColorValue, boolean initial) {
if (pivotColorValue < 0 || pivotColorValue > 255) {
throw new IllegalArgumentException("color value must be between 0 and 255, was " + pivotColorValue);
}
if (initial) {
// set all the bits of the color
int alpha = 0xFF000000;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int r = 0, b = 0, g = 0;
switch (pivotColor) {
case 'R':
case 'r':
r = pivotColorValue << 16;
g = (256 * x / w) << 8;
b = 256 * y / h;
break;
case 'G':
case 'g':
r = (256 * x / w) << 16;
g = pivotColorValue << 8;
b = 256 * y / h;
break;
case 'B':
case 'b':
r = (256 * x / w) << 16;
g = (256 * y / h) << 8;
b = pivotColorValue;
break;
}
int index = y * w + x;
pixels[index] = alpha | r | g | b;
}
}
} else {
// only set the bits of the color that is changing
int colorBits = 0;
switch (pivotColor) {
case 'R':
case 'r':
colorBits = pivotColorValue << 16;
break;
case 'G':
case 'g':
colorBits = pivotColorValue << 8;
break;
case 'B':
case 'b':
colorBits = pivotColorValue;
break;
}
for (int i = 0; i < pixels.length; i++) {
switch (pivotColor) {
case 'R':
case 'r':
pixels[i] = (pixels[i] & 0xFF00FFFF) | colorBits;
break;
case 'G':
case 'g':
pixels[i] = (pixels[i] & 0xFFFF00FF) | colorBits;
break;
case 'B':
case 'b':
pixels[i] = (pixels[i] & 0xFFFFFF00) | colorBits;
break;
}
}
}
这是我测试它的方法:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private ImageView mImageView;
private Bitmap mBitmap;
private int[] mPixels;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle("Demo");
mPixels = new int[256 * 256];
mBitmap = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_8888);
mImageView = (ImageView) findViewById(R.id.imageview);
long start = SystemClock.elapsedRealtime();
changeColor(256, 256, mPixels, 'r', 0, true);
mBitmap.setPixels(mPixels, 0, 256, 0, 0, 256, 256);
mImageView.setImageBitmap(mBitmap);
long elapsed = SystemClock.elapsedRealtime() - start;
Log.d(TAG, "initial elapsed time: " + elapsed + " ms");
SeekBar seekBar = (SeekBar) findViewById(R.id.seekbar);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
long start = SystemClock.elapsedRealtime();
changeColor(256, 256, mPixels, 'r', progress, false);
mBitmap.setPixels(mPixels, 0, 256, 0, 0, 256, 256);
mImageView.setImageBitmap(mBitmap);
long elapsed = SystemClock.elapsedRealtime() - start;
Log.d(TAG, "elapsed time: " + elapsed + " ms");
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) { }
@Override
public void onStopTrackingTouch(SeekBar seekBar) { }
});
}
// changeColor method goes here
}
活动主.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<ImageView
android:id="@+id/imageview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scaleType="fitCenter"/>
<SeekBar
android:id="@+id/seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="24dp"
android:max="255"/>
</LinearLayout>
试试看,看看它是否对你来说表现得足够好。我认为这是合理的。
我认为底层的 Skia 库有一个 Porter-Duff 模式可以做到这一点,但它在android.graphics.PorterDuff.Mode
.
好吧,我想我们只需要自己动手:
private Bitmap makeColorPicker(int w, int h, int r) {
if (r < 0 || r > 255) {
throw new IllegalArgumentException("red value must be between 0 and 255, was " + r);
}
// need to manage memory, OutOfMemoryError could happen here
int[] pixels = new int[w * h];
int baseColor = 0xFF000000 | (r << 16); // alpha and red value
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int g = (256 * x / w) << 8;
int b = 256 * y / h;
int index = y * w + x;
pixels[index] = baseColor | g | b;
}
}
return Bitmap.createBitmap(pixels, w, h, Bitmap.Config.ARGB_8888);
}
关于 HSV:
一旦您切换到 HSV 颜色空间,就会为您打开一些不同的选项。现在像您最初考虑的那样合成两个图像是有意义的。我只是给你图片的千字版本。请不要让我打开 PhotoShop。
以色相为中心:
我正在描绘一个可以在开发时渲染的双向渐变图像。此渐变在右上角将具有零 alpha,在底部边缘为全黑,在左上角为全白。当您移动色调角度时,您只需在此图像下方绘制一个纯色矩形。颜色将是完全饱和度和亮度下的所需色调,因此您只会在右上角看到这种颜色。
以饱和度为中心:
在这里,我正在描绘两个渐变图像,两者都可以在开发时渲染。第一个是完全饱和,你可以看到顶部的水平彩虹在底部混合成黑色。第二个是零饱和度,顶部为白色,底部为黑色。您在底部绘制彩虹渐变,然后在顶部绘制白色/黑色渐变。将顶部图像的 alpha 从零更改为完全将显示从完全饱和到零饱和的变化。
以亮度为中心(值)
为此,我正在描绘一个黑色矩形底座和另一个图像,该图像也是一个水平彩虹渐变,而不是在底部垂直补间到白色(全亮度)。现在,您可以通过将彩虹图像从完整 Alpha 更改为零 Alpha 来调整亮度,从而显示下方的黑色矩形。
我必须做一些数学运算来确保这些 alpha 复合材料代表实际的色彩空间,但我认为我非常接近。