12

バックグラウンド:

ビットマップは大量のメモリを必要とするため、メモリ不足エラーが発生しやすいため、メモリを消費するハードな作業を C/C++ コードに置くことにしました。

ビットマップを回転させるために使用する手順は次のとおりです。

  1. ビットマップ情報を読み取る (幅、高さ)
  2. ビットマップ ピクセルを配列に格納します。
  3. ビットマップをリサイクルします。
  4. 反対のサイズの新しいビットマップを作成します。
  5. ピクセルを新しいビットマップに入れます。
  6. ピクセルを解放し、ビットマップを返します。

問題:

すべてがエラーなしで実行されているように見えますが、出力画像は元の画像の回転ではありません。実際、それはそれを完全に台無しにします。

回転は反時計回り、90 度にする必要があります。

私が得るものの例(スクリーンショットは拡大されています):

ここに画像の説明を入力

ご覧のとおり、色がおかしくなっただけでなく、サイズが設定したものと一致しません。ここで何かが本当に奇妙です。

多分私はデータを正しく読んだり入れたりしていませんか?

もちろんこれはほんの一例です。デバイスにそれを保持するのに十分なメモリがある限り、コードはどのビットマップでも正常に動作するはずです。また、ビットマップを回転させる以外に、ビットマップに対して他の操作を実行したい場合もあります。

私が作成したコード:

Android.mk ファイル:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := JniTest
LOCAL_SRC_FILES := JniTest.cpp
LOCAL_LDLIBS := -llog
LOCAL_LDFLAGS += -ljnigraphics
include $(BUILD_SHARED_LIBRARY)
APP_OPTIM := debug
LOCAL_CFLAGS := -g

cpp ファイル:

#include <jni.h>
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <android/bitmap.h>
#include <cstring>
#include <unistd.h>

#define  LOG_TAG    "DEBUG"
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

extern "C"
  {
  JNIEXPORT jobject JNICALL Java_com_example_jnitest_MainActivity_rotateBitmapCcw90(JNIEnv * env, jobject obj, jobject bitmap);
  }

JNIEXPORT jobject JNICALL Java_com_example_jnitest_MainActivity_rotateBitmapCcw90(JNIEnv * env, jobject obj, jobject bitmap)
  {
  //
  //getting bitmap info:
  //
  LOGD("reading bitmap info...");
  AndroidBitmapInfo info;
  int ret;
  if ((ret = AndroidBitmap_getInfo(env, bitmap, &info)) < 0)
    {
    LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
    return NULL;
    }
  LOGD("width:%d height:%d stride:%d", info.width, info.height, info.stride);
  if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888)
    {
    LOGE("Bitmap format is not RGBA_8888!");
    return NULL;
    }
  //
  //read pixels of bitmap into native memory :
  //
  LOGD("reading bitmap pixels...");
  void* bitmapPixels;
  if ((ret = AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels)) < 0)
    {
    LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
    return NULL;
    }
  uint32_t* src = (uint32_t*) bitmapPixels;
  uint32_t* tempPixels = new uint32_t[info.height * info.width];
  int stride = info.stride;
  int pixelsCount = info.height * info.width;
  memcpy(tempPixels, src, sizeof(uint32_t) * pixelsCount);
  AndroidBitmap_unlockPixels(env, bitmap);
  //
  //recycle bitmap - using bitmap.recycle()
  //
  LOGD("recycling bitmap...");
  jclass bitmapCls = env->GetObjectClass(bitmap);
  jmethodID recycleFunction = env->GetMethodID(bitmapCls, "recycle", "()V");
  if (recycleFunction == 0)
    {
    LOGE("error recycling!");
    return NULL;
    }
  env->CallVoidMethod(bitmap, recycleFunction);
  //
  //creating a new bitmap to put the pixels into it - using Bitmap Bitmap.createBitmap (int width, int height, Bitmap.Config config) :
  //
  LOGD("creating new bitmap...");
  jmethodID createBitmapFunction = env->GetStaticMethodID(bitmapCls, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
  jstring configName = env->NewStringUTF("ARGB_8888");
  jclass bitmapConfigClass = env->FindClass("android/graphics/Bitmap$Config");
  jmethodID valueOfBitmapConfigFunction = env->GetStaticMethodID(bitmapConfigClass, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;");
  jobject bitmapConfig = env->CallStaticObjectMethod(bitmapConfigClass, valueOfBitmapConfigFunction, configName);
  jobject newBitmap = env->CallStaticObjectMethod(bitmapCls, createBitmapFunction, info.height, info.width, bitmapConfig);
  //
  // putting the pixels into the new bitmap:
  //
  if ((ret = AndroidBitmap_lockPixels(env, newBitmap, &bitmapPixels)) < 0)
    {
    LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
    return NULL;
    }
  uint32_t* newBitmapPixels = (uint32_t*) bitmapPixels;
  int whereToPut = 0;    
  for (int x = info.width - 1; x >= 0; --x)
    for (int y = 0; y < info.height; ++y)
      {
      uint32_t pixel = tempPixels[info.width * y + x];
      newBitmapPixels[whereToPut++] = pixel;
      }
  AndroidBitmap_unlockPixels(env, newBitmap);
  //
  // freeing the native memory used to store the pixels
  //
  delete[] tempPixels;
  return newBitmap;
  }

Java ファイル:

  static
    {
    System.loadLibrary("JniTest");
    }

  /**
   * rotates a bitmap by 90 degrees counter-clockwise . <br/>
   * notes:<br/>
   * -the input bitmap will be recycled and shouldn't be used anymore <br/>
   * -returns the rotated bitmap . <br/>
   * -could take some time , so do the operation in a new thread
   */
  public native Bitmap rotateBitmapCcw90(Bitmap bitmap);

...
  Bitmap rotatedImage=rotateBitmapCcw90(bitmapToRotate);

編集: 回答を得た後、このコードとそれに関するメモをみんなと共有したいと思います:

  • それが機能するために、コード内の「uint16_t」のすべてのインスタンスを「uint32_t」に置き換えました(これは、私が尋ねたコードのバグです)。

  • 入力および出力ビットマップは 8888 構成 ( ARGB ) である必要があります

  • 入力ビットマップはプロセス中に再利用されます。

  • このコードは、イメージを反時計回りに 90 度回転させます。もちろん、必要に応じて変更できます。


より良い解決策

この機能とその他の機能を備えた素敵な投稿を作成しまし

4

1 に答える 1

6

formatを使用しているためARGB_8888、すべてのピクセルはuint32_tnotuint16_tです。回転したビットマップの作成を変更してuint32_t、ソース配列と宛先配列に使用するように変更してみてください。うまく機能するはずです。

于 2013-01-18T12:48:13.237 に答える