編集:
私はいくつかの仮定をするつもりです。参照したリンクに基づいて、右側の垂直スライダーのようなスライダー コントロールを使用してリアルタイムで「ピボット」カラーを変更できる同様のことをしたいと思うでしょう。また、ピボットの色として赤/緑/青を切り替えたいと仮定します。
パフォーマンスを向上させる方法は次のとおりです。
- 色の配列を一度割り当てて、
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
}
activity_main.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 モードがあると思いますが、.NET では利用できません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 色空間に切り替えると、いくつかの異なるオプションが開かれます。当初考えていたように 2 つの画像を合成することは理にかなっています。画像の千の言葉のバージョンを提供するだけです。PhotoShop を開かないようにしてください。
色相にピボット:
開発時にレンダリングできる双方向グラデーション画像を描いています。このグラデーションでは、右上隅のアルファがゼロになり、下端が完全に黒になり、左上隅が完全に白になります。色相角度を移動するときは、この画像の下に無地の長方形を描くだけです。色は、完全な彩度と明るさで目的の色相になるため、この色だけが右上隅に表示されます。
彩度にピボット:
ここでは 2 つのグラデーション イメージを描いています。どちらも開発時にレンダリングできます。最初は完全な彩度で、上部の水平の虹が下部の黒に溶け込んでいます。2 つ目は彩度ゼロで、上が白、下が黒です。下に虹のグラデーションを描き、上に白/黒のグラデーションを描きます。上の画像のアルファをゼロからフルに変更すると、フル サチュレーションからゼロ サチュレーションへの変化が示されます。
明るさ(値)にピボット
このために、私は黒い四角形のベースを別の画像で描いています。これは、下部で垂直にトゥイーンするのではなく、水平の虹のグラデーションでもあります(完全な明るさ)。次に、虹の画像をフル アルファからゼロ アルファに変更して明るさを調整し、下にある黒い四角形を明らかにします。
これらのアルファ合成が実際の色空間を表していることを確認するには、いくつかの計算を行う必要がありますが、かなり近いと思います.