使用 JNI 和 NDK 旋转位图

2023-12-06

背景:

我决定,由于位图占用大量内存,很容易导致内存不足错误,因此我将把艰苦的、消耗内存的工作放在 C/C++ 代码上。

我用于旋转位图的步骤是:

  1. 读取位图信息(宽度,高度)
  2. 将位图像素存储到数组中。
  3. 回收位图。
  4. 创建一个相反大小的新位图。
  5. 将像素放入新的位图中。
  6. 释放像素并返回位图。

问题:

尽管一切似乎都运行没有任何错误,但输出图像并不是原始图像的旋转。事实上,它完全毁了它。

旋转应为逆时针方向,即 90 度。

我得到的示例(屏幕截图已放大):

enter image description here

正如你所看到的,不仅颜色变得更奇怪,而且尺寸也与我设置的不匹配。这里确实有些奇怪。

也许我没有正确读取/放置数据?

当然这只是一个例子。只要设备有足够的内存来容纳该代码,该代码就可以在任何位图上正常工作。另外,除了旋转位图之外,我可能还想对位图执行其他操作。

我创建的代码:

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);

编辑:在我得到答案后,我希望与大家分享这段代码和注释:

  • 为了让它工作,我在代码中用“uint32_t”替换了“uint16_t”的每个实例(这是我询问过的代码中的错误)。

  • 输入和输出位图必须使用 8888 配置(即 ARGB )

  • 输入位图将在此过程中被回收。

  • 该代码将图像逆时针旋转 90 度。当然,您可以根据您的需要进行更改。


更好的解决方案

我已经发表了一篇很好的文章,其中包含此功能和其他功能,here .


既然你正在使用ARGB_8888每个像素的格式是uint32_t not uint16_t。尝试更改旋转位图创建以使用uint32_t对于源和目标数组,它应该工作得更好。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 JNI 和 NDK 旋转位图 的相关文章

  • 如何为不同的gradle buildTypes提供不同的Android应用程序图标?

    我的 gradle 文件中设置了两种构建类型 debug and release 我希望能够为debug构建类型 有没有办法只通过构建类型来实现这一点 而无需考虑产品风格 build gradle 文件如下 apply plugin and
  • 一部又一部的Android动画

    我在 TextView 上有两个 TranslateAnimations 我希望它们一个接一个地执行 但是 通过使用下面的代码 仅执行第二个代码 我该如何解决这个问题 TranslateAnimation animation new Tra
  • MPAndroidChart - 饼图的所有部分都是相同的颜色

    我正在使用 MPAndroidChart https github com PhilJay MPAndroidChart https github com PhilJay MPAndroidChart 库来生成饼图 我遵循了多个教程 包括
  • 无法绑定到调试器的本地 XXXX

    我不断得到Can t bind to local XXXX for debugger控制台中的消息 但不适用于 1 个端口 适用于所有随机端口 我已经完成了中所述的操作这个问题 https stackoverflow com questio
  • CollapsingToolbarLayout 无法识别滚动 fling

    我创建了一个简单的折叠工具栏布局它就像一个魅力 我的问题是 如果我尝试在嵌套滚动视图 当我松开手指时它就会停止 正常的滚动就像它应该的那样工作 我的活动代码是不变 gt 自动生成空活动 我只是单击了 android studio 中的 创建
  • 编译JNI时出现问题

    我通过引用已使用 JNI 生成的头文件实现了以下 C 代码 include
  • Android - 缩放和压缩位图

    我正在开发一个 Android 应用程序 它具有相机捕捉和照片上传功能 如果设备具有高分辨率相机 则捕获的图像尺寸将非常大 1 3MB或更大 由于应用程序需要将此图像上传到服务器 因此我需要在上传之前压缩图像 例如 如果相机拍摄了 1920
  • 覆盖服务 - 按下返回按钮

    我怎样才能做到这一点 目前的解决方案 我启动了一个透明的活动 捕获后按 将其转发到我的服务 然后自行关闭 但此活动将在当前正在运行的活动中可见 因此这不是一个非常漂亮的解决方案 看到的解决方案 我见过一个应用程序确实可以捕获服务中的后按 而
  • 使用 Gradle 构建未签名的 APK

    目前我正在尝试学习 Gradle 来构建 Android APK 如何在 gradle 中设置选项来构建未签名的 APK 您不必设置任何选项 只需运行常规任务即可 gradle assemble 这将在project build apk 中
  • 尝试在空对象引用上调用接口方法“...”

    我正在开发一个具有蓝牙功能的应用程序 我使用片段来扫描并列出蓝牙设备 单击时 会回调提供所选蓝牙设备的主要活动 我从使用 Android 6 API 23 的智能手机开始 然后必须调整代码以用于 Android 5 0 API 21 我刚刚
  • 如何在 Google Maps API V2 中获取我的当前位置

    我正在创建一个应用程序 用户需要使用 getMyLocation 查看他 她的地理位置 但这返回 null 有没有解决这个问题的方法 因为我确实读到 getMyLocation 方法总是返回 null 我是 Google 地图新手 因此我们
  • 如何在线性布局上制作波纹效果,而不覆盖其子级的背景颜色?

    I have a LinearLayout that looks like this 我希望每一行都是可点击的 这LinearLayout一行的代码如下所示
  • 设置画廊间距的任何值都会禁用 onKeyEvent

    当用户单击左侧或右侧按钮时 我试图使图库以动画向左或向右滚动 如果我使用下面的代码调用 keyevent 来滚动图库 效果会很好 gallery onKeyDown KeyEvent KEYCODE DPAD LEFT new KeyEve
  • 即使在 Scaffold 中定义了 BottomModalSheet 小部件,Flutter 中也未找到 Scaffold 错误

    我试图为测试应用程序实现 BottomModalSheet 但每次都会弹出同样的错误 说找不到脚手架 该应用程序的代码如下 该错误表明 Scaffold 是由 MaterialApp Widget 实现的 因此我删除了 MaterialAp
  • 问题:将大数据传递给第二个 Activity

    我有一个奇怪的问题 我在网上浏览但没有找到答案 我仍然是android编程的初学者 那么让我们开始吧 我想做的就是用一些数据调用第二个活动 它适用于小数据 但如果数据变大 第二个 Activity 将不会显示 第一个 Activity 将完
  • SQLite CursorWindow 限制 - 如何避免崩溃

    我必须执行查询并将结果存储在列表中 我使用的函数如下 List
  • 画廊新媒体如何播放?

    我试图收到有关添加到手机图库的新图片或视频的通知 我需要获取新媒体的 URI 目的是让我可以自动备份它 因此 我需要一个在后台设置的寄存器来连续侦听或检查添加到图库的新媒体 并捕获 Uri 这过去是通过广播接收器完成的 例如
  • 让 DrawerLayout 在 ActionBar 上滑动

    我在活动中有一个滑动抽屉菜单 其中有一个带有一些选项卡的操作栏 我想让滑动抽屉滑过标签 而不是滑过标签下方 这就是现在的样子 关于如何做到这一点有什么想法吗 注意 我知道我可能会在这里打破一些约定和 UI 模式 如果它根本不起作用 我会考虑
  • 在java中访问dll方法

    我正在尝试访问java中用c 编写的dll方法 从下面的代码我试图构建已成功生成的 dll using System using Microsoft Win32 namespace CyberoamWinHelper public clas
  • Android:Html 锚链接仅在 Web 视图中有效一次

    在使用锚链接加载 html 内容时 我在 webview 中遇到一些奇怪的问题 以下代码非常适合锚标记 但是只有一次 第二次当我按下锚标签时不工作 protected void onCreate Bundle savedInstanceSt

随机推荐

  • 使用预签名 URL 通过 cURL 将文件加载到 S3

    我获得了一个预签名 URL 用于在 S3 存储桶上上传文件 这是卷曲命令 curl v T dansero jpg https ss files dev s3 ap southeast 2 amazonaws com dansero jpg
  • 如何在Robot Framework中实现java库

    如何在 Eclipse 中创建库 然后将其导入 Robot FrameWork 中 我现在进行了很多搜索 但没有任何指南可以帮助我 您需要执行以下操作 创建您的 java 库 运行机器人框架jython版本时将其添加到类路径中 创建您的 j
  • 如果失败,请重试 SFTP?

    我正在使用 SSH NET 上传 但如果进程失败 我想重试 sftp 文件 我有这段代码 但我认为这不是处理重试的最佳方法 处理这个问题的最佳方法是什么 var exceptions new List
  • 在 Android AsyncTask 中获取 JSON

    我正在尝试获取 JSON 但我必须在 AsyncTask 中执行此操作 因为我在 logcat 中获取了它AndroidRuntime 18153 Caused by android os NetworkOnMainThreadExcept
  • Docker Compose 连接 ECONNREFUSED 172.18.0.4:3306

    当我使用以下命令构建项目的容器时 sudo docker build t PROJECT NAME 然后我通过这个 Docker Compose 配置下载 mysql 的镜像 db image mysql restart always po
  • 在 Windows Phone 8.1 上使用 MediaCapture 时拍摄的照片为黑色

    我正在使用 MediaCapture 捕获照片并存储它们 它可以在模拟器中运行 但当在真实手机 诺基亚 Lumia 530 上运行该应用程序时 捕获的照片只是黑色 它们具有正确的大小并且文件具有一定的字节长度 但是当显示照片时它是黑色的 请
  • 记忆游戏的 GUI 组件

    我正在做作业 所以我不要求代码 我想自己做这个 顺便说一句 我再次陷入 GUI 部分 并且代码部分没有什么问题 首先是按钮大小和图像大小 我没有使用按钮大小的方法 只是将图像设置为按钮的图标 但正如您在下面看到的 按钮不适合图像 第二件事是
  • 反序列化会导致列表条目的副本

    我想创建一个非常通用的模型层 它也可以作为 JSON 传递 一个模型应显示 RaspberryPi2 的 LED 面板 由于我希望对类进行尽可能接近现实的建模 因此我强制列表始终具有 8 8 个 LED 该类看起来像这样 public cl
  • 用子进程包装 cmd.exe

    我尝试使用以下程序在Windows下包装cmd exe 但它不起作用 它似乎在等待某些东西并且不显示任何内容 知道这里出了什么问题吗 import subprocess process subprocess Popen cmd exe sh
  • iphone sdk CGAffineTransform 获取物体的旋转角度

    我如何计算任何给定对象 即 uiimageview 的旋转角度 从技术上讲你不能 因为转换可以包括skew将图像变成平行四边形的操作 并且旋转角度不再定义 无论如何 由于旋转矩阵生成 cos x sin x 0 sin x cos x 0
  • VS2010 - 将 html 代码格式分配给 T4 (.tt) 文件

    我在 VS2010 中有一个 T4 文本模板 tt 主要用于生成 HTML 代码 基本上是一些包含和 JavaScript 是否可以指定 HTML 代码格式 颜色等 到该 tt 文件 情况 T4 想要有 更新Marcio Barcellos
  • MYSQL:如何查询JSON数组包含特定标签的位置

    MySQL 5 7 24 假设我有 3 行 如下所示 ID PK Name VARCHAR Data JSON 1 Admad label Color value Red label Age value 40 2 Saleem label
  • Struts 2重构代码以避免OGNL静态方法访问

    Struts 2 2 3 20 提到 将禁用对从表达式访问静态方法的支持 很快 请考虑重构您的应用程序 以避免进一步 问题 我们在验证器中使用了 OGNL 静态调用 ExpressionValidator expression foo ba
  • Spark SQL 中按日期分组的聚合

    我有一个包含时间戳的 RDDtime长类型 root id string nullable true value1 string nullable true value2 string nullable true time long nul
  • 如何使用命令行将新的 MySQL 数据库结构从开发网站迁移到生产网站?

    我有两个网站环境 独立的服务器 Media Temple DV 开发和生产 我们开始在生产上构建站点 然后获得了开发服务器 因此我最初使用如下命令将生产数据库移动到开发 mysqldump a u USERNAME p DATABASE g
  • 涉及多个变量的程序的时间复杂度

    最近 我被要求创建一个程序来查找文本片段中的最佳匹配 我已经成功编写了这个程序 但我确实对其时间复杂度有疑问 问题定义如下 给定一个查询 查找文档中出现的查询词并突出显示最佳标记 我的程序花费的时间 O m n p here m 文档长度
  • Facebook Connect + jQuery Mobile + Phonegap 构建

    我试图了解如何使用脸书连接 登录 与jQuery 移动 and 音隙构建 但随着我搜索这些信息的次数越多 我就越感到困惑 我已经在 Facebook 上创建了我的应用程序 并且我有 API 编号 我不知道最好的方法是否是调用 PHP 页面
  • Ninject 和 XML 配置绑定

    我一直在互联网上搜索任何示例或入门文章 了解如何使用 XML 扩展与 Ninject 进行绑定 但我找不到任何帮助 任何人都可以向我提供一个很小的样本来说明我该如何做到这一点吗 提前致谢 我也找不到任何示例 但老实说源代码非常小 我只是下载
  • 更新 ListView 的 ObservableCollection 中一项的显示

    我有一个绑定到 ObservableCollection 的 ListView 有没有一种方法可以在 SomeModel 项的属性发生更改时更新单个单元格 而无需通过更改 ObservableCollection 来重新加载 ListVie
  • 使用 JNI 和 NDK 旋转位图

    背景 我决定 由于位图占用大量内存 很容易导致内存不足错误 因此我将把艰苦的 消耗内存的工作放在 C C 代码上 我用于旋转位图的步骤是 读取位图信息 宽度 高度 将位图像素存储到数组中 回收位图 创建一个相反大小的新位图 将像素放入新的位