📜 ⬆️ ⬇️

Download PNG and JPEG images in Android NDK

Greetings to all readers!
This article will discuss how to download PNG and JPEG images from a file or memory into Android NDK, as well as some useful code for feeding these OpenGL images.

Download


To upload images we will use the following libraries:

Unpack


We decompose everything neatly into folders, for example, create a modules folder in the project root, in the same place where the jni folder is located . Unpack:
so such structure will turn out
PROJECT / jni
PROJECT / modules / png
PROJECT / modules / jpeg
PROJECT / modules / zlib

Tune



Android.mk rule


In Android.mk we connect all the libraries together
LOCAL_PATH: = $ (call my-dir)
HEADERS: =
STATICLIBS: =

include $ (CLEAR_VARS)
LOCAL_MODULE: = png
FILE_LIST: = $ (wildcard $ (LOCAL_PATH) /../ modules / png / *. C *)
LOCAL_SRC_FILES: = $ (FILE_LIST: $ (LOCAL_PATH) /% =%)
LOCAL_EXPORT_C_INCLUDES: = $ (LOCAL_PATH) /../ modules / png
HEADERS + = $ (LOCAL_EXPORT_C_INCLUDES)
STATICLIBS + = $ (LOCAL_MODULE)
include $ (BUILD_STATIC_LIBRARY)

include $ (CLEAR_VARS)
LOCAL_MODULE: = zlib
FILE_LIST: = $ (wildcard $ (LOCAL_PATH) /../ modules / zlib / *. C *)
LOCAL_SRC_FILES: = $ (FILE_LIST: $ (LOCAL_PATH) /% =%)
LOCAL_EXPORT_C_INCLUDES: = $ (LOCAL_PATH) /../ modules / zlib
HEADERS + = $ (LOCAL_EXPORT_C_INCLUDES)
STATICLIBS + = $ (LOCAL_MODULE)
include $ (BUILD_STATIC_LIBRARY)
')
include $ (CLEAR_VARS)
LOCAL_MODULE: = jpeg
LOCAL_SRC_FILES: = ../modules/jpeg/obj/local/$(TARGET_ARCH_ABI)/libjpeg.a
LOCAL_EXPORT_C_INCLUDES: = $ (LOCAL_PATH) /../ modules / jpeg
STATICLIBS + = $ (LOCAL_MODULE)
HEADERS + = $ (LOCAL_EXPORT_C_INCLUDES)
include $ (PREBUILT_STATIC_LIBRARY)

# ------------------------------------------------- ---------

include $ (CLEAR_VARS)
LOCAL_ARM_MODE: = arm
LOCAL_MODULE: = LoadImage
LOCAL_SRC_FILES: = loadimage.cpp
LOCAL_CFLAGS: = -Werror -DGL_GLEXT_PROTOTYPES = 1 -fsigned-char -Wno-write-strings -Wno-psabi
LOCAL_LDLIBS: = -llog -lGLESv1_CM
LOCAL_STATIC_LIBRARIES: = $ (STATICLIBS)
LOCAL_C_INCLUDES = $ (HEADERS)
include $ (BUILD_SHARED_LIBRARY)

Reading pictures


In your C ++ code (I have this file loadimage.cpp ), do the following:
Code with comments
 #include <jni.h> #include <android/log.h> #include <GLES/gl.h> #include <GLES/glext.h> //  extern "C" { #include "png.h" #include <setjmp.h> #include "jpeglib.h" } #define LOG(...) __android_log_print(ANDROID_LOG_VERBOSE, "NDK",__VA_ARGS__) //   struct image { png_uint_32 imWidth, imHeight; //   png_uint_32 glWidth, glHeight; //    OpenGL int bit_depth, color_type; char* data; // RGB/RGBA }; //-       OpenGL static int reNpot(int w) { //  OpenGL      //            //String s = gl.glGetString(GL10.GL_EXTENSIONS); //NON_POWER_OF_TWO_SUPPORTED = s.contains("texture_2D_limited_npot") || s.contains("texture_npot") || s.contains("texture_non_power_of_two"); bool NON_POWER_OF_TWO_SUPPORTED = false; if (NON_POWER_OF_TWO_SUPPORTED) { if (w % 2) w++; } else { if (w <= 4) w = 4; else if (w <= 8) w = 8; else if (w <= 16) w = 16; else if (w <= 32) w = 32; else if (w <= 64) w = 64; else if (w <= 128) w = 128; else if (w <= 256) w = 256; else if (w <= 512) w = 512; else if (w <= 1024) w = 1024; else if (w <= 2048) w = 2048; else if (w <= 4096) w = 4096; } return w; } //-  PNG  static image readPng(const char* fileName) { image im; FILE* file = fopen(fileName, "rb"); // ,       PNG   JPEG,  -      fseek(file, 8, SEEK_CUR); png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_infop info_ptr = png_create_info_struct(png_ptr); png_init_io(png_ptr, file); png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); //    png_get_IHDR(png_ptr, info_ptr, &im.imWidth, &im.imHeight, &im.bit_depth, &im.color_type, NULL, NULL, NULL); //     OpenGL im.glWidth = reNpot(im.imWidth); im.glHeight = reNpot(im.imHeight); //        4  (RGBA),  3 (RGB) int row = im.glWidth * (im.color_type == PNG_COLOR_TYPE_RGBA ? 4 : 3); im.data = new char[row * im.glHeight]; //         png_bytep * row_pointers = new png_bytep[im.imHeight]; for(int i = 0; i < im.imHeight; ++i) row_pointers[i] = (png_bytep) (im.data + i * row); //  png_read_image(png_ptr, row_pointers); png_destroy_read_struct(&png_ptr, &info_ptr, 0); delete[] row_pointers; return im; } //   libjpeg-turbo struct my_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; typedef struct my_error_mgr * my_error_ptr; METHODDEF(void) my_error_exit(j_common_ptr cinfo) { my_error_ptr myerr = (my_error_ptr) cinfo->err; (*cinfo->err->output_message)(cinfo); longjmp(myerr->setjmp_buffer, 1); } //-  JPEG  static image readJpeg(const char* fileName) { image im; FILE* file = fopen(fileName, "rb"); struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; if (setjmp(jerr.setjmp_buffer)) { jpeg_destroy_decompress(&cinfo); return im; } jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo, file); //    jpeg_read_header(&cinfo, TRUE); jpeg_start_decompress(&cinfo); im.imWidth = cinfo.image_width; im.imHeight = cinfo.image_height; im.glWidth = reNpot(im.imWidth); im.glHeight = reNpot(im.imHeight); //JPEG        3- (RGB) int row = im.glWidth * 3; im.data = new char[row * im.glHeight]; //   unsigned char* line = (unsigned char*) (im.data); while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, &line, 1); line += row; } // jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); return im; } 

Tests


We are testing the loading of PNG images from the memory card:
 // OpenGL  GLuint texture1; glGenTextures(1, &texture1); glBindTexture(GL_TEXTURE_2D, texture1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // PNG  image im = readPng("/mnt/sdcard/scrrihs.png"); LOG("PNG: %dx%d (%dx%d) bit:%d type:%d", im.imWidth, im.imHeight, im.glWidth, im.glHeight, im.bit_depth, im.color_type); //       OpenGL if (im.color_type == PNG_COLOR_TYPE_RGBA) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, im.glWidth, im.glHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, im.data); } else { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, im.glWidth, im.glHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, im.data); } delete[] im.data; 

Similarly, we do with JPEG , given that JPEG is always without transparency
 GLuint texture2; glGenTextures(1, &texture2); glBindTexture(GL_TEXTURE_2D, texture1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); image imJpeg = readJpeg("/mnt/sdcard/test.jpg"); LOG("JPEG: %dx%d (%dx%d)", imJpeg.imWidth, imJpeg.imHeight, imJpeg.glWidth, imJpeg.glHeight); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imJpeg.glWidth, imJpeg.glHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, imJpeg.data); delete[] imJpeg.data; 

Reading encrypted pictures


If you need to use simple image encryption, you can insert the decryption function directly into the image reading process:

Reading pictures from memory


For example, the application received a picture over the network and the picture hangs entirely in memory.

Opengl buns


 //   RGBA  RGBA4444 int len = im.glWidth * im.glHeight; unsigned short* tmp = (unsigned short*) im.data; for(int i = 0; i < len; i++) tmp[i] = ((im.data[i * 4] >> 4) << 12) | ((im.data[i * 4 + 1] >> 4) << 8) | ((im.data[i * 4 + 2] >> 4) << 4) | (im.data[i * 4 + 3] >> 4); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, im.glWidth, im.glHeight, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, im.data); // RGB   RGB565 int len = im.glWidth * im.glHeight; unsigned short* tmp = (unsigned short*) im.data; for(int i = 0; i < len; i++) tmp[i] = ((im.data[i * row] >> 3) << 11) | ((im.data[i * row + 1] >> 2) << 5) | (im.data[i * row + 2] >> 3); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, im.glWidth, im.glHeight, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, im.data); //  RGB/RGBA   GL_LUMINANCE  GL_ALPHA int row = HAS_ALPHA?4:3; int len = im.glWidth * im.glHeight * row; for(int i = 0, a = 0; i < len; i += row, a++) im.data[a] = im.data[i]; glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, im.glWidth, im.glHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, im.data); 

It's okay that in the loop the array writes to itself because the final array is always smaller than the original array and after creating the OpenGL texture, we no longer need it.

I am sure someone these developments will be useful. In any case, before that, I had to throw downloaded files through JNI to Java, create a Bitmap there, read pixels and give back to the NDK, which was an order of magnitude more expensive both in time and in memory. In addition, all these functions can be used not only in the Android NDK, but also in iOS / MacOS.
Just in case, here are the commands for compiling libjpeg-turbo (libpng compiles without problems by simply adding a folder to XCode):
Hidden text
cd {source_directory}
autoreconf -fiv
mkdir build
cd build

MacOS
sh ../configure --host i686-apple-darwin CFLAGS = '- O3 -m32' LDFLAGS = -m32

iOS ARM v7 only
sh ../configure --host arm-apple-darwin10 --enable-static --disable-shared CC = "/ Applications / Xcode.app / Contents / Developer / Platforms / iPhoneOS.platform / Developer / usr / bin / arm -apple-darwin10-llvm-gcc-4.2 "LD =" / Applications / Xcode.app / Contents / Developer / Platforms / iPhoneOS.platform / Developer / usr / bin / arm-apple-darwin10-llvm-gcc-4.2 "CFLAGS = "- mfloat-abi = softfp -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk -O3 -march = armv7 -mcpu = cortex-a8 -mtune = cortex-a8 -mfpu = neon "LDFLAGS =" - mfloat-abi = softfp -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk -march = armv7 -mcpu = cortex-a8 -mtune = cortex-a8 -mfpu = neon "

Update:
The zlib library can be connected to the native of the NDK. To do this, in Android.mk, you need to remove the block that concerns zlib and register:
 LOCAL_LDLIBS := -llog -lGLESv1_CM -lz 

Source: https://habr.com/ru/post/173303/


All Articles