mirror of
				https://github.com/android/ndk-samples
				synced 2025-11-04 14:27:06 +08:00 
			
		
		
		
	Compare commits
	
		
			3 Commits
		
	
	
		
			b21d220f67
			...
			develop
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					5d2eef15d3 | ||
| 
						 | 
					44d725a358 | ||
| 
						 | 
					9d05e29492 | 
@@ -1,9 +1,10 @@
 | 
			
		||||
WEBP Samples
 | 
			
		||||
=============
 | 
			
		||||
Webp is an Android sample including a small app to demo usage of webp in [Native Activity](http://developer.android.com/reference/android/app/NativeActivity.html)    
 | 
			
		||||
view:
 | 
			
		||||
- rotate decoding 3 webp images and load them into on-screen buffer. Decoding is in its own thread
 | 
			
		||||
Sample in this directory is to demonstrate image decoding
 | 
			
		||||
- view: demonstrate how to use webp in [Native Activity](http://developer.android.com/reference/android/app/NativeActivity.html)    
 | 
			
		||||
- image-decoder: demonstrate AImageDecoder usage integrated in Android 12
 | 
			
		||||
 | 
			
		||||
Note that the directory names will be changed in DP2
 | 
			
		||||
 | 
			
		||||
This sample uses the new [Android Studio CMake plugin](https://developer.android.com/ndk/guides/cmake.html).
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#Thu Feb 18 09:46:00 PST 2021
 | 
			
		||||
distributionBase=GRADLE_USER_HOME
 | 
			
		||||
distributionPath=wrapper/dists
 | 
			
		||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
 | 
			
		||||
zipStoreBase=GRADLE_USER_HOME
 | 
			
		||||
zipStorePath=wrapper/dists
 | 
			
		||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										40
									
								
								webp/image-decoder/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								webp/image-decoder/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
# Image-Decoder
 | 
			
		||||
The sample demonstrates how to use the new AImageDecoder animated decoding API,
 | 
			
		||||
added in Android 12. The animated images such as webp, avif and other formats, are
 | 
			
		||||
decoded and copied into the native window to display them on the screen.
 | 
			
		||||
 | 
			
		||||
The goal of the sample is to collect feedback for new AImageDecoder API added in Android 12
 | 
			
		||||
and Android 11, please try it out and let us know your thoughts!
 | 
			
		||||
 | 
			
		||||
## Pre-requistites
 | 
			
		||||
- Android Gradle Plugin 4.1.2
 | 
			
		||||
- Platform SDK and NDK for Android 12
 | 
			
		||||
- An Android device running Android 12(API level 31)
 | 
			
		||||
 | 
			
		||||
## Getting Start
 | 
			
		||||
- build the sample and run it on an Android 12 device
 | 
			
		||||
- swap in new image files: drop them into src/main/assets/images, and add to imageFiles[] in image_viewer.cpp
 | 
			
		||||
 | 
			
		||||
## Support
 | 
			
		||||
Android 12, AImageDecoder API and this sample are at developer preview stage, you
 | 
			
		||||
may expect changes in the future Android 12 release cycles. If you find any error
 | 
			
		||||
or have any feedback, please [file an issue](https://github.com/googlesamples/android-ndk/issues/new).
 | 
			
		||||
Comments and patches are highly very welcome too!
 | 
			
		||||
 | 
			
		||||
## Copyright
 | 
			
		||||
Copyright 2021 Google, Inc.
 | 
			
		||||
 | 
			
		||||
Licensed to the Apache Software Foundation (ASF) under one or more contributor
 | 
			
		||||
license agreements.  See the NOTICE file distributed with this work for
 | 
			
		||||
additional information regarding copyright ownership.  The ASF licenses this
 | 
			
		||||
file to you under the Apache License, Version 2.0 (the "License"); you may not
 | 
			
		||||
use this file except in compliance with the License.  You may obtain a copy of
 | 
			
		||||
the License at
 | 
			
		||||
 | 
			
		||||
  http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 | 
			
		||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 | 
			
		||||
License for the specific language governing permissions and limitations under
 | 
			
		||||
the License.
 | 
			
		||||
							
								
								
									
										30
									
								
								webp/image-decoder/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								webp/image-decoder/build.gradle
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
apply plugin: 'com.android.application'
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
    compileSdkVersion 'android-S'
 | 
			
		||||
    ndkVersion '23.0.7123448'
 | 
			
		||||
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        applicationId 'com.android.example.webp_image_decoder'
 | 
			
		||||
        minSdkVersion 'S'
 | 
			
		||||
        targetSdkVersion 'S'
 | 
			
		||||
        
 | 
			
		||||
        externalNativeBuild {
 | 
			
		||||
            cmake {
 | 
			
		||||
                arguments '-DANDROID_STL=c++_static'
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    buildTypes {
 | 
			
		||||
        release {
 | 
			
		||||
            minifyEnabled false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    externalNativeBuild {
 | 
			
		||||
        cmake {
 | 
			
		||||
            version '3.10.2'
 | 
			
		||||
            path 'src/main/cpp/CMakeLists.txt'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										26
									
								
								webp/image-decoder/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								webp/image-decoder/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
          package="com.android.example.webp_accelerated_view"
 | 
			
		||||
          android:versionCode="1"
 | 
			
		||||
          android:versionName="1.0">
 | 
			
		||||
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
 | 
			
		||||
  <uses-feature android:glEsVersion="0x00030002"></uses-feature>
 | 
			
		||||
 | 
			
		||||
  <application
 | 
			
		||||
      android:allowBackup="false"
 | 
			
		||||
      android:fullBackupContent="false"
 | 
			
		||||
      android:icon="@mipmap/ic_launcher"
 | 
			
		||||
      android:label="@string/app_name"
 | 
			
		||||
      android:hasCode="false">
 | 
			
		||||
    <activity android:name="android.app.NativeActivity"
 | 
			
		||||
              android:label="@string/app_name"
 | 
			
		||||
              android:exported="true">
 | 
			
		||||
      <meta-data android:name="android.app.lib_name"
 | 
			
		||||
                 android:value="image_decoder" />
 | 
			
		||||
      <intent-filter>
 | 
			
		||||
        <action android:name="android.intent.action.MAIN" />
 | 
			
		||||
        <category android:name="android.intent.category.LAUNCHER" />
 | 
			
		||||
      </intent-filter>
 | 
			
		||||
    </activity>
 | 
			
		||||
  </application>
 | 
			
		||||
</manifest>
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								webp/image-decoder/src/main/assets/images/android.avif
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								webp/image-decoder/src/main/assets/images/android.avif
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 244 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								webp/image-decoder/src/main/assets/images/android.heic
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								webp/image-decoder/src/main/assets/images/android.heic
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								webp/image-decoder/src/main/assets/images/android.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								webp/image-decoder/src/main/assets/images/android.webp
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 811 KiB  | 
							
								
								
									
										42
									
								
								webp/image-decoder/src/main/cpp/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								webp/image-decoder/src/main/cpp/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
#[[
 | 
			
		||||
# Copyright (C) 2021 The Android Open Source Project
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#]]
 | 
			
		||||
project("image_decoder")
 | 
			
		||||
cmake_minimum_required(VERSION 3.10.2)
 | 
			
		||||
 | 
			
		||||
set(CMAKE_VERBOSE_MAKEFILE on)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Build native_app_glue as a static lib
 | 
			
		||||
include_directories(${ANDROID_NDK}/sources/android/native_app_glue)
 | 
			
		||||
 | 
			
		||||
add_library(native_app_glue STATIC
 | 
			
		||||
    ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
 | 
			
		||||
 | 
			
		||||
# Create app's shared lib
 | 
			
		||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
 | 
			
		||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall")
 | 
			
		||||
 | 
			
		||||
# Export ANativeActivity_onCreate(),
 | 
			
		||||
# Refer to: https://github.com/android-ndk/ndk/issues/381.
 | 
			
		||||
set(CMAKE_SHARED_LINKER_FLAGS
 | 
			
		||||
    "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
 | 
			
		||||
 | 
			
		||||
add_library(image_decoder SHARED
 | 
			
		||||
     android_main.cpp
 | 
			
		||||
     image_viewer.cpp
 | 
			
		||||
     image_decoder.cpp)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(image_decoder native_app_glue jnigraphics android log m)
 | 
			
		||||
							
								
								
									
										54
									
								
								webp/image-decoder/src/main/cpp/android_debug.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								webp/image-decoder/src/main/cpp/android_debug.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 The Android Open Source Project
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <android/log.h>
 | 
			
		||||
 | 
			
		||||
#if 1
 | 
			
		||||
 | 
			
		||||
#ifndef MODULE_NAME
 | 
			
		||||
#define MODULE_NAME "ImageDecoder"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define LOGV(...) \
 | 
			
		||||
  __android_log_print(ANDROID_LOG_VERBOSE, MODULE_NAME, __VA_ARGS__)
 | 
			
		||||
#define LOGD(...) \
 | 
			
		||||
  __android_log_print(ANDROID_LOG_DEBUG, MODULE_NAME, __VA_ARGS__)
 | 
			
		||||
#define LOGI(...) \
 | 
			
		||||
  __android_log_print(ANDROID_LOG_INFO, MODULE_NAME, __VA_ARGS__)
 | 
			
		||||
#define LOGW(...) \
 | 
			
		||||
  __android_log_print(ANDROID_LOG_WARN, MODULE_NAME, __VA_ARGS__)
 | 
			
		||||
#define LOGE(...) \
 | 
			
		||||
  __android_log_print(ANDROID_LOG_ERROR, MODULE_NAME, __VA_ARGS__)
 | 
			
		||||
#define LOGF(...) \
 | 
			
		||||
  __android_log_print(ANDROID_LOG_FATAL, MODULE_NAME, __VA_ARGS__)
 | 
			
		||||
 | 
			
		||||
#define ASSERT(cond, ...)                                  \
 | 
			
		||||
  if (!(cond)) {                                           \
 | 
			
		||||
    __android_log_assert(#cond, MODULE_NAME, __VA_ARGS__); \
 | 
			
		||||
  }
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
#define LOGV(...)
 | 
			
		||||
#define LOGD(...)
 | 
			
		||||
#define LOGI(...)
 | 
			
		||||
#define LOGW(...)
 | 
			
		||||
#define LOGE(...)
 | 
			
		||||
#define LOGF(...)
 | 
			
		||||
#define ASSERT(cond, ...)
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										107
									
								
								webp/image-decoder/src/main/cpp/android_main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								webp/image-decoder/src/main/cpp/android_main.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 The Android Open Source Project
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "image_viewer.h"
 | 
			
		||||
#include "android_debug.h"
 | 
			
		||||
 | 
			
		||||
static int32_t ProcessAndroidInput(struct android_app *app, AInputEvent *event) {
 | 
			
		||||
    ImageViewer* imageViewer = reinterpret_cast<ImageViewer*>(app->userData);
 | 
			
		||||
    if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
 | 
			
		||||
        imageViewer->StartAnimation(true);
 | 
			
		||||
        return 1;
 | 
			
		||||
    } else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) {
 | 
			
		||||
        LOGI("Key event: action=%d keyCode=%d metaState=0x%x",
 | 
			
		||||
             AKeyEvent_getAction(event),
 | 
			
		||||
             AKeyEvent_getKeyCode(event),
 | 
			
		||||
             AKeyEvent_getMetaState(event));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ProcessAndroidCmd(struct android_app *app, int32_t cmd) {
 | 
			
		||||
    static int32_t format = WINDOW_FORMAT_RGBA_8888;
 | 
			
		||||
    ImageViewer* imageViewer = reinterpret_cast<ImageViewer*>(app->userData);
 | 
			
		||||
    switch (cmd) {
 | 
			
		||||
        case APP_CMD_INIT_WINDOW:
 | 
			
		||||
            if (imageViewer->GetAndroidApp()->window) {
 | 
			
		||||
                // save current format to format variable, and set
 | 
			
		||||
                // display format to 8888
 | 
			
		||||
                format = ANativeWindow_getFormat(app->window);
 | 
			
		||||
                ANativeWindow_setBuffersGeometry(app->window,
 | 
			
		||||
                                                 ANativeWindow_getWidth(app->window),
 | 
			
		||||
                                                 ANativeWindow_getHeight(app->window),
 | 
			
		||||
                                                 WINDOW_FORMAT_RGBA_8888);
 | 
			
		||||
                imageViewer->PrepareDrawing();
 | 
			
		||||
                imageViewer->UpdateDisplay();
 | 
			
		||||
                imageViewer->StartAnimation(true);
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case APP_CMD_TERM_WINDOW:
 | 
			
		||||
            imageViewer->StartAnimation(false);
 | 
			
		||||
            imageViewer->TerminateDisplay();
 | 
			
		||||
            ANativeWindow_setBuffersGeometry(app->window,
 | 
			
		||||
                                             ANativeWindow_getWidth(app->window),
 | 
			
		||||
                                             ANativeWindow_getHeight(app->window),
 | 
			
		||||
                                             format);
 | 
			
		||||
            break;
 | 
			
		||||
        case APP_CMD_LOST_FOCUS:
 | 
			
		||||
            imageViewer->StartAnimation(false);
 | 
			
		||||
            imageViewer->UpdateDisplay();
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Android application glue entry function for us
 | 
			
		||||
extern "C" void android_main(struct android_app* state) {
 | 
			
		||||
 | 
			
		||||
    ImageViewer imageViewer(state);
 | 
			
		||||
 | 
			
		||||
    state->userData = reinterpret_cast<void*>(&imageViewer);
 | 
			
		||||
    state->onAppCmd = ProcessAndroidCmd;
 | 
			
		||||
    state->onInputEvent = ProcessAndroidInput;
 | 
			
		||||
 | 
			
		||||
    // loop waiting for stuff to do.
 | 
			
		||||
    while (1) {
 | 
			
		||||
        // Read all pending events.
 | 
			
		||||
        int ident;
 | 
			
		||||
        int events;
 | 
			
		||||
        struct android_poll_source* source;
 | 
			
		||||
 | 
			
		||||
        // If not animating, we will block forever waiting for events.
 | 
			
		||||
        // If animating, we loop until all events are read, then continue
 | 
			
		||||
        // to draw the next frame of animation.
 | 
			
		||||
        while ((ident = ALooper_pollAll(imageViewer.IsAnimating() ? 0 : -1, NULL, &events,
 | 
			
		||||
                                        (void**)&source)) >= 0) {
 | 
			
		||||
 | 
			
		||||
            // Process this event.
 | 
			
		||||
            if (source != NULL) {
 | 
			
		||||
                source->process(state, source);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Check if we are exiting.
 | 
			
		||||
            if (state->destroyRequested != 0) {
 | 
			
		||||
                LOGI("Engine thread destroy requested!");
 | 
			
		||||
                imageViewer.TerminateDisplay();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (imageViewer.IsAnimating()) {
 | 
			
		||||
            imageViewer.UpdateDisplay();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										57
									
								
								webp/image-decoder/src/main/cpp/display_timer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								webp/image-decoder/src/main/cpp/display_timer.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 The Android Open Source Project
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <ctime>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * DisplayTimer: a utility class to track the display time in nano seconds
 | 
			
		||||
 */
 | 
			
		||||
class DisplayTimer {
 | 
			
		||||
public:
 | 
			
		||||
    explicit DisplayTimer(void) {
 | 
			
		||||
        Reset(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Reset(uint64_t durationInNanoSecond) {
 | 
			
		||||
        durationInNano = durationInNanoSecond;
 | 
			
		||||
        struct timespec cur;
 | 
			
		||||
        clock_gettime(CLOCKS_MONO, &cur);
 | 
			
		||||
        startTime = cur.tv_sec * 1000000000UL + cur.tv_nsec;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool IsExpired(void) {
 | 
			
		||||
        uint64_t timePassed = GetTimePassed();
 | 
			
		||||
        return timePassed >= durationInNano ? true : false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint64_t timeLeft(void) {
 | 
			
		||||
        uint64_t timePassed = GetTimePassed();
 | 
			
		||||
        return (timePassed >= durationInNano) ? 0 : (timePassed - durationInNano);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    uint64_t GetTimePassed(void) {
 | 
			
		||||
        struct timespec cur;
 | 
			
		||||
        clock_gettime(CLOCKS_MONO, &cur);
 | 
			
		||||
        uint64_t curNS = cur.tv_sec * 1000000000UL + cur.tv_nsec;
 | 
			
		||||
 | 
			
		||||
        return (curNS - startTime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint64_t durationInNano;
 | 
			
		||||
    uint64_t startTime;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										240
									
								
								webp/image-decoder/src/main/cpp/image_decoder.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								webp/image-decoder/src/main/cpp/image_decoder.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,240 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021  The Android Open Source Project
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <android/imagedecoder.h>
 | 
			
		||||
#include <android/data_space.h>
 | 
			
		||||
#include "image_decoder.h"
 | 
			
		||||
#include "image_scale_descriptor.h"
 | 
			
		||||
#include "android_debug.h"
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <thread>
 | 
			
		||||
 | 
			
		||||
// Default displaying time image for each frame, used when switching from one image file to
 | 
			
		||||
// the next image file at run time:
 | 
			
		||||
//  - frames inside the animated files would provide the display time, so kDEFAULT_DURATION
 | 
			
		||||
//    is not needed
 | 
			
		||||
//  - non-animated image files will need this.
 | 
			
		||||
const uint64_t kDEFAULT_DURATION = (2 * 1000000000UL);  // 2 second
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ImageDecoder constructor:
 | 
			
		||||
 */
 | 
			
		||||
ImageDecoder::ImageDecoder(const char** files, uint32_t count,
 | 
			
		||||
                           DecodeSurfaceDescriptor *frameBuf,
 | 
			
		||||
                           AAssetManager* mgr)
 | 
			
		||||
    : assetMgr(mgr), curImgFile(nullptr), imgAsset(nullptr),
 | 
			
		||||
      decoder(nullptr),
 | 
			
		||||
      headerInfo(nullptr), frameInfo(nullptr), frameDuration(0UL),
 | 
			
		||||
      decodeState(DecodeState::IDLE),
 | 
			
		||||
      decodedImageStride(0), rotationAngle(0) {
 | 
			
		||||
 | 
			
		||||
    for (auto i = 0; i < count; i++) {
 | 
			
		||||
      fileNames.push(files[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bufInfo = *frameBuf;
 | 
			
		||||
    if (count && frameBuf) {
 | 
			
		||||
        switch (bufInfo.format) {
 | 
			
		||||
            case SurfaceFormat::SURFACE_FORMAT_RGB_565:
 | 
			
		||||
                bytePerPixel = 2;
 | 
			
		||||
                break;
 | 
			
		||||
            case SurfaceFormat::SURFACE_FORMAT_RGBA_8888:
 | 
			
		||||
            case SurfaceFormat::SURFACE_FORMAT_RGBX_8888:
 | 
			
		||||
                bytePerPixel = 4;
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                ASSERT(false, "Unsupported display buffer format(%d) to %s",
 | 
			
		||||
                       bufInfo.format, __FUNCTION__);
 | 
			
		||||
                return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    memset(&targetRect, 0, sizeof(targetRect));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Report decoder is currently decoding frames.
 | 
			
		||||
 * User should call this function before sending command to decode the
 | 
			
		||||
 * next frame.
 | 
			
		||||
 */
 | 
			
		||||
bool ImageDecoder::IsBusy(void) {
 | 
			
		||||
    return (decodeState == DecodeState::BUSY ? true : false);
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
 * DecodeNextImage():  start decode the next image or frame.
 | 
			
		||||
 */
 | 
			
		||||
bool ImageDecoder::DecodeNextImage(void) {
 | 
			
		||||
 | 
			
		||||
    std::unique_lock<std::mutex> decodeLock(decodeMutex);
 | 
			
		||||
 | 
			
		||||
    if(IsBusy())
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    int status;
 | 
			
		||||
    if(!curImgFile) {
 | 
			
		||||
        curImgFile = fileNames.front();
 | 
			
		||||
        fileNames.pop();
 | 
			
		||||
        // Open the asset with the give name from the APK's assets folder.
 | 
			
		||||
        imgAsset =
 | 
			
		||||
                AAssetManager_open(assetMgr, curImgFile, AASSET_MODE_BUFFER);
 | 
			
		||||
        ASSERT(imgAsset, "%s does not exist in %s", curImgFile,
 | 
			
		||||
               __FUNCTION__);
 | 
			
		||||
 | 
			
		||||
        // Create an AImageDecoder from the given AAsset
 | 
			
		||||
        decoder = nullptr;
 | 
			
		||||
        status = AImageDecoder_createFromAAsset(imgAsset, &decoder);
 | 
			
		||||
        ASSERT(ANDROID_IMAGE_DECODER_SUCCESS == status,
 | 
			
		||||
               "Failed to create ImageDecoder for %s with the error of %s",
 | 
			
		||||
               curImgFile, AImageDecoder_resultToString(status));
 | 
			
		||||
 | 
			
		||||
        status = AImageDecoder_setAndroidBitmapFormat(
 | 
			
		||||
                decoder, GetDecodingBitmapFormat());
 | 
			
		||||
        ASSERT(ANDROID_IMAGE_DECODER_SUCCESS == status,
 | 
			
		||||
               "Failed to request decoding output format %s(format: %d)",
 | 
			
		||||
               curImgFile, bufInfo.format);
 | 
			
		||||
 | 
			
		||||
        status = AImageDecoder_setUnpremultipliedRequired(decoder, false);
 | 
			
		||||
        ASSERT(ANDROID_IMAGE_DECODER_SUCCESS == status,
 | 
			
		||||
               "Failed to use pre-multiply alpha for %s", curImgFile);
 | 
			
		||||
 | 
			
		||||
        // scale the image to the max possible size to fit into display window
 | 
			
		||||
        // but keep the image's aspect ratio.
 | 
			
		||||
        headerInfo =AImageDecoder_getHeaderInfo(decoder);
 | 
			
		||||
        ASSERT(headerInfo != nullptr, "Failed to get ImageHeaderInfo for %s",
 | 
			
		||||
               curImgFile);
 | 
			
		||||
 | 
			
		||||
        ARect imgRect = {
 | 
			
		||||
                .left = 0, .top = 0,
 | 
			
		||||
                .right = AImageDecoderHeaderInfo_getWidth(headerInfo),
 | 
			
		||||
                .bottom = AImageDecoderHeaderInfo_getHeight(headerInfo)
 | 
			
		||||
        };
 | 
			
		||||
        ARect dstRect = {
 | 
			
		||||
                .left =0, .top = 0,
 | 
			
		||||
                .right = bufInfo.width,
 | 
			
		||||
                .bottom =bufInfo.height
 | 
			
		||||
        };
 | 
			
		||||
        ImageScaleDescriptor scalerInfo(dstRect, imgRect);
 | 
			
		||||
        rotationAngle = scalerInfo.getScaleRectInfo(&targetRect);
 | 
			
		||||
        status = AImageDecoder_setTargetSize(decoder, targetRect.right - targetRect.left,
 | 
			
		||||
            targetRect.bottom - targetRect.top);
 | 
			
		||||
        ASSERT(ANDROID_IMAGE_DECODER_SUCCESS == status,
 | 
			
		||||
               "Failed to set target rect (%d, %d), error:%s", bufInfo.width, bufInfo.height,
 | 
			
		||||
               AImageDecoder_resultToString(status));
 | 
			
		||||
 | 
			
		||||
        // scRGB/sRGB/SCRGB_LINEAR are okay for this sample
 | 
			
		||||
        ADataSpace dataSpace =
 | 
			
		||||
                static_cast<ADataSpace>(AImageDecoderHeaderInfo_getDataSpace(headerInfo));
 | 
			
		||||
        if (dataSpace != ADATASPACE_SCRGB && dataSpace != ADATASPACE_SRGB &&
 | 
			
		||||
            dataSpace != ADATASPACE_SCRGB_LINEAR) {
 | 
			
		||||
            status = AImageDecoder_setDataSpace(decoder, ADATASPACE_SRGB);
 | 
			
		||||
            ASSERT(ANDROID_IMAGE_DECODER_SUCCESS == status,
 | 
			
		||||
                   "Failed to set SRGB color space for %s", curImgFile);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        decodedImageStride = AImageDecoder_getMinimumStride(decoder);
 | 
			
		||||
 | 
			
		||||
        decodedImageBits.resize(bufInfo.height * decodedImageStride);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    frameDuration =  kDEFAULT_DURATION;
 | 
			
		||||
    if (!frameInfo) {
 | 
			
		||||
        // create frameInfo for each image file, delete it when that file decoding is completed.
 | 
			
		||||
        // for animated image files, frameInfo will be created once and reused for the whole file.
 | 
			
		||||
        frameInfo = AImageDecoderFrameInfo_create();
 | 
			
		||||
        ASSERT(frameInfo, "Failed to create FrameInfo object for %s", curImgFile);
 | 
			
		||||
    }
 | 
			
		||||
    if (AImageDecoder_isAnimated(decoder) &&
 | 
			
		||||
        ANDROID_IMAGE_DECODER_SUCCESS == AImageDecoder_getFrameInfo(decoder, frameInfo)) {
 | 
			
		||||
        frameDuration = AImageDecoderFrameInfo_getDuration(frameInfo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Create and start decoding thread: the thread is detached right after creation,
 | 
			
		||||
    // hence it will run to completion.
 | 
			
		||||
    decodeState = DecodeState::BUSY;
 | 
			
		||||
    std::thread decodingThread([&] () {
 | 
			
		||||
        std::unique_lock<std::mutex> threadLock(decodeMutex);
 | 
			
		||||
        int status = AImageDecoder_decodeImage(decoder,
 | 
			
		||||
                                               decodedImageBits.data(),
 | 
			
		||||
                                               decodedImageStride,
 | 
			
		||||
                                               decodedImageBits.size());
 | 
			
		||||
        ASSERT(status == ANDROID_IMAGE_DECODER_SUCCESS, "Failed to decode image %s",
 | 
			
		||||
               curImgFile);
 | 
			
		||||
 | 
			
		||||
        if(!AImageDecoder_isAnimated(decoder) ||
 | 
			
		||||
           ANDROID_IMAGE_DECODER_SUCCESS != AImageDecoder_advanceFrame(decoder)) {
 | 
			
		||||
            if (frameInfo) {
 | 
			
		||||
                AImageDecoderFrameInfo_delete(frameInfo);
 | 
			
		||||
                frameInfo = nullptr;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            AImageDecoder_delete(decoder);
 | 
			
		||||
            decoder = nullptr;
 | 
			
		||||
            AAsset_close(imgAsset);
 | 
			
		||||
            imgAsset = nullptr;
 | 
			
		||||
            fileNames.push(curImgFile);
 | 
			
		||||
            curImgFile = nullptr;
 | 
			
		||||
        }
 | 
			
		||||
        decodeState = DecodeState::COMPLETED;
 | 
			
		||||
    });
 | 
			
		||||
    decodingThread.detach();
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Report the decoded frame info.
 | 
			
		||||
 * param frame - pointer to data structure for decoded frame info
 | 
			
		||||
 * return bool:
 | 
			
		||||
 *   true - image successfully copied into frame
 | 
			
		||||
 *   false - image is not ready (not decoded yet)
 | 
			
		||||
 */
 | 
			
		||||
bool ImageDecoder::GetDecodedFrame(DecodeFrameDescriptor* _Nonnull frame) {
 | 
			
		||||
    if(decodeState == DecodeState::COMPLETED) {
 | 
			
		||||
        frame->bits = decodedImageBits.data();
 | 
			
		||||
        frame->width = targetRect.right - targetRect.left;
 | 
			
		||||
        frame->height = targetRect.bottom - targetRect.top;
 | 
			
		||||
        frame->stride = decodedImageStride;
 | 
			
		||||
        frame->rotation = rotationAngle;
 | 
			
		||||
        frame->displayDuration = frameDuration;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** GetDecodingBitmapFormat():
 | 
			
		||||
 *     translate the required image format to image decoder's format
 | 
			
		||||
 */
 | 
			
		||||
AndroidBitmapFormat ImageDecoder::GetDecodingBitmapFormat() {
 | 
			
		||||
    switch (bufInfo.format) {
 | 
			
		||||
        case SurfaceFormat::SURFACE_FORMAT_RGBA_8888:
 | 
			
		||||
            return ANDROID_BITMAP_FORMAT_RGBA_8888;
 | 
			
		||||
        case SurfaceFormat::SURFACE_FORMAT_RGB_565:
 | 
			
		||||
            return ANDROID_BITMAP_FORMAT_RGB_565;
 | 
			
		||||
        default:
 | 
			
		||||
            LOGE("Unsupported display buffer format (%d) in %s", bufInfo.format, __FUNCTION__);
 | 
			
		||||
        }
 | 
			
		||||
    return ANDROID_BITMAP_FORMAT_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Destructor to wait for outstanding decoding finish before
 | 
			
		||||
 * tearing down.
 | 
			
		||||
 */
 | 
			
		||||
ImageDecoder::~ImageDecoder() {
 | 
			
		||||
    // make sure the decoding thread is finished
 | 
			
		||||
    std::lock_guard<std::mutex> lock(decodeMutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										114
									
								
								webp/image-decoder/src/main/cpp/image_decoder.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								webp/image-decoder/src/main/cpp/image_decoder.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 The Android Open Source Project
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <queue>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <android/asset_manager.h>
 | 
			
		||||
#include <android/imagedecoder.h>
 | 
			
		||||
 | 
			
		||||
enum class DecodeState { IDLE, BUSY, COMPLETED };
 | 
			
		||||
enum class SurfaceFormat : unsigned int {
 | 
			
		||||
    SURFACE_FORMAT_RGBA_8888,
 | 
			
		||||
    SURFACE_FORMAT_RGBX_8888,
 | 
			
		||||
    SURFACE_FORMAT_RGB_565,
 | 
			
		||||
    SURFACE_FORMAT_YUV_420 // Not implemented yet
 | 
			
		||||
};
 | 
			
		||||
struct DecodeSurfaceDescriptor {
 | 
			
		||||
    // surface size in pixels
 | 
			
		||||
    int32_t width, height, stride;
 | 
			
		||||
    SurfaceFormat format;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct DecodeFrameDescriptor {
 | 
			
		||||
    int32_t width;
 | 
			
		||||
    int32_t height;
 | 
			
		||||
    int32_t stride;
 | 
			
		||||
    uint8_t* _Nullable bits;
 | 
			
		||||
    int32_t rotation;
 | 
			
		||||
    uint64_t displayDuration;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ImageDecoder:
 | 
			
		||||
 *     Select a picture to decode in its own thread. The thread is deleted
 | 
			
		||||
 *     once a picture is decoded into its internal memory. The next picture
 | 
			
		||||
 *     will be decoded when:
 | 
			
		||||
 *       - the decoded frame is retrieved
 | 
			
		||||
 *       - Another DecodeFrame() is called [this is trigger]
 | 
			
		||||
 *    when display format changes, call DestroyDecoder() to release this decoder
 | 
			
		||||
 *    and allocate a new decoder object.
 | 
			
		||||
 */
 | 
			
		||||
class ImageDecoder {
 | 
			
		||||
  public:
 | 
			
		||||
    explicit ImageDecoder(const char* _Nonnull * _Nonnull files, uint32_t count,
 | 
			
		||||
                          DecodeSurfaceDescriptor * _Nullable frameBuf,
 | 
			
		||||
                          AAssetManager* _Nonnull assetMgr);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Report whether the decoder is currently decoding frames.
 | 
			
		||||
     * Caller should:
 | 
			
		||||
     *    - call isBusy() to make sure decoder is not busy
 | 
			
		||||
     *    - call DecoderNextImage() to kick start decoding
 | 
			
		||||
     *    - [Optional] call isBusy() to make sure frame is decoded
 | 
			
		||||
     *    - call getDecodedFrame() to retrieve the decoded bits
 | 
			
		||||
     *    - call DecodeNextImage() again to kick start the next frame decoding
 | 
			
		||||
     *    ...
 | 
			
		||||
     */
 | 
			
		||||
    bool IsBusy(void);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Kick start to decode the next image(or frame). Refer to isBusy() for usage.
 | 
			
		||||
     * This is non-re-entrant function: do not call if previous frame is not completed
 | 
			
		||||
     * yet.
 | 
			
		||||
     * return bool: true - decoding started,
 | 
			
		||||
     *              false - error happened, decoding could not proceed
 | 
			
		||||
     */
 | 
			
		||||
    bool DecodeNextImage(void);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Report the decoded frame info.
 | 
			
		||||
     * param frame - pointer to data structure for decoded frame info
 | 
			
		||||
     * return bool:
 | 
			
		||||
     *   true - image successfully copied into the given frame.
 | 
			
		||||
     *   false - image is not ready (not decoded yet).
 | 
			
		||||
     */
 | 
			
		||||
    bool GetDecodedFrame(DecodeFrameDescriptor* _Nonnull frame);
 | 
			
		||||
 | 
			
		||||
    ~ImageDecoder();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    DecodeSurfaceDescriptor bufInfo;
 | 
			
		||||
    AAssetManager * _Nonnull assetMgr;
 | 
			
		||||
    std::queue<const char* _Nonnull> fileNames;
 | 
			
		||||
    const char*  _Nullable curImgFile;
 | 
			
		||||
    AAsset* _Nullable imgAsset;
 | 
			
		||||
    AImageDecoder*  _Nullable decoder;
 | 
			
		||||
    const AImageDecoderHeaderInfo* _Nullable headerInfo;
 | 
			
		||||
    AImageDecoderFrameInfo* _Nullable frameInfo;
 | 
			
		||||
    uint64_t frameDuration;
 | 
			
		||||
 | 
			
		||||
    std::atomic<DecodeState> decodeState;
 | 
			
		||||
    std::vector<uint8_t>    decodedImageBits;
 | 
			
		||||
    int32_t  decodedImageStride;
 | 
			
		||||
    ARect    targetRect;
 | 
			
		||||
    int32_t  rotationAngle;
 | 
			
		||||
 | 
			
		||||
    uint32_t   bytePerPixel;
 | 
			
		||||
 | 
			
		||||
    std::mutex decodeMutex;
 | 
			
		||||
    AndroidBitmapFormat GetDecodingBitmapFormat();
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										79
									
								
								webp/image-decoder/src/main/cpp/image_scale_descriptor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								webp/image-decoder/src/main/cpp/image_scale_descriptor.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 The Android Open Source Project
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <android/rect.h>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
 | 
			
		||||
class ImageScaleDescriptor {
 | 
			
		||||
  public:
 | 
			
		||||
    explicit ImageScaleDescriptor(ARect dRect, ARect sRect) {
 | 
			
		||||
        requestRect = dRect;
 | 
			
		||||
        srcRect = sRect;
 | 
			
		||||
        memset(&resultRect, 0, sizeof(requestRect));
 | 
			
		||||
        rotationAngle = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculate the min scaled rectangle size.
 | 
			
		||||
     * @param rect: resulting scaled rectangle
 | 
			
		||||
     * @return counter clockwise rotation angle
 | 
			
		||||
     */
 | 
			
		||||
    int32_t getScaleRectInfo(ARect* _Nonnull rect) {
 | 
			
		||||
        bool srcPortrait = true;
 | 
			
		||||
        int32_t srcH = srcRect.bottom - srcRect.top;
 | 
			
		||||
        int32_t srcW = srcRect.right - srcRect.left;
 | 
			
		||||
        if(srcW > srcH) srcPortrait = false;
 | 
			
		||||
 | 
			
		||||
        bool dstPortrait = true;
 | 
			
		||||
        int32_t dstH = requestRect.bottom - requestRect.top;
 | 
			
		||||
        int32_t dstW = requestRect.right - requestRect.left;
 | 
			
		||||
        if(dstW > dstH) dstPortrait = false;
 | 
			
		||||
 | 
			
		||||
        int32_t height =0, width = 0;
 | 
			
		||||
        if(dstPortrait != srcPortrait) {
 | 
			
		||||
            rotationAngle = 270;
 | 
			
		||||
            float hRatio = static_cast<float>(dstH) / srcW;
 | 
			
		||||
            float wRatio = static_cast<float>(dstW) / srcH;
 | 
			
		||||
 | 
			
		||||
            float ratio = (hRatio < wRatio)? hRatio : wRatio;
 | 
			
		||||
 | 
			
		||||
            width = static_cast<int32_t>(ratio * srcW);
 | 
			
		||||
            height = static_cast<int32_t>(ratio * srcH);
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            rotationAngle = 0;
 | 
			
		||||
            float hRatio = static_cast<float>(dstH) / srcH;
 | 
			
		||||
            float wRatio = static_cast<float>(dstW) / srcW;
 | 
			
		||||
            float ratio = (hRatio < wRatio) ? hRatio : wRatio;
 | 
			
		||||
            height = static_cast<int32_t>(ratio * srcH);
 | 
			
		||||
            width = static_cast<int32_t>(ratio * srcW);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        rect->left = 0;
 | 
			
		||||
        rect->right = width;
 | 
			
		||||
        rect->top = 0;
 | 
			
		||||
        rect->bottom = height;
 | 
			
		||||
        return rotationAngle;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~ImageScaleDescriptor() {};
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    ARect requestRect, srcRect, resultRect;
 | 
			
		||||
    int32_t rotationAngle;  // Counter Clock rotation Angle
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										278
									
								
								webp/image-decoder/src/main/cpp/image_viewer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								webp/image-decoder/src/main/cpp/image_viewer.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,278 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 The Android Open Source Project
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <android/native_window.h>
 | 
			
		||||
#include <android_native_app_glue.h>
 | 
			
		||||
#include "image_viewer.h"
 | 
			
		||||
#include "image_decoder.h"
 | 
			
		||||
#include "display_timer.h"
 | 
			
		||||
#include "android_debug.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Compressed files that are inside assets folder:
 | 
			
		||||
 *    they will be decoded and displayed as slide-show
 | 
			
		||||
 *    decoding will happen in its own thread
 | 
			
		||||
 */
 | 
			
		||||
const char * imageFiles[] = {
 | 
			
		||||
        "images/android.webp",
 | 
			
		||||
        "images/android.avif",
 | 
			
		||||
        "images/android.heic",
 | 
			
		||||
};
 | 
			
		||||
const int kFRAME_COUNT = sizeof(imageFiles) / sizeof(imageFiles[0]);
 | 
			
		||||
 | 
			
		||||
ImageViewer::ImageViewer(android_app* _Nullable app) :
 | 
			
		||||
    androidApp(app),
 | 
			
		||||
    imgDecoder(nullptr),
 | 
			
		||||
    animatingInProgress(false) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ImageViewer::~ImageViewer() {
 | 
			
		||||
    delete imgDecoder;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct android_app* _Nullable ImageViewer::GetAndroidApp(void) const{
 | 
			
		||||
    return androidApp;
 | 
			
		||||
}
 | 
			
		||||
void ImageViewer::StartAnimation(bool start) {
 | 
			
		||||
    animatingInProgress = start;
 | 
			
		||||
}
 | 
			
		||||
bool ImageViewer::IsAnimating(void) const {
 | 
			
		||||
    return animatingInProgress;
 | 
			
		||||
}
 | 
			
		||||
void ImageViewer::TerminateDisplay(void) {
 | 
			
		||||
    StartAnimation(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Engine class implementations
 | 
			
		||||
bool ImageViewer::PrepareDrawing(void) {
 | 
			
		||||
    // create decoder
 | 
			
		||||
    if (imgDecoder) {
 | 
			
		||||
        delete imgDecoder;
 | 
			
		||||
        imgDecoder = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    ANativeWindow_Buffer buf;
 | 
			
		||||
    if (ANativeWindow_lock(androidApp->window, &buf, NULL) < 0) {
 | 
			
		||||
        LOGW("Unable to lock window buffer to create decoder");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    ClearFrameBuffer(&buf);
 | 
			
		||||
    ANativeWindow_unlockAndPost(androidApp->window);
 | 
			
		||||
 | 
			
		||||
    DecodeSurfaceDescriptor descriptor;
 | 
			
		||||
    switch (buf.format) {
 | 
			
		||||
        case  WINDOW_FORMAT_RGB_565:
 | 
			
		||||
            descriptor.format = SurfaceFormat::SURFACE_FORMAT_RGB_565;
 | 
			
		||||
            break;
 | 
			
		||||
        case WINDOW_FORMAT_RGBX_8888:
 | 
			
		||||
            descriptor.format = SurfaceFormat::SURFACE_FORMAT_RGBX_8888;
 | 
			
		||||
            break;
 | 
			
		||||
        case WINDOW_FORMAT_RGBA_8888:
 | 
			
		||||
            descriptor.format = SurfaceFormat::SURFACE_FORMAT_RGBA_8888;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
    descriptor.width  = buf.width;
 | 
			
		||||
    descriptor.height = buf.height;
 | 
			
		||||
    descriptor.stride = buf.stride;
 | 
			
		||||
 | 
			
		||||
    imgDecoder = new ImageDecoder(imageFiles, kFRAME_COUNT, &descriptor, androidApp->activity->assetManager);
 | 
			
		||||
 | 
			
		||||
    if (!imgDecoder) {
 | 
			
		||||
        LOGE("Unable to create ImageDecoder instance in %s", __FUNCTION__);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Kick start the 1st image decoding
 | 
			
		||||
    imgDecoder->DecodeNextImage();
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Only copy decoded webp picture when:
 | 
			
		||||
 *  - current frame has been on for kFrame_DISPLAY_TIME seconds
 | 
			
		||||
 *  - a new picture is decoded
 | 
			
		||||
 * After copying, start decoding the next frame
 | 
			
		||||
 */
 | 
			
		||||
bool ImageViewer::UpdateDisplay(void) {
 | 
			
		||||
    if (!androidApp->window || !imgDecoder) {
 | 
			
		||||
        LOGE("Decoder(%p) or NativeWindow(%p) is not ready in %s",
 | 
			
		||||
              androidApp->window, imgDecoder, __FUNCTION__);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(!displayTimer.IsExpired() || imgDecoder->IsBusy()) {
 | 
			
		||||
        // current frame is displayed less than required duration or image is not ready
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DecodeFrameDescriptor rawImg;
 | 
			
		||||
    if(!imgDecoder->GetDecodedFrame(&rawImg))
 | 
			
		||||
        return false;
 | 
			
		||||
 | 
			
		||||
    ANativeWindow_Buffer buffer;
 | 
			
		||||
    if (ANativeWindow_lock(androidApp->window, &buffer, nullptr) < 0) {
 | 
			
		||||
        LOGW("Unable to lock window buffer");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    ClearFrameBuffer(&buffer); // clear the screen
 | 
			
		||||
    UpdateFrameBuffer(&buffer, &rawImg);
 | 
			
		||||
    ANativeWindow_unlockAndPost(androidApp->window);
 | 
			
		||||
 | 
			
		||||
    displayTimer.Reset(rawImg.displayDuration);
 | 
			
		||||
 | 
			
		||||
    imgDecoder->DecodeNextImage();
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Internal function to translate ANativeWindow format to byte per pixel.
 | 
			
		||||
 * @param buf: points to the ANativeWindow format.
 | 
			
		||||
 * @return int32_t: byte per pixel for the give ANativeWindow
 | 
			
		||||
 */
 | 
			
		||||
int32_t ImageViewer::getNativeWindowBpp(ANativeWindow_Buffer* _Nonnull buf) {
 | 
			
		||||
    uint32_t bpp = 0;
 | 
			
		||||
    switch (buf->format) {
 | 
			
		||||
        case WINDOW_FORMAT_RGB_565:
 | 
			
		||||
            bpp = 2;
 | 
			
		||||
            break;
 | 
			
		||||
        case WINDOW_FORMAT_RGBA_8888:
 | 
			
		||||
        case WINDOW_FORMAT_RGBX_8888:
 | 
			
		||||
            bpp = 4;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            LOGE("Could not recognize the NativeWindow format: %d in %s",
 | 
			
		||||
                 buf->format, __FUNCTION__);
 | 
			
		||||
            assert(0);;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return bpp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ClearFrameBuffer: zero out the framebuffer
 | 
			
		||||
 * @param buf: the display buffer to clear
 | 
			
		||||
 */
 | 
			
		||||
void ImageViewer::ClearFrameBuffer(ANativeWindow_Buffer * _Nonnull buf) {
 | 
			
		||||
    uint8_t *dst = reinterpret_cast<uint8_t *> (buf->bits);
 | 
			
		||||
 | 
			
		||||
    int32_t bpp = getNativeWindowBpp(buf);
 | 
			
		||||
    uint32_t dstStride, width;
 | 
			
		||||
    dstStride = buf->stride * bpp;
 | 
			
		||||
    width = buf->width * bpp;
 | 
			
		||||
 | 
			
		||||
    // blank the screen
 | 
			
		||||
    for (auto height = 0; height < buf->height; ++height) {
 | 
			
		||||
        memset(dst, 0, width);
 | 
			
		||||
        dst += dstStride;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/**
 | 
			
		||||
 * UpdateFrameBuffer():
 | 
			
		||||
 *     Internal function to perform bits copying onto current frame buffer
 | 
			
		||||
 * @param buf: already locked-down target NativeWindow to update,
 | 
			
		||||
 * @param frame: already decoded raw image to display.
 | 
			
		||||
 *        - if nullptr, clear screen
 | 
			
		||||
 *        - otherwise, pixel by pixel copy with letterbox enabled
 | 
			
		||||
 */
 | 
			
		||||
void ImageViewer::UpdateFrameBuffer(ANativeWindow_Buffer * _Nonnull buf,
 | 
			
		||||
        DecodeFrameDescriptor* _Nonnull frame) {
 | 
			
		||||
 | 
			
		||||
    uint8_t *dst = reinterpret_cast<uint8_t *> (buf->bits);
 | 
			
		||||
 | 
			
		||||
    int32_t bpp = getNativeWindowBpp(buf);
 | 
			
		||||
    uint32_t dstStride, width;
 | 
			
		||||
    dstStride = buf->stride * bpp;
 | 
			
		||||
    width = buf->width * bpp;
 | 
			
		||||
 | 
			
		||||
    // perform pixel by pixel copying.
 | 
			
		||||
    uint8_t *src = frame->bits;
 | 
			
		||||
    int32_t start_x;
 | 
			
		||||
    int32_t start_y;
 | 
			
		||||
    if(frame->rotation == 0 || frame->rotation == 180) {
 | 
			
		||||
        start_x = (buf->width - frame->width) / 2;
 | 
			
		||||
        start_y = (buf->height - frame->height) / 2;
 | 
			
		||||
    } else if (frame->rotation == 90 || frame->rotation == 270){
 | 
			
		||||
        // need to rotate 90 or 270 degree
 | 
			
		||||
        start_x = (buf->width - frame->height) / 2;
 | 
			
		||||
        start_y = (buf->height - frame->width) / 2;
 | 
			
		||||
    } else {
 | 
			
		||||
        LOGE("Wrong frame->rotation(%d) to %s", frame->rotation, __FUNCTION__ );
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    assert(start_x >= 0 && start_y >= 0);
 | 
			
		||||
    dst += (start_y * dstStride) + (start_x * bpp);
 | 
			
		||||
 | 
			
		||||
    // line by line copying for 0 degree rotation
 | 
			
		||||
    if(frame->rotation == 0) {
 | 
			
		||||
        for (auto height = 0; height < frame->height; ++height) {
 | 
			
		||||
            memcpy(dst, src, width);
 | 
			
		||||
            dst += dstStride, src += frame->stride;
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(frame->rotation == 180) {
 | 
			
		||||
        src += frame->stride * (frame->height - 1);
 | 
			
		||||
        for (auto height = 0; height < frame->height; ++height) {
 | 
			
		||||
            memcpy(dst, src, width);
 | 
			
		||||
            dst += dstStride, src -= frame->stride;
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // one pixel at a time, rotate on the fly
 | 
			
		||||
    if (frame->rotation == 90) {
 | 
			
		||||
        // one pixel at a time, rotate on the fly
 | 
			
		||||
        src += (frame->width - 1) * bpp;
 | 
			
		||||
 | 
			
		||||
        for (auto height = 0; height < frame->width; height++) {
 | 
			
		||||
            uint8_t *srcLine = src;
 | 
			
		||||
            uint8_t *dstLine = dst;
 | 
			
		||||
            for (auto w = 0; w < frame->height; w++) {
 | 
			
		||||
                memcpy(dstLine, srcLine, bpp);
 | 
			
		||||
 | 
			
		||||
                dstLine += bpp;             // advance one pixel
 | 
			
		||||
                srcLine += frame->stride;   // move down a line
 | 
			
		||||
            }
 | 
			
		||||
            dst += dstStride;  // advance 1 line
 | 
			
		||||
            src -= bpp;        // move 1 pixel backward for the next source column
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(frame->rotation == 270) {
 | 
			
		||||
        src += (frame->height - 1) * frame->stride;
 | 
			
		||||
        for (auto height = 0; height < frame->width; height++) {
 | 
			
		||||
            uint8_t *srcLine = src;
 | 
			
		||||
            uint8_t *dstLine = dst;
 | 
			
		||||
            for (auto w = 0; w < frame->height; w++) {
 | 
			
		||||
                memcpy(dstLine, srcLine, bpp);
 | 
			
		||||
                dstLine += bpp;             // advance one pixel
 | 
			
		||||
                srcLine -= frame->stride;   // move up a line
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            dst += dstStride;  // advance 1 line
 | 
			
		||||
            src += bpp;        // move 1 pixel forward for the next source column
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										59
									
								
								webp/image-decoder/src/main/cpp/image_viewer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								webp/image-decoder/src/main/cpp/image_viewer.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 The Android Open Source Project
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <android/native_window.h>
 | 
			
		||||
#include <android_native_app_glue.h>
 | 
			
		||||
#include "image_decoder.h"
 | 
			
		||||
#include "display_timer.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Main object handles Android window frame update, and use webp to decode
 | 
			
		||||
 * pictures
 | 
			
		||||
 */
 | 
			
		||||
class ImageViewer {
 | 
			
		||||
  public:
 | 
			
		||||
    explicit ImageViewer(android_app* _Nullable app);
 | 
			
		||||
    ~ImageViewer();
 | 
			
		||||
 | 
			
		||||
    // Functions to connect to native_activity
 | 
			
		||||
    struct android_app* _Nullable GetAndroidApp(void) const;
 | 
			
		||||
    void StartAnimation(bool start);
 | 
			
		||||
    bool IsAnimating(void) const;
 | 
			
		||||
    void TerminateDisplay(void);
 | 
			
		||||
 | 
			
		||||
     // PrepareDrawing(): Initialize the Engine with current native window geometry
 | 
			
		||||
     //   and blank current screen to avoid garbage displaying on device
 | 
			
		||||
    bool PrepareDrawing(void);
 | 
			
		||||
 | 
			
		||||
    // Update the next image into display window when
 | 
			
		||||
    //    - decoding is completed, and
 | 
			
		||||
    //    - the current image has been displayed longer than the requested time
 | 
			
		||||
    bool UpdateDisplay(void);
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
    void ClearFrameBuffer(ANativeWindow_Buffer* _Nonnull buf);
 | 
			
		||||
    void UpdateFrameBuffer(ANativeWindow_Buffer * _Nonnull buf,
 | 
			
		||||
            DecodeFrameDescriptor* _Nonnull frame);
 | 
			
		||||
    int32_t getNativeWindowBpp(ANativeWindow_Buffer* _Nonnull buf);
 | 
			
		||||
 | 
			
		||||
    struct android_app* _Nullable androidApp;
 | 
			
		||||
    ImageDecoder* _Nullable imgDecoder;
 | 
			
		||||
    bool animatingInProgress;
 | 
			
		||||
    DisplayTimer displayTimer;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								webp/image-decoder/src/main/res/mipmap-hdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								webp/image-decoder/src/main/res/mipmap-hdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 3.3 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								webp/image-decoder/src/main/res/mipmap-mdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								webp/image-decoder/src/main/res/mipmap-mdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 2.2 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								webp/image-decoder/src/main/res/mipmap-xhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								webp/image-decoder/src/main/res/mipmap-xhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 4.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								webp/image-decoder/src/main/res/mipmap-xxhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								webp/image-decoder/src/main/res/mipmap-xxhdpi/ic_launcher.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 7.5 KiB  | 
							
								
								
									
										4
									
								
								webp/image-decoder/src/main/res/values/strings.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								webp/image-decoder/src/main/res/values/strings.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<resources>
 | 
			
		||||
    <string name="app_name">NdkImageDecoder</string>
 | 
			
		||||
</resources>
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
include ':view'
 | 
			
		||||
include ':image-decoder'
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user