Compare commits

...
74 Commits
Author SHA1 Message Date
tarsin 722fbec72c 1.8.0.3
CI / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-07-29 18:08:31 +08:00
tarsin 22190fb686 Fix a double-free problem 2022-07-28 23:45:35 +08:00
txyyh 49ea70c839 Add monochrome icon (#144) 2022-07-28 12:06:56 +08:00
tarsin c655e7e1c9 Drop blank res 2022-07-28 11:28:45 +08:00
tarsin d13cd1e07f 1.8.0.2
CI / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-07-28 10:37:07 +08:00
tarsin 7365fd0448 Fix a memleak 2022-07-28 10:33:47 +08:00
txyyh f1febca312 Let renovate bot detect two branch 2022-07-28 04:12:33 +08:00
renovate[bot] dbf67404c0 Update fragment_version to v1.5.1 (#138)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-07-28 03:39:42 +08:00
renovate[bot] 8cfe8c4b80 Update collection_version to v1.3.0-alpha02 (#137)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-07-28 03:39:25 +08:00
tarsin 674f53315f Code clean 2022-07-27 19:46:39 +08:00
tarsin e51cd6b90d Drop taptargetview
* Not MD3
2022-07-25 20:44:59 +08:00
tarsin a44f12b2e0 Reformat Code 2022-07-25 20:07:16 +08:00
tarsin a2552ed5b9 Reformat 2022-07-25 20:05:38 +08:00
tarsin 763c007950 Log tweaks 2022-07-25 20:02:47 +08:00
tarsin b221bf8e52 Release 1.8.0.1
CI / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-07-25 19:16:07 +08:00
tarsin ab9a89685c Code clean 2022-07-25 19:02:10 +08:00
tarsin 41ccb16227 Code clean 2022-07-25 18:42:18 +08:00
tarsin f6bf7b1ba6 No need to thanks image decs anymore 2022-07-25 13:06:24 +08:00
tarsin 09e7113df1 Fix a dirty dealloc 2022-07-25 12:23:52 +08:00
tarsin 62a7e283a4 Should be able to support all format now 2022-07-25 10:51:57 +08:00
tarsin a7f7bf1316 Release 1.8
CI / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-07-25 10:20:16 +08:00
tarsin a72fca3dfb Fix a lag 2022-07-25 10:16:54 +08:00
tarsin c5e7a6e8f9 Code clean 2022-07-25 10:02:34 +08:00
tarsin 80a20f25b9 Fix animated 2022-07-25 09:28:40 +08:00
tarsin 500163fe1a Merge pull request #132 from Ehviewer-Overhauled/master
Pull master
2022-07-25 07:54:04 +08:00
txyyh 513886eff4 pick gallery_detail_header.xml changes to television (#131)
* Rewrite info TableLayout to ConstraintLayout for television

* Rewrite layout and minor design improve for television
2022-07-25 07:50:58 +08:00
txyyh 0093db9986 Make buttongroup horizontal (#125)
* Use another impl to create space

* Horizontal button
2022-07-25 07:50:41 +08:00
tarsin 50c6111773 Imagedecoder do not support anon pipe, drop it and use memarea 2022-07-24 23:32:03 +08:00
tarsin 3a05649c71 Code clean 2022-07-24 22:34:19 +08:00
tarsin 9d25376e1f Refactor 2022-07-24 21:41:53 +08:00
tarsin 89935b5639 Hard cast 2022-07-24 20:32:39 +08:00
tarsin e095627aee Refactor 2022-07-24 20:20:04 +08:00
tarsin b61e46e956 Clean up 2022-07-24 19:49:34 +08:00
tarsin 76459c5dd2 Initial stupid migrate to ImageDecoder 2022-07-24 19:18:54 +08:00
tarsin 7ad67a3dbd Minsdk 31 2022-07-24 18:24:24 +08:00
tarsin 577b49ac19 Drop image decode native libs 2022-07-24 18:17:37 +08:00
txyyh 182e3a0d40 Rewrite README description (#124) 2022-07-24 17:40:19 +08:00
txyyh 6d140aaa9d Delete FOSSA (#123) 2022-07-24 14:24:31 +08:00
tarsin 17248d3a82 Revert "Use SMAPSHOT version of mdc"
This reverts commit 7a0fab29bc.
2022-07-24 13:07:28 +08:00
txyyh ec80e7a189 Back to corner (#121) 2022-07-24 05:36:07 +08:00
txyyh 493eeadf1b Add note for CI download (#122) 2022-07-24 03:35:28 +08:00
tarsin 7a0fab29bc Use SMAPSHOT version of mdc 2022-07-23 20:41:02 +08:00
hathlife 5f7e40f258 Gallery detail head tv layout (#96)
* Make height of App logo ImageView match parent

* Add TV layout for gallery detail scene.

* Add credits

* Use GPLv3 for all TV layout code
2022-07-23 20:27:42 +08:00
txyyh 0bf6b8d827 Release 1.7.29.1
CI / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-07-23 17:14:25 +08:00
tarsin 4c6ccab051 Native lib info refactor 2022-07-23 16:25:53 +08:00
tarsin 00377f15b9 Set our own libs 2022-07-23 16:07:23 +08:00
tarsin 96b50eb5aa Add nettle for encrypt 2022-07-23 15:51:43 +08:00
tarsin acdf681d40 Add gnutls libnettle 2022-07-23 14:26:17 +08:00
tarsin b92cf9746b Add .gz .xz .tar .cbz .cbr to supported mimeTypes 2022-07-23 13:28:41 +08:00
txyyh d0dfbaaae0 Rewrite layout and minor design improve (#116)
* Use ConstraintLayout instead

* Specify info Card Text to BodySmall

* Specify thumb shape to none (no corner)
2022-07-23 07:44:33 +08:00
txyyh 0c656e617b Add CI download link (#117)
* Add CI Download link

* Use head in right way
2022-07-23 00:49:03 +08:00
tarsin 148b1a6344 Release 1.7.29.0
CI / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-07-22 23:12:27 +08:00
tarsin 1fe0b079cd Support auto store archive's passwd 2022-07-22 22:53:44 +08:00
tarsin 87505aa989 Code clean 2022-07-22 22:18:41 +08:00
tarsin 3d7a71647e Initial support for encrypted archive read 2022-07-22 21:28:19 +08:00
txyyh c9d2f698b6 Add MDC-Android to Thanks (#113) 2022-07-22 16:51:52 +08:00
tarsin 233adbddae Drop another spinner 2022-07-22 14:14:29 +08:00
tarsin 6900d8a8c9 Rename .java to .kt 2022-07-22 14:14:29 +08:00
tarsin 640f75d8d9 Finally our data is no need to pass any jByteArray 2022-07-22 10:08:04 +08:00
tarsin 4381eb8c3d Fuck off the buffer 2022-07-22 09:52:35 +08:00
tarsin 3026b16d70 Don't emulate posix read, do mmap instead 2022-07-22 00:13:53 +08:00
txyyh ac3662a469 Release 1.7.28.4
CI / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-07-22 00:07:31 +08:00
tarsin 912e6c5a5b Fix a miscompare 2022-07-21 22:25:50 +08:00
txyyh 6a73a51f63 Rewrite info TableLayout to ConstraintLayout (#111) 2022-07-21 22:13:07 +08:00
tarsin 768b6bdda7 Don't rewind fd if we can continue read 2022-07-21 22:11:50 +08:00
tarsin 9ea7cd5f7c JNIEnv is shared in the same thread 2022-07-21 21:56:32 +08:00
tarsin a91547ace7 Use in-place decompress 2022-07-21 20:47:55 +08:00
tarsin ed58085b8f Update AndroidManifest.xml
https://developer.android.google.cn/about/versions/13/behavior-changes-13?hl=zh-cn#migration
2022-07-20 21:10:07 +08:00
txyyh 944e267795 Separate different action (#107)
* Rename ci action

* Separate push and pull request action

* Change README CI shield

* Rename artifact to right name
2022-07-20 19:22:59 +08:00
txyyh 325d6df2d3 Add thanks (#106) 2022-07-20 07:57:02 +08:00
txyyh 3ba08bf003 Auto-gen releases discussion (#105) 2022-07-20 07:56:49 +08:00
tarsin 86d00cc0d1 Use mdc's slider 2022-07-19 22:17:40 +08:00
tarsin 4ee518451c Release 1.7.28.3 2022-07-19 19:42:10 +08:00
tarsin 7b167b1612 Check to our xz fork 2022-07-19 19:01:03 +08:00
124 changed files with 2533 additions and 6992 deletions

No files matched your search

@@ -1,6 +1,6 @@
name: EhViewer
name: CI
on: [push, pull_request]
on: [push]
jobs:
linux:
@@ -32,7 +32,7 @@ jobs:
- name: Update ARM64
uses: actions/upload-artifact@v3
with:
name: arm64-${{ github.sha }}
name: arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
- name: Update x86_64
+54
View File
@@ -0,0 +1,54 @@
name: Pull Request Check
on: [pull_request]
jobs:
linux:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Java 11
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
- name: Setup Android SDK Tools
uses: android-actions/setup-android@v2
- name: Install NDK
run: |
echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;25.0.8775105" --sdk_root=${ANDROID_SDK_ROOT} &> /dev/null
- name: Gradle Build
uses: gradle/gradle-build-action@v2
- run: |
git submodule update --init --recursive
gradle assembleRelease
- name: Update ARM64
uses: actions/upload-artifact@v3
with:
name: arm64-v8a-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
- name: Update x86_64
uses: actions/upload-artifact@v3
with:
name: x86_64-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86_64-release.apk
- name: Update ARM32
uses: actions/upload-artifact@v3
with:
name: armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/release/app-armeabi-v7a-release.apk
- name: Update x86
uses: actions/upload-artifact@v3
with:
name: x86-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86-release.apk
+1
View File
@@ -36,6 +36,7 @@ jobs:
uses: softprops/action-gh-release@v0.1.14
with:
body: Bump Version
discussion_category_name: Announcements
files: |
app/build/outputs/apk/release/app-arm64-v8a-release.apk
app/build/outputs/apk/release/app-x86_64-release.apk
+4 -10
View File
@@ -1,15 +1,9 @@
[submodule "native/src/main/jni/image/libjpeg-turbo/libjpeg-turbo"]
path = native/src/main/jni/image/libjpeg-turbo/libjpeg-turbo
url = https://github.com/Ehviewer-Overhauled/libjpeg-turbo
[submodule "native/src/main/jni/image/giflib/giflib"]
path = native/src/main/jni/image/giflib/giflib
url = https://github.com/Ehviewer-Overhauled/giflib
[submodule "native/src/main/jni/image/libpng/libpng"]
path = native/src/main/jni/image/libpng/libpng
url = https://github.com/Ehviewer-Overhauled/libpng
[submodule "native/src/main/jni/libarchive"]
path = native/src/main/jni/libarchive
url = https://github.com/Ehviewer-Overhauled/libarchive
[submodule "native/src/main/jni/liblzma"]
path = native/src/main/jni/liblzma
url = https://git.tukaani.org/xz.git
url = https://github.com/Ehviewer-Overhauled/xz
[submodule "native/src/main/jni/nettle/nettle"]
path = native/src/main/jni/nettle/nettle
url = https://github.com/Ehviewer-Overhauled/nettle
+15 -20
View File
@@ -4,8 +4,8 @@
</h1>
<p align="center">
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/actions">
<img src="https://img.shields.io/github/workflow/status/Ehviewer-Overhauled/Ehviewer/EhViewer" alt="Github Actions">
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/actions/workflows/ci.yml">
<img src="https://github.com/Ehviewer-Overhauled/Ehviewer/actions/workflows/ci.yml/badge.svg" alt="Github Actions">
</a>
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/Ehviewer-Overhauled/Ehviewer" alt="LICENSE">
@@ -21,30 +21,29 @@
</a>
</p>
## Description
这是一个 E-Hentai Android 平台的浏览器
# Description
致力于轻量化与良好性能表现的 EhViewer 分支
已针对性适配 Material Design 3 及其动态取色系统
动态配色仅供 Android 12 及以上平台使用!
EhViewer fork dedicated to lightweight and high-performance
An E-Hentai Application for Android.
with Material Design 3 and Dynamic Color Support
with Material Design 3 and Dynamic Color Support.
Dynamic Color only support Android S and above!
## Download
# Download
[Github 下载](https://github.com/Ehviewer-Overhauled/Ehviewer/releases)
## Screenshot
[CI 版本下载(需登录)](https://github.com/Ehviewer-Overhauled/Ehviewer/actions/workflows/ci.yml)
# Screenshot
![screenshot-01](art/screenshot-01.png)
![screenshot-02](art/screenshot-02.png)
## Thanks
# Thanks
本项目受到了诸多开源项目的帮助
@@ -52,13 +51,11 @@ Here is the libraries
- [AOSP](http://source.android.com/)
- [android-advancedrecyclerview](https://github.com/h6ah4i/android-advancedrecyclerview)
- [material-components-android](https://github.com/material-components/material-components-android)
- [material-design-icons](https://github.com/google/material-design-icons)
- [Apache Commons Lang](https://commons.apache.org/proper/commons-lang/)
- [apng](http://apng.sourceforge.net/)
- [giflib](http://giflib.sourceforge.net)
- [greenDAO](https://github.com/greenrobot/greenDAO)
- [jsoup](https://github.com/jhy/jsoup)
- [libjpeg-turbo](https://libjpeg-turbo.org/)
- [libpng](http://www.libpng.org/pub/png/libpng.html)
- [okhttp](https://github.com/square/okhttp)
- [roaster](https://github.com/forge/roaster)
- [TapTargetView](https://github.com/KeepSafe/TapTargetView)
@@ -66,7 +63,7 @@ Here is the libraries
- [XZ Utils](https://tukaani.org/xz/)
## License
# License
Copyright 2014-2019 Hippo Seven
Copyright 2020-2022 NekoInverter
@@ -77,5 +74,3 @@ Here is the libraries
EhViewer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with EhViewer. If not, see <https://www.gnu.org/licenses/>.
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fasuka-mio%2FEhviewer.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fasuka-mio%2FEhviewer?ref=badge_large)
+5 -6
View File
@@ -32,10 +32,10 @@ android {
defaultConfig {
applicationId "moe.tarsin.ehviewer"
minSdkVersion 26
minSdkVersion 31
targetSdkVersion 33
versionCode 114529
versionName "1.7.28.2"
versionCode 180003
versionName "1.8.0.3"
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
}
@@ -107,7 +107,7 @@ dependencies {
implementation "androidx.biometric:biometric-ktx:1.2.0-alpha04"
implementation "androidx.browser:browser:1.4.0"
def collection_version = "1.3.0-alpha01"
def collection_version = "1.3.0-alpha02"
implementation "androidx.collection:collection:$collection_version"
implementation "androidx.collection:collection-ktx:$collection_version"
@@ -122,7 +122,7 @@ dependencies {
implementation "androidx.documentfile:documentfile:1.1.0-alpha01"
implementation "androidx.dynamicanimation:dynamicanimation:1.1.0-alpha03"
def fragment_version = "1.5.0"
def fragment_version = "1.5.1"
implementation "androidx.fragment:fragment:$fragment_version"
implementation "androidx.fragment:fragment-ktx:$fragment_version"
@@ -148,7 +148,6 @@ dependencies {
implementation 'com.drakeet.drawer:drawer:1.0.3'
implementation 'com.google.android.material:material:1.7.0-alpha03'
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.13.3'
implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0'
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.10'
implementation 'dev.rikka.rikkax.core:core-ktx:1.4.1'
+18 -30
View File
@@ -26,9 +26,11 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- Required to maintain app compatibility. -->
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-feature
android:glEsVersion="0x00020000"
@@ -119,35 +121,21 @@
<data android:mimeType="application/x-7z-compressed" />
<data android:mimeType="application/x-rar-compressed" />
<data android:mimeType="application/x-zip-compressed" />
</intent-filter>
<data android:mimeType="application/x-tar" />
<data android:mimeType="application/x-xz" />
<data android:mimeType="application/gzip" />
<data android:mimeType="application/x-cbz" />
<data android:mimeType="application/x-cbr" />
<data android:mimeType="application/x-cbt" />
<data android:mimeType="application/x-cb7" />
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:host="*" />
<data android:pathPattern=".*\\.7z" />
<data android:pathPattern=".*\\.rar" />
<data android:pathPattern=".*\\.zip" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:host="*" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.7z" />
<data android:pathPattern=".*\\.rar" />
<data android:pathPattern=".*\\.zip" />
<!-- untested -->
<data android:mimeType="application/vnd.comicbook+zip" />
<data android:mimeType="application/vnd.comicbook-rar" />
<data android:mimeType="application/x-compressed-tar" />
<data android:mimeType="application/x-bzip-compressed-tar" />
<data android:mimeType="application/x-lzma-compressed-tar" />
<data android:mimeType="application/x-xz-compressed-tar" />
</intent-filter>
</activity>
@@ -49,7 +49,7 @@ class EditTextDialogBuilder constructor(
return mDialog as AlertDialog
}
override fun onEditorAction(v: TextView, actionId: Int, event: KeyEvent): Boolean {
override fun onEditorAction(v: TextView?, p1: Int, event: KeyEvent?): Boolean {
return if (mDialog != null) {
val button = mDialog!!.getButton(DialogInterface.BUTTON_POSITIVE)
button?.performClick()
@@ -167,6 +167,7 @@ public final class DiskLruCache implements Closeable {
* its sequence number is not equal to its entry's sequence number.
*/
private long nextSequenceNumber = 0;
private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) {
this.directory = directory;
this.appVersion = appVersion;
@@ -175,21 +176,7 @@ public final class DiskLruCache implements Closeable {
this.journalFileBackup = new File(directory, JOURNAL_FILE_BACKUP);
this.valueCount = valueCount;
this.maxSize = maxSize;
} private final Callable<Void> cleanupCallable = new Callable<>() {
public Void call() throws Exception {
synchronized (DiskLruCache.this) {
if (journalWriter == null) {
return null; // Closed.
}
trimToSize();
if (journalRebuildRequired()) {
rebuildJournal();
redundantOpCount = 0;
}
}
return null;
}
};
}
/**
* Opens the cache in {@code directory}, creating a cache if none exists
@@ -244,7 +231,21 @@ public final class DiskLruCache implements Closeable {
cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
cache.rebuildJournal();
return cache;
}
} private final Callable<Void> cleanupCallable = new Callable<>() {
public Void call() throws Exception {
synchronized (DiskLruCache.this) {
if (journalWriter == null) {
return null; // Closed.
}
trimToSize();
if (journalRebuildRequired()) {
rebuildJournal();
redundantOpCount = 0;
}
}
return null;
}
};
private static void deleteIfExists(File file) throws IOException {
if (file.exists() && !file.delete()) {
@@ -23,6 +23,7 @@ import com.hippo.conaco.ValueHelper;
import com.hippo.image.ImageBitmap;
import com.hippo.streampipe.InputStreamPipe;
import java.io.FileInputStream;
import java.io.IOException;
public class ImageBitmapHelper implements ValueHelper<ImageBitmap> {
@@ -34,7 +35,8 @@ public class ImageBitmapHelper implements ValueHelper<ImageBitmap> {
public ImageBitmap decode(@NonNull InputStreamPipe isPipe) {
try {
isPipe.obtain();
return ImageBitmap.decode(isPipe.open());
FileInputStream is = (FileInputStream) isPipe.open();
return ImageBitmap.decode(is.getFD());
} catch (OutOfMemoryError | IOException e) {
return null;
} finally {
@@ -41,6 +41,7 @@ import com.hippo.yorozuya.NumberUtils;
import java.io.File;
import java.util.Locale;
import java.util.Set;
public class Settings {
@@ -271,6 +272,11 @@ public class Settings {
private static final int DEFAULT_DOWNLOAD_DELAY = 0;
private static final String KEY_REQUEST_NEWS = "request_news";
private static final boolean DEFAULT_REQUEST_NEWS = true;
private static final String KEY_ARCHIVE_PASSWDS = "archive_passwds";
private static Context sContext;
private static SharedPreferences sSettingsPre;
private static EhConfig sEhConfig;
@@ -384,6 +390,21 @@ public class Settings {
sSettingsPre.edit().putString(key, value).apply();
}
public static Set<String> getStringSet(String key) {
return sSettingsPre.getStringSet(key, null);
}
public static void putStringToStringSet(String key, String value) {
Set<String> set = getStringSet(key);
if (set == null)
set = Set.of(value);
else if (set.contains(value))
return;
else
set.add(value);
sSettingsPre.edit().putStringSet(key, set).apply();
}
public static int getIntFromStr(String key, int defValue) {
try {
return NumberUtils.parseIntSafely(sSettingsPre.getString(key, Integer.toString(defValue)), defValue);
@@ -1139,4 +1160,12 @@ public class Settings {
public static void putClipboardTextHashCode(int value) {
putInt(KEY_CLIPBOARD_TEXT_HASH_CODE, value);
}
public static Set<String> getArchivePasswds() {
return getStringSet(KEY_ARCHIVE_PASSWDS);
}
public static void putPasswdToArchivePasswds(String value) {
putStringToStringSet(KEY_ARCHIVE_PASSWDS, value);
}
}
@@ -18,6 +18,7 @@ package com.hippo.ehviewer.gallery;
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
import android.os.Process;
import androidx.annotation.NonNull;
@@ -26,26 +27,29 @@ import androidx.annotation.Nullable;
import com.hippo.UriArchiveAccessor;
import com.hippo.ehviewer.GetText;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.Settings;
import com.hippo.glgallery.GalleryPageView;
import com.hippo.image.Image;
import com.hippo.unifile.UniFile;
import com.hippo.yorozuya.thread.PVLock;
import com.hippo.yorozuya.thread.PriorityThread;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicInteger;
public class ArchiveGalleryProvider extends GalleryProvider2 {
private static final AtomicInteger sIdGenerator = new AtomicInteger();
public static Handler showPasswd;
public static String passwd;
public static PVLock pv = new PVLock(0);
private final UriArchiveAccessor archiveAccessor;
private final Stack<Integer> requests = new Stack<>();
private final AtomicInteger extractingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX);
private final LinkedHashMap<Integer, InputStream> streams = new LinkedHashMap<>();
private final LinkedHashMap<Integer, Long> streams = new LinkedHashMap<>();
private final AtomicInteger decodingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX);
private Thread archiveThread;
private Thread decodeThread;
@@ -56,7 +60,7 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
UriArchiveAccessor archiveAccessor1;
try {
archiveAccessor1 = new UriArchiveAccessor(context, uri);
} catch (FileNotFoundException e) {
} catch (Exception e) {
archiveAccessor1 = null;
e.printStackTrace();
}
@@ -90,6 +94,11 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
decodeThread.interrupt();
decodeThread = null;
}
try {
archiveAccessor.close();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
@@ -159,9 +168,27 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
}
private class ArchiveTask implements Runnable {
private void waitPasswd() throws InterruptedException {
showPasswd.sendEmptyMessage(0);
pv.p();
}
private void notifyError() {
showPasswd.sendEmptyMessage(1);
}
private void notifyDismiss() {
showPasswd.sendEmptyMessage(2);
}
@Override
public void run() {
size = archiveAccessor.open();
try {
size = archiveAccessor.open();
} catch (Exception e) {
e.printStackTrace();
size = 0;
}
if (size <= 0) {
size = STATE_ERROR;
error = GetText.getString(R.string.error_reading_failed);
@@ -171,6 +198,34 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
// Update size and notify changed
notifyDataChanged();
if (archiveAccessor.needPassword()) {
boolean need_request = true;
Set<String> set = Settings.getArchivePasswds();
if (set != null) {
for (String passwd : set) {
if (archiveAccessor.providePassword(passwd)) {
need_request = false;
break;
}
}
}
while (!Thread.currentThread().isInterrupted() && need_request) {
try {
waitPasswd();
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
if (!archiveAccessor.providePassword(passwd))
notifyError();
else {
Settings.putPasswdToArchivePasswds(passwd);
notifyDismiss();
break;
}
}
}
while (!Thread.currentThread().isInterrupted()) {
int index;
synchronized (requests) {
@@ -194,17 +249,18 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
continue;
}
Pipe pipe = new Pipe(4096 * 16);
synchronized (streams) {
if (streams.get(index) != null) {
continue;
}
streams.put(index, pipe.getInputStream());
streams.notify();
}
archiveAccessor.extractTargetIndexToOutputStream(index, pipe.getOutputStream());
long addr = archiveAccessor.extracttoOutputStream(index);
synchronized (streams) {
streams.put(index, addr);
streams.notify();
}
}
}
}
@@ -214,7 +270,7 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
int index;
InputStream stream;
Long addr;
synchronized (streams) {
if (streams.isEmpty()) {
try {
@@ -226,16 +282,16 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
continue;
}
Iterator<Map.Entry<Integer, InputStream>> iterator = streams.entrySet().iterator();
Map.Entry<Integer, InputStream> entry = iterator.next();
Iterator<Map.Entry<Integer, Long>> iterator = streams.entrySet().iterator();
Map.Entry<Integer, Long> entry = iterator.next();
iterator.remove();
index = entry.getKey();
stream = entry.getValue();
addr = entry.getValue();
decodingIndex.lazySet(index);
}
try {
Image image = Image.decode(stream, true);
Image image = Image.decodeAddr(addr, true);
if (image != null) {
notifyPageSucceed(index, image);
} else {
@@ -34,6 +34,7 @@ import com.hippo.yorozuya.IOUtils;
import com.hippo.yorozuya.StringUtils;
import com.hippo.yorozuya.thread.PriorityThread;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -258,7 +259,7 @@ public class DirGalleryProvider extends GalleryProvider2 implements Runnable {
InputStream is = null;
try {
is = files[index].openInputStream();
Image image = Image.decode(is, true);
Image image = Image.decode(((FileInputStream) is).getFD(), true);
mDecodingIndex.lazySet(GalleryPageView.INVALID_INDEX);
if (image != null) {
notifyPageSucceed(index, image);
@@ -1,156 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* 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.
*/
package com.hippo.ehviewer.gallery;
import androidx.annotation.NonNull;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
class Pipe {
private final int capacity;
private final byte[] buffer;
private int head = 0;
private int tail = 0;
private boolean full = false;
private boolean inClosed = false;
private boolean outClosed = false;
private final InputStream inputStream = new InputStream() {
@Override
public int read() throws IOException {
synchronized (Pipe.this) {
byte[] bytes = new byte[1];
if (read(bytes, 0, 1) != -1) {
return bytes[0];
} else {
return -1;
}
}
}
@Override
public int read(@NonNull byte[] b, int off, int len) throws IOException {
synchronized (Pipe.this) {
for (; ; ) {
if (inClosed) {
throw new IOException("The InputStream is closed");
}
if (len == 0) {
return 0;
}
if (head == tail && !full) {
if (outClosed) {
// No bytes available and the OutputStream is closed. So it's the end.
return -1;
} else {
// Wait for OutputStream write bytes
try {
Pipe.this.wait();
} catch (InterruptedException e) {
throw new IOException("The thread interrupted", e);
}
}
} else {
int read = Math.min(len, (head < tail ? tail : capacity) - head);
System.arraycopy(buffer, head, b, off, read);
head += read;
if (head == capacity) {
head = 0;
}
full = false;
Pipe.this.notifyAll();
return read;
}
}
}
}
@Override
public void close() {
synchronized (Pipe.this) {
inClosed = true;
Pipe.this.notifyAll();
}
}
};
private final OutputStream outputStream = new OutputStream() {
@Override
public void write(int b) throws IOException {
synchronized (Pipe.this) {
byte[] bytes = new byte[]{(byte) b};
write(bytes, 0, 1);
}
}
@Override
public void write(@NonNull byte[] b, int off, int len) throws IOException {
synchronized (Pipe.this) {
while (len != 0) {
if (head == tail && full) {
// The buffer is full, wait for InputStream read bytes
try {
Pipe.this.wait();
} catch (InterruptedException e) {
throw new IOException("The thread interrupted", e);
}
} else {
int write = Math.min(len, (head <= tail ? capacity : head) - tail);
System.arraycopy(b, off, buffer, tail, write);
off += write;
len -= write;
tail += write;
if (tail == capacity) {
tail = 0;
}
if (head == tail) {
full = true;
}
Pipe.this.notifyAll();
}
}
}
}
@Override
public void close() {
synchronized (Pipe.this) {
outClosed = true;
Pipe.this.notifyAll();
}
}
};
Pipe(int capacity) {
this.capacity = capacity;
this.buffer = new byte[capacity];
}
InputStream getInputStream() {
return inputStream;
}
OutputStream getOutputStream() {
return outputStream;
}
}
@@ -41,6 +41,27 @@ class VersionPreference @JvmOverloads constructor(
override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {
super.onPrepareDialogBuilder(builder)
builder.setTitle(R.string.show_library_version)
builder.setMessage(Native.getlibarchiveVersion() + "\nlzma(xz-utils) " + Native.getliblzmaVersion() + "\nzlib " + Native.getzlibVersion() + "\nlibjpeg-turbo " + Native.getlibjpeg_turboVersion())
val info = NativeLibInfo().addLib(Native.getlibarchiveVersion())
.addLib("lzma(xz-utils)", Native.getliblzmaVersion())
.addLib("zlib", Native.getzlibVersion())
.addLib(Native.getnettleVersion())
builder.setMessage(info.getMessage())
}
inner class NativeLibInfo {
private var message: String = ""
fun addLib(libname: String, libversion: String): NativeLibInfo {
message = "$message$libname $libversion\n"
return this
}
fun getMessage(): String {
return message;
}
fun addLib(msg: String): NativeLibInfo {
message = "$message$msg\n"
return this
}
}
}
@@ -66,6 +66,7 @@ import com.hippo.yorozuya.thread.PriorityThread;
import com.hippo.yorozuya.thread.PriorityThreadFactory;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -1096,33 +1097,6 @@ public final class SpiderQueen implements Runnable {
void onGetImageFailure(int index, String error);
}
private static class AutoCloseInputStream extends InputStream {
private final InputStreamPipe mPipe;
private final InputStream mIs;
public AutoCloseInputStream(InputStreamPipe pipe, InputStream is) {
mPipe = pipe;
mIs = is;
}
@Override
public int read() throws IOException {
return mIs.read();
}
@Override
public int read(@NonNull byte[] buffer, int byteOffset, int byteCount) throws IOException {
return mIs.read(buffer, byteOffset, byteCount);
}
@Override
public void close() throws IOException {
mPipe.close();
mPipe.release();
}
}
private class SpiderWorker implements Runnable {
private final long mGid;
@@ -1693,7 +1667,7 @@ public final class SpiderQueen implements Runnable {
pipe.obtain();
try {
is = new AutoCloseInputStream(pipe, pipe.open());
is = pipe.open();
} catch (IOException e) {
// Can't open pipe
error = GetText.getString(R.string.error_reading_failed);
@@ -1703,7 +1677,17 @@ public final class SpiderQueen implements Runnable {
}
if (is != null) {
image = Image.decode(is, true);
try {
image = Image.decode(((FileInputStream) is).getFD(), true);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (image == null) {
error = GetText.getString(R.string.error_decoding_failed);
}
@@ -37,6 +37,8 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.view.KeyEvent;
@@ -67,6 +69,7 @@ import androidx.core.view.WindowInsetsControllerCompat;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.materialswitch.MaterialSwitch;
import com.hippo.app.EditTextDialogBuilder;
import com.hippo.ehviewer.AppConfig;
import com.hippo.ehviewer.BuildConfig;
import com.hippo.ehviewer.R;
@@ -103,6 +106,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import rikka.core.res.ConfigurationKt;
import rikka.core.res.ResourcesKt;
@@ -222,6 +226,9 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
private int mSize;
private int mCurrentIndex;
private int mSavingPage = -1;
private EditTextDialogBuilder builder;
private boolean dialogShown = false;
private AlertDialog dialog;
private void buildProvider() {
if (mGalleryProvider != null) {
@@ -283,7 +290,16 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
}
outState.putInt(KEY_PAGE, mPage);
outState.putInt(KEY_CURRENT_INDEX, mCurrentIndex);
}
} ActivityResultLauncher<String> requestStoragePermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
result -> {
if (result && mSavingPage != -1) {
saveImage(mSavingPage);
} else {
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
}
mSavingPage = -1;
});
@Override
protected void attachBaseContext(@NonNull Context newBase) {
@@ -314,10 +330,19 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
onRestore(savedInstanceState);
}
builder = new EditTextDialogBuilder(this, null, getString(R.string.archive_passwd));
builder.setTitle(getString(R.string.archive_need_passwd));
builder.setPositiveButton(getString(android.R.string.ok), null);
dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
if (mGalleryProvider == null) {
finish();
return;
}
ArchiveGalleryProvider.showPasswd = new ShowPasswdDialogHandler(this);
mGalleryProvider.start();
// Get start page
@@ -464,16 +489,7 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
FrameLayout mainLayout = (FrameLayout) ViewUtils.$$(this, R.id.main);
mainLayout.addView(new GalleryGuideView(this));
}
} ActivityResultLauncher<String> requestStoragePermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
result -> {
if (result && mSavingPage != -1) {
saveImage(mSavingPage);
} else {
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
}
mSavingPage = -1;
});
}
@Override
protected void onDestroy() {
@@ -1033,6 +1049,66 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
}
}
private void showPasswdDialog() {
if (!dialogShown) {
dialogShown = true;
dialog.show();
if (dialog.getButton(AlertDialog.BUTTON_POSITIVE) != null) {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
GalleryActivity.this.onProvidePasswd();
});
}
dialog.setOnCancelListener(v -> finish());
}
}
private void onProvidePasswd() {
String passwd = builder.getText();
if (passwd.isEmpty())
builder.setError(getString(R.string.passwd_cannot_be_empty));
else {
ArchiveGalleryProvider.passwd = passwd;
ArchiveGalleryProvider.pv.v();
}
}
private void onPasswdWrong() {
builder.setError(getString(R.string.passwd_wrong));
}
private void onPasswdCorrect() {
dialog.dismiss();
}
private static class ShowPasswdDialogHandler extends Handler {
//弱引用持有HandlerActivity , GC 回收时会被回收掉
private final WeakReference<GalleryActivity> weakReference;
public ShowPasswdDialogHandler(GalleryActivity activity) {
this.weakReference = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
GalleryActivity activity = weakReference.get();
super.handleMessage(msg);
if (null != activity) {
switch (msg.what) {
case 0:
activity.showPasswdDialog();
break;
case 1:
activity.onPasswdWrong();
break;
case 2:
activity.onPasswdCorrect();
break;
}
}
}
}
private class GalleryMenuHelper implements DialogInterface.OnClickListener {
private final View mView;
@@ -1294,6 +1370,4 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
}
}
@@ -22,7 +22,6 @@ import android.content.res.Resources;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
@@ -55,8 +55,6 @@ import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.SimpleItemAnimator;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import com.getkeepsafe.taptargetview.TapTarget;
import com.getkeepsafe.taptargetview.TapTargetView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator;
@@ -390,90 +388,9 @@ public class DownloadsScene extends ToolbarScene
updateView();
guide();
return view;
}
private void guide() {
if (Settings.getGuideDownloadThumb() && null != mRecyclerView) {
mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (Settings.getGuideDownloadThumb()) {
guideDownloadThumb();
}
if (null != mRecyclerView) {
ViewUtils.removeOnGlobalLayoutListener(mRecyclerView.getViewTreeObserver(), this);
}
}
});
} else {
guideDownloadLabels();
}
}
private void guideDownloadThumb() {
MainActivity activity = getMainActivity();
if (null == activity || !Settings.getGuideDownloadThumb() || null == mLayoutManager || null == mRecyclerView) {
guideDownloadLabels();
return;
}
int position = mLayoutManager.findFirstCompletelyVisibleItemPositions(null)[0];
if (position < 0) {
guideDownloadLabels();
return;
}
RecyclerView.ViewHolder holder = mRecyclerView.findViewHolderForAdapterPosition(position);
if (null == holder) {
guideDownloadLabels();
return;
}
TapTargetView.showFor(requireActivity(),
TapTarget.forView(((DownloadHolder) holder).thumb,
getString(R.string.guide_download_thumb_title),
getString(R.string.guide_download_thumb_text))
.transparentTarget(true),
new TapTargetView.Listener() {
@Override
public void onTargetDismissed(TapTargetView view, boolean userInitiated) {
super.onTargetDismissed(view, userInitiated);
Settings.putGuideDownloadThumb(false);
guideDownloadLabels();
}
});
}
private void guideDownloadLabels() {
MainActivity activity = getMainActivity();
if (null == activity || !Settings.getGuideDownloadLabels()) {
return;
}
Display display = activity.getWindowManager().getDefaultDisplay();
Point point = new Point();
display.getSize(point);
Rect bounds = new Rect(point.x + LayoutUtils.dp2pix(requireContext(), 20),
point.y / 3 + LayoutUtils.dp2pix(requireContext(), 20),
point.x - LayoutUtils.dp2pix(requireContext(), 20),
point.y / 3 - LayoutUtils.dp2pix(requireContext(), 20));
TapTargetView.showFor(requireActivity(),
TapTarget.forBounds(bounds,
getString(R.string.guide_download_labels_title),
getString(R.string.guide_download_labels_text))
.transparentTarget(true),
new TapTargetView.Listener() {
@Override
public void onTargetDismissed(TapTargetView view, boolean userInitiated) {
super.onTargetClick(view);
Settings.puttGuideDownloadLabels(false);
openDrawer(Gravity.RIGHT);
}
});
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
@@ -45,8 +45,6 @@ import androidx.drawerlayout.widget.DrawerLayout;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.getkeepsafe.taptargetview.TapTarget;
import com.getkeepsafe.taptargetview.TapTargetView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.hippo.annotation.Implemented;
@@ -311,40 +309,9 @@ public class FavoritesScene extends BaseScene implements
mHelper.firstRefresh();
}
guideCollections();
return view;
}
private void guideCollections() {
Activity activity = getMainActivity();
if (null == activity || !Settings.getGuideCollections()) {
return;
}
Display display = activity.getWindowManager().getDefaultDisplay();
Point point = new Point();
display.getSize(point);
Rect bounds = new Rect(point.x + LayoutUtils.dp2pix(requireContext(), 20),
point.y / 3 + LayoutUtils.dp2pix(requireContext(), 20),
point.x - LayoutUtils.dp2pix(requireContext(), 20),
point.y / 3 - LayoutUtils.dp2pix(requireContext(), 20));
TapTargetView.showFor(requireActivity(),
TapTarget.forBounds(bounds,
getString(R.string.guide_collections_title),
getString(R.string.guide_collections_text))
.transparentTarget(true),
new TapTargetView.Listener() {
@Override
public void onTargetDismissed(TapTargetView view, boolean userInitiated) {
super.onTargetClick(view);
Settings.putGuideCollections(false);
openDrawer(Gravity.RIGHT);
}
});
}
// keyword of mUrlBuilder, fav cat of mUrlBuilder, mFavCatArray.
// They changed, call it
private void updateSearchBar() {
@@ -59,8 +59,6 @@ import androidx.drawerlayout.widget.DrawerLayout;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.getkeepsafe.taptargetview.TapTarget;
import com.getkeepsafe.taptargetview.TapTargetView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator;
@@ -634,40 +632,9 @@ public final class GalleryListScene extends BaseScene
mHelper.firstRefresh();
}
guideQuickSearch();
return view;
}
private void guideQuickSearch() {
Activity activity = getMainActivity();
if (null == activity || !Settings.getGuideQuickSearch()) {
return;
}
Display display = activity.getWindowManager().getDefaultDisplay();
Point point = new Point();
display.getSize(point);
Rect bounds = new Rect(point.x + LayoutUtils.dp2pix(requireContext(), 20),
point.y / 3 + LayoutUtils.dp2pix(requireContext(), 20),
point.x - LayoutUtils.dp2pix(requireContext(), 20),
point.y / 3 - LayoutUtils.dp2pix(requireContext(), 20));
TapTargetView.showFor(requireActivity(),
TapTarget.forBounds(bounds,
getString(R.string.guide_quick_search_title),
getString(R.string.guide_quick_search_text))
.transparentTarget(true),
new TapTargetView.Listener() {
@Override
public void onTargetDismissed(TapTargetView view, boolean userInitiated) {
super.onTargetClick(view);
Settings.putGuideQuickSearch(false);
openDrawer(Gravity.RIGHT);
}
});
}
@Override
public void onDestroyView() {
super.onDestroyView();
@@ -36,6 +36,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.slider.Slider;
import com.hippo.easyrecyclerview.EasyRecyclerView;
import com.hippo.easyrecyclerview.MarginItemDecoration;
import com.hippo.ehviewer.EhApplication;
@@ -53,7 +54,6 @@ import com.hippo.ehviewer.ui.MainActivity;
import com.hippo.scene.SceneFragment;
import com.hippo.widget.ContentLayout;
import com.hippo.widget.LoadImageView;
import com.hippo.widget.Slider;
import com.hippo.widget.recyclerview.AutoGridLayoutManager;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.LayoutUtils;
@@ -209,7 +209,7 @@ public class GalleryPreviewsScene extends ToolbarScene {
return true;
}
int pages = mHelper.getPages();
if (pages > 0 && mHelper.canGoTo()) {
if (pages > 1 && mHelper.canGoTo()) {
GoToDialogHelper helper = new GoToDialogHelper(pages, mHelper.getPageForTop());
AlertDialog dialog = new MaterialAlertDialogBuilder(context).setTitle(R.string.go_to)
.setView(R.layout.dialog_go_to)
@@ -418,8 +418,8 @@ public class GalleryPreviewsScene extends ToolbarScene {
((TextView) ViewUtils.$$(dialog, R.id.start)).setText(String.format(Locale.US, "%d", 1));
((TextView) ViewUtils.$$(dialog, R.id.end)).setText(String.format(Locale.US, "%d", mPages));
mSlider = (Slider) ViewUtils.$$(dialog, R.id.slider);
mSlider.setRange(1, mPages);
mSlider.setProgress(mCurrentPage + 1);
mSlider.setValueTo(mPages);
mSlider.setValue(mCurrentPage + 1);
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(this);
dialog.setOnDismissListener(this);
@@ -431,7 +431,7 @@ public class GalleryPreviewsScene extends ToolbarScene {
return;
}
int page = mSlider.getProgress() - 1;
int page = (int) (mSlider.getValue() - 1);
if (page >= 0 && page < mPages && mHelper != null) {
mHelper.goTo(page);
if (mDialog != null) {
@@ -1,229 +0,0 @@
/*
* Copyright (C) 2015 Hippo Seven
*
* 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.
*/
package com.hippo.ehviewer.widget;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
import com.hippo.ehviewer.R;
import com.hippo.yorozuya.NumberUtils;
public class AdvanceSearchTable extends LinearLayout {
public static final int SNAME = 0x1;
public static final int STAGS = 0x2;
public static final int SDESC = 0x4;
public static final int STORR = 0x8;
public static final int STO = 0x10;
public static final int SDT1 = 0x20;
public static final int SDT2 = 0x40;
public static final int SH = 0x80;
public static final int SFL = 0x100;
public static final int SFU = 0x200;
public static final int SFT = 0x400;
private static final String STATE_KEY_SUPER = "super";
private static final String STATE_KEY_ADVANCE_SEARCH = "advance_search";
private static final String STATE_KEY_MIN_RATING = "min_rating";
private static final String STATE_KEY_PAGE_FROM = "page_from";
private static final String STATE_KEY_PAGE_TO = "page_to";
private CheckBox mSname;
private CheckBox mStags;
private CheckBox mSdesc;
private CheckBox mStorr;
private CheckBox mSto;
private CheckBox mSdt1;
private CheckBox mSdt2;
private CheckBox mSh;
private CheckBox mSr;
private Spinner mMinRating;
private CheckBox mSp;
private EditText mSpf;
private EditText mSpt;
private CheckBox mSfl;
private CheckBox mSfu;
private CheckBox mSft;
public AdvanceSearchTable(Context context) {
super(context);
init(context);
}
public AdvanceSearchTable(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public void init(Context context) {
setOrientation(LinearLayout.VERTICAL);
LayoutInflater inflater = LayoutInflater.from(getContext());
inflater.inflate(R.layout.widget_advance_search_table, this);
ViewGroup row0 = (ViewGroup) getChildAt(0);
mSname = (CheckBox) row0.getChildAt(0);
mStags = (CheckBox) row0.getChildAt(1);
ViewGroup row1 = (ViewGroup) getChildAt(1);
mSdesc = (CheckBox) row1.getChildAt(0);
mStorr = (CheckBox) row1.getChildAt(1);
ViewGroup row2 = (ViewGroup) getChildAt(2);
mSto = (CheckBox) row2.getChildAt(0);
mSdt1 = (CheckBox) row2.getChildAt(1);
ViewGroup row3 = (ViewGroup) getChildAt(3);
mSdt2 = (CheckBox) row3.getChildAt(0);
mSh = (CheckBox) row3.getChildAt(1);
ViewGroup row4 = (ViewGroup) getChildAt(4);
mSr = (CheckBox) row4.getChildAt(0);
mMinRating = (Spinner) row4.getChildAt(1);
ViewGroup row5 = (ViewGroup) getChildAt(5);
mSp = (CheckBox) row5.getChildAt(0);
mSpf = (EditText) row5.getChildAt(1);
mSpt = (EditText) row5.getChildAt(3);
ViewGroup row7 = (ViewGroup) getChildAt(7);
mSfl = (CheckBox) row7.getChildAt(0);
mSfu = (CheckBox) row7.getChildAt(1);
mSft = (CheckBox) row7.getChildAt(2);
// Avoid java.lang.IllegalStateException: focus search returned a view that wasn't able to take focus!
mSpt.setOnEditorActionListener((v, actionId, event) -> {
View nextView = v.focusSearch(View.FOCUS_DOWN);
if (nextView != null) {
nextView.requestFocus(View.FOCUS_DOWN);
}
return true;
});
}
public int getAdvanceSearch() {
int advanceSearch = 0;
if (mSname.isChecked()) advanceSearch |= SNAME;
if (mStags.isChecked()) advanceSearch |= STAGS;
if (mSdesc.isChecked()) advanceSearch |= SDESC;
if (mStorr.isChecked()) advanceSearch |= STORR;
if (mSto.isChecked()) advanceSearch |= STO;
if (mSdt1.isChecked()) advanceSearch |= SDT1;
if (mSdt2.isChecked()) advanceSearch |= SDT2;
if (mSh.isChecked()) advanceSearch |= SH;
if (mSfl.isChecked()) advanceSearch |= SFL;
if (mSfu.isChecked()) advanceSearch |= SFU;
if (mSft.isChecked()) advanceSearch |= SFT;
return advanceSearch;
}
public void setAdvanceSearch(int advanceSearch) {
mSname.setChecked(NumberUtils.int2boolean(advanceSearch & SNAME));
mStags.setChecked(NumberUtils.int2boolean(advanceSearch & STAGS));
mSdesc.setChecked(NumberUtils.int2boolean(advanceSearch & SDESC));
mStorr.setChecked(NumberUtils.int2boolean(advanceSearch & STORR));
mSto.setChecked(NumberUtils.int2boolean(advanceSearch & STO));
mSdt1.setChecked(NumberUtils.int2boolean(advanceSearch & SDT1));
mSdt2.setChecked(NumberUtils.int2boolean(advanceSearch & SDT2));
mSh.setChecked(NumberUtils.int2boolean(advanceSearch & SH));
mSfl.setChecked(NumberUtils.int2boolean(advanceSearch & SFL));
mSfu.setChecked(NumberUtils.int2boolean(advanceSearch & SFU));
mSft.setChecked(NumberUtils.int2boolean(advanceSearch & SFT));
}
public int getMinRating() {
int position = mMinRating.getSelectedItemPosition();
if (mSr.isChecked() && position >= 0) {
return position + 2;
} else {
return -1;
}
}
public void setMinRating(int minRating) {
if (minRating >= 2 && minRating <= 5) {
mSr.setChecked(true);
mMinRating.setSelection(minRating - 2);
} else {
mSr.setChecked(false);
}
}
public int getPageFrom() {
if (mSp.isChecked()) {
return NumberUtils.parseIntSafely(mSpf.getText().toString(), -1);
}
return -1;
}
public void setPageFrom(int pageFrom) {
if (pageFrom > 0) {
mSpf.setText(Integer.toString(pageFrom));
mSp.setChecked(true);
} else {
mSp.setChecked(false);
mSpf.setText(null);
}
}
public int getPageTo() {
if (mSp.isChecked()) {
return NumberUtils.parseIntSafely(mSpt.getText().toString(), -1);
}
return -1;
}
public void setPageTo(int pageTo) {
if (pageTo > 0) {
mSpt.setText(Integer.toString(pageTo));
mSp.setChecked(true);
} else {
mSp.setChecked(false);
mSpt.setText(null);
}
}
@Override
public Parcelable onSaveInstanceState() {
final Bundle state = new Bundle();
state.putParcelable(STATE_KEY_SUPER, super.onSaveInstanceState());
state.putInt(STATE_KEY_ADVANCE_SEARCH, getAdvanceSearch());
state.putInt(STATE_KEY_MIN_RATING, getMinRating());
state.putInt(STATE_KEY_PAGE_FROM, getPageFrom());
state.putInt(STATE_KEY_PAGE_TO, getPageTo());
return state;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
final Bundle savedState = (Bundle) state;
super.onRestoreInstanceState(savedState.getParcelable(STATE_KEY_SUPER));
setAdvanceSearch(savedState.getInt(STATE_KEY_ADVANCE_SEARCH));
setMinRating(savedState.getInt(STATE_KEY_MIN_RATING));
setPageFrom(savedState.getInt(STATE_KEY_PAGE_FROM));
setPageTo(savedState.getInt(STATE_KEY_PAGE_TO));
}
}
}
@@ -0,0 +1,189 @@
/*
* Copyright (C) 2015 Hippo Seven
*
* 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.
*/
package com.hippo.ehviewer.widget
import android.content.Context
import android.util.AttributeSet
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.*
import com.google.android.material.textfield.TextInputLayout
import com.hippo.ehviewer.R
import com.hippo.yorozuya.NumberUtils
class AdvanceSearchTable @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : LinearLayout(context, attrs) {
private var mSname: CheckBox
private var mStags: CheckBox
private var mSdesc: CheckBox
private var mStorr: CheckBox
private var mSto: CheckBox
private var mSdt1: CheckBox
private var mSdt2: CheckBox
private var mSh: CheckBox
private var mSr: CheckBox
private var mMinRating: TextInputLayout
private var mSp: CheckBox
private var mSpf: EditText
private var mSpt: EditText
private var mSfl: CheckBox
private var mSfu: CheckBox
private var mSft: CheckBox
private val mArray: Array<String> = context.resources.getStringArray(R.array.search_min_rating)
init {
orientation = VERTICAL
val inflater = LayoutInflater.from(context)
inflater.inflate(R.layout.widget_advance_search_table, this)
val row0 = getChildAt(0) as ViewGroup
mSname = row0.getChildAt(0) as CheckBox
mStags = row0.getChildAt(1) as CheckBox
val row1 = getChildAt(1) as ViewGroup
mSdesc = row1.getChildAt(0) as CheckBox
mStorr = row1.getChildAt(1) as CheckBox
val row2 = getChildAt(2) as ViewGroup
mSto = row2.getChildAt(0) as CheckBox
mSdt1 = row2.getChildAt(1) as CheckBox
val row3 = getChildAt(3) as ViewGroup
mSdt2 = row3.getChildAt(0) as CheckBox
mSh = row3.getChildAt(1) as CheckBox
val row4 = getChildAt(4) as ViewGroup
mSr = row4.getChildAt(0) as CheckBox
mMinRating = row4.getChildAt(1) as TextInputLayout
val row5 = getChildAt(5) as ViewGroup
mSp = row5.getChildAt(0) as CheckBox
mSpf = row5.getChildAt(1) as EditText
mSpt = row5.getChildAt(3) as EditText
val row7 = getChildAt(7) as ViewGroup
mSfl = row7.getChildAt(0) as CheckBox
mSfu = row7.getChildAt(1) as CheckBox
mSft = row7.getChildAt(2) as CheckBox
mSpt.setOnEditorActionListener { v: TextView, _: Int, _: KeyEvent? ->
val nextView = v.focusSearch(
FOCUS_DOWN
)
nextView?.requestFocus(FOCUS_DOWN)
true
}
mMinRating.isEnabled = mSr.isChecked
mSr.setOnCheckedChangeListener { _: CompoundButton, isChecked: Boolean ->
mMinRating.isEnabled = isChecked
}
mSpt.isEnabled = mSp.isChecked
mSpf.isEnabled = mSp.isChecked
mSp.setOnCheckedChangeListener { _: CompoundButton, isChecked: Boolean ->
mSpt.isEnabled = isChecked
mSpf.isEnabled = isChecked
}
}
var advanceSearch: Int
get() {
var advanceSearch = 0
if (mSname.isChecked) advanceSearch = advanceSearch or SNAME
if (mStags.isChecked) advanceSearch = advanceSearch or STAGS
if (mSdesc.isChecked) advanceSearch = advanceSearch or SDESC
if (mStorr.isChecked) advanceSearch = advanceSearch or STORR
if (mSto.isChecked) advanceSearch = advanceSearch or STO
if (mSdt1.isChecked) advanceSearch = advanceSearch or SDT1
if (mSdt2.isChecked) advanceSearch = advanceSearch or SDT2
if (mSh.isChecked) advanceSearch = advanceSearch or SH
if (mSfl.isChecked) advanceSearch = advanceSearch or SFL
if (mSfu.isChecked) advanceSearch = advanceSearch or SFU
if (mSft.isChecked) advanceSearch = advanceSearch or SFT
return advanceSearch
}
set(advanceSearch) {
mSname.isChecked =
NumberUtils.int2boolean(advanceSearch and SNAME)
mStags.isChecked =
NumberUtils.int2boolean(advanceSearch and STAGS)
mSdesc.isChecked =
NumberUtils.int2boolean(advanceSearch and SDESC)
mStorr.isChecked = NumberUtils.int2boolean(advanceSearch and STORR)
mSto.isChecked = NumberUtils.int2boolean(advanceSearch and STO)
mSdt1.isChecked = NumberUtils.int2boolean(advanceSearch and SDT1)
mSdt2.isChecked = NumberUtils.int2boolean(advanceSearch and SDT2)
mSh.isChecked = NumberUtils.int2boolean(advanceSearch and SH)
mSfl.isChecked = NumberUtils.int2boolean(advanceSearch and SFL)
mSfu.isChecked = NumberUtils.int2boolean(advanceSearch and SFU)
mSft.isChecked = NumberUtils.int2boolean(advanceSearch and SFT)
}
var minRating: Int
get() {
val position = mArray.indexOf(mMinRating.editText!!.text.toString())
return if (mSr.isChecked && position >= 0) {
position + 2
} else {
-1
}
}
set(minRating) {
if (minRating in 2..5) {
mSr.isChecked = true
(mMinRating.editText!! as AutoCompleteTextView).setText(
mArray[minRating - 2],
false
)
} else {
mSr.isChecked = false
}
}
var pageFrom: Int
get() = if (mSp.isChecked) {
NumberUtils.parseIntSafely(mSpf.text.toString(), -1)
} else -1
set(pageFrom) {
if (pageFrom > 0) {
mSpf.setText(pageFrom.toString())
mSp.isChecked = true
} else {
mSp.isChecked = false
mSpf.text = null
}
}
var pageTo: Int
get() = if (mSp.isChecked) {
NumberUtils.parseIntSafely(mSpt.text.toString(), -1)
} else -1
set(pageTo) {
if (pageTo > 0) {
mSpt.setText(pageTo.toString())
mSp.isChecked = true
} else {
mSp.isChecked = false
}
}
companion object {
const val SNAME = 0x1
const val STAGS = 0x2
const val SDESC = 0x4
const val STORR = 0x8
const val STO = 0x10
const val SDT1 = 0x20
const val SDT2 = 0x40
const val SH = 0x80
const val SFL = 0x100
const val SFU = 0x200
const val SFT = 0x400
}
}
@@ -57,6 +57,7 @@ public abstract class GalleryProvider {
public void stop() {
OSUtils.checkMainLoop();
mImageCache.evictAll();
Image.lazyEvictAll();
}
public void setGLRoot(GLRoot glRoot) {
@@ -245,18 +246,14 @@ public abstract class GalleryProvider {
}
public void add(Integer key, ImageWrapper value) {
if (value.getFormat() != Image.FORMAT_GIF && value.obtain()) {
put(key, value);
}
if (value.getFormat() != Image.FORMAT_ANIMATED)
if (value.obtain())
put(key, value);
}
@Override
protected int sizeOf(Integer key, ImageWrapper value) {
int size = value.getWidth() * value.getHeight() * 4;
if (value.getFormat() == Image.FORMAT_GIF) {
size *= 5;
}
return size;
return value.getWidth() * value.getHeight() * 4;
}
@Override
@@ -153,12 +153,6 @@ public class ImageTexture implements Texture, Animatable {
}
mTiles = list.toArray(new Tile[list.size()]);
if (!mImage.isCompleted()) {
Runnable runnable = new AnimateRunnable();
mAnimateRunnable = runnable;
sThreadExecutor.execute(runnable);
}
}
private static Tile obtainSmallTile() {
@@ -239,7 +233,7 @@ public class ImageTexture implements Texture, Animatable {
}
boolean end = mReleased.get() || mImage.isImageRecycled() || mNeedRelease.get() ||
(mImage.isCompleted() && mImage.getFrameCount() <= 1) || mRunning.get();
(mImage.getFormat() == 0) || mRunning.get();
synchronized (mImage) {
mImageBusy = false;
@@ -597,25 +591,11 @@ public class ImageTexture implements Texture, Animatable {
mImageBusy = true;
}
if (!mImage.isCompleted()) {
try {
sPVLock.p();
} catch (InterruptedException e) {
// Ignore
}
if (!mNeedRelease.get()) {
mImage.complete();
}
sPVLock.v();
}
int frameCount = mImage.getFrameCount();
synchronized (mImage) {
// Release image
mImageBusy = false;
// Check need release, frameCount <= 1
if (mNeedRelease.get() || frameCount <= 1) {
if (mNeedRelease.get() || mImage.getFormat() == 0) {
mAnimateRunnable = null;
return;
}
@@ -122,22 +122,6 @@ public class ImageWrapper {
return mCut.height();
}
/**
* @see Image#complete()
*/
public boolean complete() {
synchronized (mImage) {
return mImage.complete();
}
}
/**
* @see Image#isCompleted()
*/
public boolean isCompleted() {
return mImage.isCompleted();
}
/**
* @see Image#render(int, int, Bitmap, int, int, int, int, boolean, int)
*/
@@ -168,13 +152,6 @@ public class ImageWrapper {
return mImage.getDelay();
}
/**
* @see Image#getFrameCount()
*/
public int getFrameCount() {
return mImage.getFrameCount();
}
/**
* @see Image#isOpaque()
*/
@@ -33,6 +33,7 @@ final class DocumentsContractApi21 {
private static final String TAG = DocumentsContractApi21.class.getSimpleName();
private static final String PATH_DOCUMENT = "document";
private static final String PATH_TREE = "tree";
private DocumentsContractApi21() {
}
@@ -41,6 +41,7 @@ class FileDescriptorRandomAccessFile implements UniRandomAccessFile {
private final int flushAfterWrite;
private final Object closeLock = new Object();
private volatile boolean closed = false;
private FileDescriptorRandomAccessFile(ParcelFileDescriptor pfd, FileDescriptor fd, String mode) {
this.pfd = pfd;
this.fd = fd;
@@ -82,6 +82,13 @@ public class BatteryView extends AppCompatTextView {
}
mCurrentColor = color;
super.setTextColor(color);
}
private void startCharger() {
if (!mIsChargerWorking) {
getHandler().post(mCharger);
mIsChargerWorking = true;
}
} private final Runnable mCharger = new Runnable() {
private int level = 0;
@@ -97,13 +104,6 @@ public class BatteryView extends AppCompatTextView {
}
};
private void startCharger() {
if (!mIsChargerWorking) {
getHandler().post(mCharger);
mIsChargerWorking = true;
}
}
private void stopCharger() {
if (mIsChargerWorking) {
getHandler().removeCallbacks(mCharger);
@@ -145,6 +145,7 @@ public class BatteryView extends AppCompatTextView {
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@@ -1,515 +0,0 @@
/*
* Copyright 2015 Hippo Seven
*
* 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.
*/
package com.hippo.widget;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.core.graphics.drawable.DrawableCompat;
import com.hippo.ehviewer.R;
import com.hippo.yorozuya.AnimationUtils;
import com.hippo.yorozuya.LayoutUtils;
import com.hippo.yorozuya.MathUtils;
import com.hippo.yorozuya.SimpleHandler;
public class Slider extends View {
private static final char[] CHARACTERS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
};
private static final int BUBBLE_WIDTH = 26;
private static final int BUBBLE_HEIGHT = 32;
private final RectF mLeftRectF = new RectF();
private final RectF mRightRectF = new RectF();
private final int[] mTemp = new int[2];
private Context mContext;
private Paint mPaint;
private Paint mBgPaint;
private PopupWindow mPopup;
private BubbleView mBubble;
private int mStart;
private int mEnd;
private int mProgress;
private float mPercent;
private int mDrawProgress;
private float mDrawPercent;
private int mTargetProgress;
private float mThickness;
private float mRadius;
private float mCharWidth;
private float mCharHeight;
private int mBubbleWidth;
private int mBubbleHeight;
private int mBubbleMinWidth;
private int mBubbleMinHeight;
private int mPopupX;
private int mPopupY;
private int mPopupWidth;
private boolean mReverse = false;
private boolean mShowBubble;
private float mDrawBubbleScale = 0f;
private ValueAnimator mProgressAnimation;
private ValueAnimator mBubbleScaleAnimation;
private OnSetProgressListener mListener;
private CheckForShowBubble mCheckForShowBubble;
public Slider(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public Slider(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
@SuppressWarnings("deprecation")
private void init(Context context, AttributeSet attrs) {
mContext = context;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setTextAlign(Paint.Align.CENTER);
Resources resources = context.getResources();
mBubbleMinWidth = resources.getDimensionPixelOffset(R.dimen.slider_bubble_width);
mBubbleMinHeight = resources.getDimensionPixelOffset(R.dimen.slider_bubble_height);
mBubble = new BubbleView(context, textPaint);
mBubble.setScaleX(0.0f);
mBubble.setScaleY(0.0f);
RelativeLayout relativeLayout = new RelativeLayout(context);
relativeLayout.addView(mBubble);
relativeLayout.setBackgroundDrawable(null);
mPopup = new PopupWindow(relativeLayout);
mPopup.setOutsideTouchable(false);
mPopup.setTouchable(false);
mPopup.setFocusable(false);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Slider);
textPaint.setColor(a.getColor(R.styleable.Slider_textColor, Color.WHITE));
textPaint.setTextSize(a.getDimensionPixelSize(R.styleable.Slider_textSize, 12));
updateTextSize();
setRange(a.getInteger(R.styleable.Slider_start, 0), a.getInteger(R.styleable.Slider_end, 0));
setProgress(a.getInteger(R.styleable.Slider_slider_progress, 0));
mThickness = a.getDimension(R.styleable.Slider_thickness, 2);
mRadius = a.getDimension(R.styleable.Slider_radius, 6);
mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
a.recycle();
mProgressAnimation = new ValueAnimator();
mProgressAnimation.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
mProgressAnimation.addUpdateListener(animation -> {
float value = (Float) animation.getAnimatedValue();
mDrawPercent = value;
mDrawProgress = Math.round(MathUtils.lerp((float) mStart, mEnd, value));
updateBubblePosition();
mBubble.setProgress(mDrawProgress);
invalidate();
});
mBubbleScaleAnimation = new ValueAnimator();
mBubbleScaleAnimation.addUpdateListener(animation -> {
float value = (Float) animation.getAnimatedValue();
mDrawBubbleScale = value;
mBubble.setScaleX(value);
mBubble.setScaleY(value);
invalidate();
});
}
private void updateTextSize() {
int length = CHARACTERS.length;
float[] widths = new float[length];
mPaint.getTextWidths(CHARACTERS, 0, length, widths);
mCharWidth = 0.0f;
for (float f : widths) {
mCharWidth = Math.max(mCharWidth, f);
}
Paint.FontMetrics fm = mPaint.getFontMetrics();
mCharHeight = fm.bottom - fm.top;
}
private void updateBubbleSize() {
int oldWidth = mBubbleWidth;
int oldHeight = mBubbleHeight;
mBubbleWidth = (int) Math.max(mBubbleMinWidth,
Integer.toString(mEnd).length() * mCharWidth + LayoutUtils.dp2pix(mContext, 8));
mBubbleHeight = (int) Math.max(mBubbleMinHeight,
mCharHeight + LayoutUtils.dp2pix(mContext, 8));
if (oldWidth != mBubbleWidth && oldHeight != mBubbleHeight) {
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) mBubble.getLayoutParams();
lp.width = mBubbleWidth;
lp.height = mBubbleHeight;
mBubble.setLayoutParams(lp);
}
}
private void updatePopup() {
int width = getWidth();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
getLocationInWindow(mTemp);
mPopupWidth = (int) (width - mRadius - mRadius + mBubbleWidth);
int popupHeight = mBubbleHeight;
mPopupX = (int) (mTemp[0] + mRadius - (mBubbleWidth / 2));
mPopupY = (int) (mTemp[1] - popupHeight + paddingTop +
((getHeight() - paddingTop - paddingBottom) / 2) -
mRadius - LayoutUtils.dp2pix(mContext, 2));
mPopup.update(mPopupX, mPopupY, mPopupWidth, popupHeight, false);
}
private void updateBubblePosition() {
float x = ((mPopupWidth - mBubbleWidth) * (mReverse ? (1.0f - mDrawPercent) : mDrawPercent));
mBubble.setX(x);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
updatePopup();
updateBubblePosition();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mPopup.showAtLocation(this, Gravity.TOP | Gravity.LEFT, mPopupX, mPopupY);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mPopup.dismiss();
}
private void startProgressAnimation(float percent) {
float currentValue;
if (mProgressAnimation.isRunning()) {
mProgressAnimation.setCurrentPlayTime(mProgressAnimation.getCurrentPlayTime());
Object value = mProgressAnimation.getAnimatedValue();
if (value instanceof Float) {
currentValue = (float) value;
} else {
currentValue = mDrawPercent;
}
} else {
currentValue = mDrawPercent;
}
mProgressAnimation.cancel();
mProgressAnimation.setFloatValues(currentValue, percent);
mProgressAnimation.setDuration(Math.min(500, (long) (50 * getWidth() * Math.abs(currentValue - percent))));
mProgressAnimation.start();
}
private void startShowBubbleAnimation() {
mBubbleScaleAnimation.cancel();
mBubbleScaleAnimation.setFloatValues(mDrawBubbleScale, 1.0f);
mBubbleScaleAnimation.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
mBubbleScaleAnimation.setDuration((long) (300 * Math.abs(mDrawBubbleScale - 1.0f)));
mBubbleScaleAnimation.start();
}
private void startHideBubbleAnimation() {
mBubbleScaleAnimation.cancel();
mBubbleScaleAnimation.setFloatValues(mDrawBubbleScale, 0.0f);
mBubbleScaleAnimation.setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR);
mBubbleScaleAnimation.setDuration((long) (300 * Math.abs(mDrawBubbleScale - 0.0f)));
mBubbleScaleAnimation.start();
}
public void setColor(int color) {
mPaint.setColor(color);
mBubble.setColor(color);
invalidate();
}
public void setRange(int start, int end) {
mStart = start;
mEnd = end;
setProgress(mProgress);
updateBubbleSize();
}
public int getProgress() {
return mProgress;
}
public void setProgress(int progress) {
progress = MathUtils.clamp(progress, mStart, mEnd);
int oldProgress = mProgress;
if (mProgress != progress) {
mProgress = progress;
mPercent = MathUtils.delerp(mStart, mEnd, mProgress);
mTargetProgress = progress;
if (mProgressAnimation == null) {
// For init
mDrawPercent = mPercent;
mDrawProgress = mProgress;
updateBubblePosition();
mBubble.setProgress(mDrawProgress);
} else {
startProgressAnimation(mPercent);
}
invalidate();
}
if (mListener != null) {
mListener.onSetProgress(this, progress, oldProgress, false, true);
}
}
public void setReverse(boolean reverse) {
if (mReverse != reverse) {
mReverse = reverse;
invalidate();
}
}
public void setOnSetProgressListener(OnSetProgressListener listener) {
mListener = listener;
}
@Override
protected void onDraw(@NonNull Canvas canvas) {
int width = getWidth();
int height = getHeight();
if (width < LayoutUtils.dp2pix(mContext, 24)) {
canvas.drawRect(0, 0, width, getHeight(), mPaint);
} else {
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int paddingRight = getPaddingRight();
int paddingBottom = getPaddingBottom();
float thickness = mThickness;
float radius = mRadius;
float halfThickness = thickness / 2;
int saved = canvas.save();
canvas.translate(0, paddingTop + ((height - paddingTop - paddingBottom) / 2));
float currentX = paddingLeft + radius + (width - radius - radius - paddingLeft - paddingRight) *
(mReverse ? (1.0f - mDrawPercent) : mDrawPercent);
mLeftRectF.set(paddingLeft + radius, -halfThickness, currentX, halfThickness);
mRightRectF.set(currentX, -halfThickness, width - paddingRight - radius, halfThickness);
// Draw bar
if (mReverse) {
canvas.drawRect(mRightRectF, mPaint);
canvas.drawRect(mLeftRectF, mBgPaint);
} else {
canvas.drawRect(mLeftRectF, mPaint);
canvas.drawRect(mRightRectF, mBgPaint);
}
// Draw controller
float scale = 1.0f - mDrawBubbleScale;
if (scale != 0.0f) {
canvas.scale(scale, scale, currentX, 0);
canvas.drawCircle(currentX, 0, radius, mPaint);
}
canvas.restoreToCount(saved);
}
}
private void setShowBubble(boolean showBubble) {
if (mShowBubble != showBubble) {
mShowBubble = showBubble;
if (showBubble) {
startShowBubbleAnimation();
} else {
startHideBubbleAnimation();
}
}
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(@NonNull MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mListener != null) {
if (action == MotionEvent.ACTION_DOWN) {
mListener.onFingerDown();
} else if (action == MotionEvent.ACTION_UP) {
mListener.onFingerUp();
}
}
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
float radius = mRadius;
float x = event.getX();
int progress = Math.round(MathUtils.lerp((float) mStart, (float) mEnd,
MathUtils.clamp((mReverse ? (getWidth() - paddingLeft - radius - x) : (x - radius - paddingLeft)) /
(getWidth() - radius - radius - paddingLeft - paddingRight), 0.0f, 1.0f)));
float percent = MathUtils.delerp(mStart, mEnd, progress);
// ACTION_CANCEL not changed
if (action == MotionEvent.ACTION_CANCEL) {
progress = mProgress;
percent = mPercent;
}
if (mTargetProgress != progress) {
mTargetProgress = progress;
startProgressAnimation(percent);
if (mListener != null) {
mListener.onSetProgress(this, mProgress, progress, true, false);
}
}
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
SimpleHandler.getInstance().removeCallbacks(mCheckForShowBubble);
setShowBubble(false);
} else if (action == MotionEvent.ACTION_DOWN) {
if (mCheckForShowBubble == null) {
mCheckForShowBubble = new CheckForShowBubble();
}
SimpleHandler.getInstance().postDelayed(mCheckForShowBubble, ViewConfiguration.getTapTimeout());
}
if (action == MotionEvent.ACTION_UP) {
int oldProgress = mProgress;
if (mProgress != progress) {
mProgress = progress;
mPercent = mDrawPercent;
}
if (mListener != null) {
mListener.onSetProgress(this, progress, oldProgress, true, true);
}
}
break;
}
return true;
}
public interface OnSetProgressListener {
void onSetProgress(Slider slider, int newProgress, int oldProgress, boolean byUser, boolean confirm);
void onFingerDown();
void onFingerUp();
}
@SuppressLint("ViewConstructor")
private static class BubbleView extends AppCompatImageView {
private static final float TEXT_CENTER = (float) BUBBLE_WIDTH / 2.0f / BUBBLE_HEIGHT;
private final Drawable mDrawable;
private final Paint mTextPaint;
private final Rect mRect = new Rect();
private String mProgressStr = "";
public BubbleView(Context context, Paint paint) {
super(context);
setImageResource(R.drawable.v_slider_bubble);
mDrawable = DrawableCompat.wrap(getDrawable());
setImageDrawable(mDrawable);
mTextPaint = paint;
}
public void setColor(int color) {
DrawableCompat.setTint(mDrawable, color);
}
public void setProgress(int progress) {
String str = Integer.toString(progress);
if (!str.equals(mProgressStr)) {
mProgressStr = str;
mTextPaint.getTextBounds(str, 0, str.length(), mRect);
invalidate();
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
setPivotX(w / 2);
setPivotY(h);
}
@Override
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
int x = width / 2;
int y = (int) ((height * TEXT_CENTER) + (mRect.height() / 2));
canvas.drawText(mProgressStr, x, y, mTextPaint);
}
}
private final class CheckForShowBubble implements Runnable {
@Override
public void run() {
setShowBubble(true);
}
}
}
@@ -97,6 +97,10 @@ public class TextClock extends AppCompatTextView {
} else {
mTime = Calendar.getInstance();
}
}
public CharSequence getFormat12Hour() {
return mFormat12;
} private final Runnable mTicker = new Runnable() {
@Override
public void run() {
@@ -109,10 +113,6 @@ public class TextClock extends AppCompatTextView {
}
};
public CharSequence getFormat12Hour() {
return mFormat12;
}
public void setFormat12Hour(CharSequence format) {
mFormat12 = format;
@@ -144,19 +144,7 @@ public class TextClock extends AppCompatTextView {
createTime(timeZone);
onTimeChanged();
} private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
chooseFormat();
onTimeChanged();
}
@Override
public void onChange(boolean selfChange, Uri uri) {
chooseFormat();
onTimeChanged();
}
};
}
/**
* Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()}
@@ -170,7 +158,19 @@ public class TextClock extends AppCompatTextView {
public CharSequence getFormat() {
return mFormat;
}
} private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
chooseFormat();
onTimeChanged();
}
@Override
public void onChange(boolean selfChange, Uri uri) {
chooseFormat();
onTimeChanged();
}
};
/**
* Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()}
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2022 Tarsin Norbin
~
~ This file is part of EhViewer
~
~ EhViewer is free software: you can redistribute it and/or modify it
~ under the terms of the GNU GeneralPublic License as published
~ by the Free Software Foundation,either version 3 of the License, or
~ (at your option) any later version.
~
~ EhViewer is distributed in the hope that it will be useful, but WITHOUT
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License along with EhViewer.
~ If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="44dp"
android:height="44dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group
android:scaleX="0.37947997"
android:scaleY="0.37947997"
android:translateX="35.1"
android:translateY="39.584126">
<path
android:fillColor="@android:color/white"
android:fillType="evenOdd"
android:pathData="M39.893,68.066L39.893,75.977L0,75.977L0,4.59L39.893,4.59L39.893,12.5L8.984,12.5L8.984,34.766L38.086,34.766L38.086,42.578L8.984,42.578L8.984,68.066L39.893,68.066Z" />
<path
android:fillColor="@android:color/white"
android:fillType="evenOdd"
android:pathData="M99.609,75.977L90.918,75.977L90.918,41.699A23.436,23.436 0,0 0,90.662 38.109Q90.055,34.207 88.013,31.958Q85.629,29.334 81.028,28.863A20.84,20.84 0,0 0,78.906 28.76A23.134,23.134 0,0 0,74.45 29.16Q71.926,29.656 69.994,30.767A11.122,11.122 0,0 0,66.968 33.301A13.32,13.32 0,0 0,64.756 37.276Q63.399,40.971 63.213,46.495A53.517,53.517 0,0 0,63.184 48.291L63.184,75.977L54.395,75.977L54.395,0L63.184,0L63.184,22.266A66.935,66.935 0,0 1,63.115 25.383Q63.043,26.914 62.897,28.251A33.547,33.547 0,0 1,62.695 29.785L63.281,29.785A16.11,16.11 0,0 1,69.394 24.035A19.448,19.448 0,0 1,70.068 23.682A20.88,20.88 0,0 1,77.292 21.622A25.763,25.763 0,0 1,79.98 21.484A32.643,32.643 0,0 1,85.483 21.917Q91.249,22.905 94.678,26.147Q98.472,29.735 99.347,36.631A35.274,35.274 0,0 1,99.609 41.064L99.609,75.977Z" />
</group>
</vector>
@@ -0,0 +1,191 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2022 hathlife
~
~ This file is part of EhViewer
~
~ EhViewer is free software: you can redistribute it and/or
~ modify it under the terms of the GNU General Public License as
~ published by the Free Software Foundation, either version 3 of the
~ License, or (at your option) any later version.
~
~ EhViewer is distributed in the hope that it will be useful, but
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License along with EhViewer.
~ If not, see <https://www.gnu.org/licenses/>.
~
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/actions"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="@dimen/keyline_margin">
<TextView
android:id="@+id/newerVersion"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:gravity="center"
android:padding="8dp"
android:text="@string/newer_version_avaliable"
android:visibility="gone" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/keyline_margin"
android:paddingRight="@dimen/keyline_margin">
<LinearLayout
android:id="@+id/rate"
android:layout_width="484dp"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:orientation="vertical"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<androidx.appcompat.widget.AppCompatRatingBar
android:id="@+id/rating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="0dp"
android:layout_marginTop="0dp"
android:isIndicator="true"
android:theme="@style/RatingBarTheme" />
<TextView
android:id="@+id/rating_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="0dp"
android:textColor="?android:attr/textColorPrimary" />
</LinearLayout>
<HorizontalScrollView
android:id="@+id/actions_scroll_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:clipToPadding="false"
android:paddingLeft="36dp"
android:paddingRight="@dimen/keyline_margin"
android:scrollbars="none">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:divider="@drawable/spacer_keyline"
android:orientation="horizontal"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:showDividers="middle">
<FrameLayout
android:id="@+id/heart_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="true"
android:nextFocusUp="@+id/download">
<TextView
android:id="@+id/heart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="4dp"
android:gravity="center_horizontal"
android:text="@string/favorited"
android:textColor="?android:attr/textColorPrimary" />
<TextView
android:id="@+id/heart_outline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="4dp"
android:gravity="center_horizontal"
android:text="@string/not_favorited"
android:textColor="?android:attr/textColorPrimary" />
</FrameLayout>
<TextView
android:id="@+id/share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:drawablePadding="4dp"
android:focusable="true"
android:gravity="center_horizontal"
android:nextFocusUp="@+id/download"
android:text="@string/share"
android:textColor="?android:attr/textColorPrimary" />
<TextView
android:id="@+id/torrent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:drawablePadding="4dp"
android:focusable="true"
android:gravity="center_horizontal"
android:nextFocusUp="@+id/download"
android:textColor="?android:attr/textColorPrimary" />
<TextView
android:id="@+id/archive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:drawablePadding="4dp"
android:focusable="true"
android:gravity="center_horizontal"
android:nextFocusUp="@+id/download"
android:text="@string/archive"
android:textColor="?android:attr/textColorPrimary" />
<TextView
android:id="@+id/similar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:drawablePadding="4dp"
android:focusable="true"
android:gravity="center_horizontal"
android:nextFocusUp="@+id/download"
android:text="@string/similar_gallery"
android:textColor="?android:attr/textColorPrimary" />
<TextView
android:id="@+id/search_cover"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:drawablePadding="4dp"
android:focusable="true"
android:gravity="center_horizontal"
android:nextFocusRight="@+id/search_cover"
android:nextFocusUp="@+id/download"
android:text="@string/search_cover"
android:textColor="?android:attr/textColorPrimary" />
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
</LinearLayout>
@@ -0,0 +1,263 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2022 hathlife
~
~ This file is part of EhViewer
~
~ EhViewer is free software: you can redistribute it and/or
~ modify it under the terms of the GNU General Public License as
~ published by the Free Software Foundation, either version 3 of the
~ License, or (at your option) any later version.
~
~ EhViewer is distributed in the hope that it will be useful, but
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License along with EhViewer.
~ If not, see <https://www.gnu.org/licenses/>.
~
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<View
android:id="@+id/color_bg"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:elevation="4dp"
app:fitsSystemWindowsInsets="top" />
<Space
android:layout_width="match_parent"
android:layout_height="28dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/title"
style="@style/TextAppearance.Material3.HeadlineSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/keyline_margin"
android:layout_marginRight="@dimen/keyline_margin"
android:ellipsize="end"
android:maxLines="5" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
style="@style/Widget.Material3.CardView.Elevated"
android:layout_width="540dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/keyline_margin"
android:layout_marginTop="@dimen/keyline_margin"
android:layout_marginRight="@dimen/keyline_margin"
android:layout_marginBottom="@dimen/keyline_margin"
app:fitsSystemWindowsInsets="top">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/header_content"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:id="@+id/thumb_card"
style="@style/Widget.Material3.CardView.Filled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.hippo.widget.LoadImageView
android:id="@+id/thumb"
android:layout_width="256dp"
android:layout_height="@dimen/gallery_detail_thumb_height" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/info"
style="@style/Widget.Material3.CardView.Filled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="@dimen/keyline_margin"
android:clickable="true"
android:focusable="true"
android:nextFocusRight="@+id/read"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp">
<TextView
android:id="@+id/language"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left"
android:singleLine="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/size"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:gravity="right"
android:singleLine="true"
app:layout_constraintBaseline_toBaselineOf="@id/language"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toEndOf="@id/language" />
<TextView
android:id="@+id/favoredTimes"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="left"
android:singleLine="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/language" />
<TextView
android:id="@+id/pages"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:gravity="right"
android:singleLine="true"
app:layout_constraintBaseline_toBaselineOf="@id/favoredTimes"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toEndOf="@id/favoredTimes" />
<TextView
android:id="@+id/posted"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="left"
android:singleLine="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/favoredTimes" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.chip.Chip
android:id="@+id/uploader"
style="@style/Widget.Material3.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/keyline_margin"
android:ellipsize="end"
android:nextFocusRight="@+id/read"
android:nextFocusDown="@+id/rate"
android:singleLine="true"
android:textAllCaps="true"
app:chipIcon="@drawable/file_upload_black_24dp"
app:ensureMinTouchTargetSize="true"
app:layout_constraintBottom_toBottomOf="@id/thumb_card"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.chip.Chip
android:id="@+id/category"
style="@style/Widget.Material3.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/keyline_margin"
android:ellipsize="end"
android:nextFocusRight="@+id/read"
android:singleLine="true"
android:textAllCaps="true"
app:chipIcon="@drawable/ic_baseline_label_24"
app:ensureMinTouchTargetSize="true"
app:layout_constraintBottom_toTopOf="@id/uploader"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<LinearLayout
android:id="@+id/action_card"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/keyline_margin"
android:layout_marginRight="@dimen/keyline_margin"
android:orientation="vertical">
<Button
android:id="@+id/read"
style="@style/Widget.Material3.Button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
android:insetLeft="40dp"
android:insetTop="16dp"
android:insetRight="40dp"
android:text="@string/read">
<requestFocus />
</Button>
<Button
android:id="@+id/download"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
android:insetLeft="40dp"
android:insetTop="16dp"
android:insetRight="40dp"
android:nextFocusDown="@+id/torrent" />
<android.widget.Space
android:layout_width="match_parent"
android:layout_height="80dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</FrameLayout>
@@ -1,17 +1,20 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2022 hathlife
~
~ 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
~ This file is part of EhViewer
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ EhViewer is free software: you can redistribute it and/or
~ modify it under the terms of the GNU General Public License as
~ published by the Free Software Foundation, either version 3 of the
~ License, or (at your option) any later version.
~
~ EhViewer is distributed in the hope that it will be useful, but
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License along with EhViewer.
~ If not, see <https://www.gnu.org/licenses/>.
~
~ 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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -1,17 +1,20 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2022 hathlife
~
~ 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
~ This file is part of EhViewer
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ EhViewer is free software: you can redistribute it and/or
~ modify it under the terms of the GNU General Public License as
~ published by the Free Software Foundation, either version 3 of the
~ License, or (at your option) any later version.
~
~ EhViewer is distributed in the hope that it will be useful, but
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License along with EhViewer.
~ If not, see <https://www.gnu.org/licenses/>.
~
~ 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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -36,7 +39,7 @@
<ImageView
android:layout_width="257dp"
android:layout_height="518dp"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:padding="@dimen/keyline_margin"
app:srcCompat="@mipmap/ic_launcher" />
@@ -1,17 +1,20 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2022 hathlife
~
~ 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
~ This file is part of EhViewer
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ EhViewer is free software: you can redistribute it and/or
~ modify it under the terms of the GNU General Public License as
~ published by the Free Software Foundation, either version 3 of the
~ License, or (at your option) any later version.
~
~ EhViewer is distributed in the hope that it will be useful, but
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License along with EhViewer.
~ If not, see <https://www.gnu.org/licenses/>.
~
~ 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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -1,17 +1,20 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2022 hathlife
~
~ 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
~ This file is part of EhViewer
~
~ http://www.apache.org/licenses/LICENSE-2.0
~ EhViewer is free software: you can redistribute it and/or
~ modify it under the terms of the GNU General Public License as
~ published by the Free Software Foundation, either version 3 of the
~ License, or (at your option) any later version.
~
~ EhViewer is distributed in the hope that it will be useful, but
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License along with EhViewer.
~ If not, see <https://www.gnu.org/licenses/>.
~
~ 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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+4 -3
View File
@@ -28,13 +28,14 @@
android:layout_gravity="center_vertical"
android:gravity="center_horizontal" />
<com.hippo.widget.Slider
<com.google.android.material.slider.Slider
android:id="@+id/slider"
style="@style/Slider"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_gravity="center_horizontal"
android:layout_weight="1" />
android:layout_weight="1"
android:stepSize="1"
android:valueFrom="1" />
<TextView
android:id="@+id/end"
@@ -64,16 +64,18 @@
android:layout_marginBottom="@dimen/keyline_margin"
app:fitsSystemWindowsInsets="top">
<RelativeLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/header_content"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:id="@+id/thumb_card"
style="@style/Widget.Material3.CardView.Elevated"
style="@style/Widget.Material3.CardView.Filled"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.hippo.widget.LoadImageView
android:id="@+id/thumb"
@@ -87,69 +89,82 @@
style="@style/Widget.Material3.CardView.Filled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginTop="8dp"
android:layout_marginEnd="@dimen/keyline_margin"
android:clickable="true"
android:focusable="true"
android:orientation="vertical">
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TableLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp">
<TableRow>
<TextView
android:id="@+id/language"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start"
android:singleLine="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/language"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:singleLine="true" />
<TextView
android:id="@+id/size"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:gravity="end"
android:singleLine="true"
app:layout_constraintBaseline_toBaselineOf="@id/language"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toEndOf="@id/language" />
<TextView
android:id="@+id/size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end"
android:singleLine="true" />
</TableRow>
<TextView
android:id="@+id/favoredTimes"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="start"
android:singleLine="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/language" />
<TableRow android:paddingTop="8dp">
<TextView
android:id="@+id/pages"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:gravity="end"
android:singleLine="true"
app:layout_constraintBaseline_toBaselineOf="@id/favoredTimes"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toEndOf="@id/favoredTimes" />
<TextView
android:id="@+id/favoredTimes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:singleLine="true" />
<TextView
android:id="@+id/pages"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end"
android:singleLine="true" />
<TextView
android:id="@+id/posted"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center"
android:singleLine="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/favoredTimes" />
</TableRow>
<TableRow android:paddingTop="8dp">
<TextView
android:id="@+id/posted"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:singleLine="true" />
</TableRow>
</TableLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
@@ -158,30 +173,31 @@
style="@style/Widget.Material3.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/thumb_card"
android:layout_alignParentEnd="true"
android:layout_marginEnd="@dimen/keyline_margin"
android:ellipsize="end"
android:singleLine="true"
android:textAllCaps="true"
app:chipIcon="@drawable/file_upload_black_24dp"
app:ensureMinTouchTargetSize="true" />
app:ensureMinTouchTargetSize="true"
app:layout_constraintBottom_toBottomOf="@id/thumb_card"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.chip.Chip
android:id="@+id/category"
style="@style/Widget.Material3.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/uploader"
android:layout_alignParentEnd="true"
android:layout_marginEnd="@dimen/keyline_margin"
android:ellipsize="end"
android:singleLine="true"
android:textAllCaps="true"
app:chipIcon="@drawable/ic_baseline_label_24"
app:ensureMinTouchTargetSize="true" />
app:chipSpacingHorizontal="8dp"
app:ensureMinTouchTargetSize="true"
app:layout_constraintBottom_toTopOf="@id/uploader"
app:layout_constraintEnd_toEndOf="parent" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
@@ -198,19 +214,17 @@
style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:clickable="true"
android:focusable="true" />
<android.widget.Space
android:layout_width="8dp"
android:layout_height="wrap_content" />
<Button
android:id="@+id/read"
style="@style/Widget.Material3.Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
+12 -9
View File
@@ -95,27 +95,30 @@
android:layout_height="wrap_content"
android:clipToPadding="false"
android:gravity="bottom"
android:orientation="vertical"
android:orientation="horizontal"
android:paddingLeft="12dp"
android:paddingTop="4dp"
android:paddingRight="12dp"
android:paddingBottom="4dp">
<Button
android:id="@+id/sign_in"
style="@style/Widget.Material3.Button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/sign_in" />
<Button
android:id="@+id/register"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:text="@string/register" />
<Button
android:id="@+id/sign_in"
style="@style/Widget.Material3.Button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:text="@string/sign_in" />
</LinearLayout>
<LinearLayout
@@ -12,7 +12,8 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout
android:layout_width="match_parent"
@@ -139,13 +140,22 @@
android:minHeight="0dp"
android:text="@string/search_sr" />
<Spinner
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/advance_search_table_item_margin"
android:layout_marginStart="@dimen/advance_search_table_item_margin"
android:layout_weight="1"
android:entries="@array/search_min_rating" />
android:hint="@string/search_sr">
<AutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
app:simpleItems="@array/search_min_rating" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<LinearLayout
@@ -18,4 +18,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
</adaptive-icon>
@@ -18,4 +18,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
</adaptive-icon>
-21
View File
@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2016 Hippo Seven
~
~ 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.
-->
<resources>
<!-- Custom preference -->
</resources>
-21
View File
@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2016 Hippo Seven
~
~ 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.
-->
<resources>
<!-- Custom preference -->
</resources>
-19
View File
@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2016 Hippo Seven
~
~ 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.
-->
<resources>
</resources>
@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2016 Hippo Seven
~
~ 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.
-->
<resources>
<!-- Custom preference -->
</resources>
@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2016 Hippo Seven
~
~ 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.
-->
<resources>
</resources>
@@ -764,4 +764,9 @@
<string name="settings_privacy_require_unlock">需要解锁</string>
<string name="proxy_type_name">代理类型</string>
<string name="filter_label">屏蔽项类型</string>
<string name="archive_passwd">归档文件密码</string>
<string name="archive_need_passwd">归档文件需要密码</string>
<string name="passwd_wrong">密码错误</string>
<string name="passwd_cannot_be_empty">密码不能为空</string>
</resources>
+5
View File
@@ -828,4 +828,9 @@
<string name="settings_privacy_require_unlock">Require Unlock</string>
<string name="proxy_type_name">Proxy Type</string>
<string name="filter_label">Blocker Type</string>
<string name="archive_passwd">password</string>
<string name="archive_need_passwd">Archive need password</string>
<string name="passwd_wrong">Password Wrong</string>
<string name="passwd_cannot_be_empty">Password can\'t be empty</string>
</resources>
-6
View File
@@ -51,12 +51,6 @@
<item name="android:gravity">center_vertical</item>
</style>
<style name="Slider">
<item name="thickness">2dp</item>
<item name="radius">6dp</item>
<item name="textSize">12sp</item>
</style>
<style name="Indicating">
<item name="indicatorHeight">1dp</item>
<item name="indicatorColor">?attr/dividerColor</item>
File renamed without changes.
+1 -1
View File
@@ -9,7 +9,7 @@ android {
ndkVersion "25.0.8775105"
defaultConfig {
minSdkVersion 26
minSdkVersion 31
targetSdkVersion 33
consumerProguardFiles 'proguard-rules.pro'
+2 -4
View File
@@ -28,9 +28,7 @@ public class Native {
public static native String getliblzmaVersion();
public static native String getlibzstdVersion();
public static native String getlibjpeg_turboVersion();
public static native String getzlibVersion();
public static native String getnettleVersion();
}
@@ -21,103 +21,44 @@ package com.hippo;
import android.content.Context;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
public class UriArchiveAccessor implements Closeable {
public static int OPEN_OK = 1;
public static int OPEN_ERR = 2;
private final OsReadableFile osf;
public class UriArchiveAccessor {
ParcelFileDescriptor pfd;
FileDescriptor fd;
long archiveAddr;
Context ctx;
Uri uri;
public UriArchiveAccessor(Context ctx, Uri uri) throws FileNotFoundException {
osf = new OsReadableFile(ctx, uri);
public UriArchiveAccessor(Context ctx, Uri uri) {
this.ctx = ctx;
this.uri = uri;
}
public int open() {
return openArchive(osf);
public int open() throws Exception {
pfd = ctx.getContentResolver().openFileDescriptor(uri, "r");
fd = pfd.getFileDescriptor();
archiveAddr = Os.mmap(0, pfd.getStatSize(), OsConstants.PROT_READ, OsConstants.MAP_PRIVATE, fd, 0);
return openArchive(archiveAddr, pfd.getStatSize());
}
public void extractTargetIndexToOutputStream(int index, OutputStream os) {
extracttoOutputStream(osf, index, os);
}
private native int openArchive(long addr, long size);
private native int openArchive(OsReadableFile osf);
public native long extracttoOutputStream(int index);
private native int extracttoOutputStream(OsReadableFile osf, int index, OutputStream os);
public native boolean needPassword();
@Override
public void close() throws IOException {
osf.close();
}
public native boolean providePassword(String str);
static class OsReadableFile {
ParcelFileDescriptor pfd;
FileDescriptor fd;
private native void closeArchive();
public OsReadableFile(Context ctx, Uri uri) throws FileNotFoundException {
pfd = ctx.getContentResolver().openFileDescriptor(uri, "r");
fd = pfd.getFileDescriptor();
}
public int read(byte[] b, int off, int len) throws IOException, ErrnoException {
return Os.read(fd, b, off, len);
}
public int read(byte[] b) throws IOException, ErrnoException {
return read(b, 0, b.length);
}
public long length() throws IOException, ErrnoException {
return Os.fstat(fd).st_size;
}
public long seek(long pos, int whence) throws IOException, ErrnoException {
try {
return Os.lseek(fd, pos, whence);
} catch (Exception e) {
e.printStackTrace();
return -30;
}
}
public void rewind() throws IOException, ErrnoException {
Os.lseek(fd, 0, OsConstants.SEEK_SET);
}
public long skip(long n) throws IOException, ErrnoException {
long pos;
long len;
long newpos;
if (n <= 0) {
return 0;
}
pos = Os.lseek(fd, 0L, OsConstants.SEEK_CUR);
len = length();
newpos = pos + n;
if (newpos > len) {
newpos = len;
}
seek(newpos, OsConstants.SEEK_SET);
/* return the actual number of bytes skipped */
return newpos - pos;
}
public void close() throws IOException {
pfd.close();
try {
Os.close(fd);
} catch (ErrnoException e) {
throw new IOException("An ErrnoException occurs", e);
}
}
public void close() throws Exception {
closeArchive();
Os.munmap(archiveAddr, pfd.getStatSize());
Os.close(fd);
pfd.close();
}
}
+57 -138
View File
@@ -20,65 +20,68 @@ import android.graphics.Bitmap;
import androidx.annotation.Nullable;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicInteger;
import java.io.FileDescriptor;
import java.util.ArrayList;
/**
* The {@code Image} is a image which stored pixel data in native heap
*/
public final class Image {
public static final int FORMAT_NORMAL = 0;
/**
* Unknown image format
*/
public static final int FORMAT_UNKNOWN = -1;
public static final int FORMAT_ANIMATED = 1;
/**
* Plain image format, for {@code Image} from {@link #create(Bitmap)}
*/
public static final int FORMAT_PLAIN = 0;
private static final ArrayList<Image> imageList = new ArrayList<>();
private static boolean needEvictAll = false;
/**
* JPEG image format
*/
public static final int FORMAT_JPEG = 1;
/**
* PNG image format
*/
public static final int FORMAT_PNG = 2;
/**
* GIF image format
*/
public static final int FORMAT_GIF = 3;
private static final AtomicInteger sImageCount = new AtomicInteger();
private final int mFormat;
private final int mWidth;
private final int mHeight;
private long mNativePtr;
private Throwable mRecycleTracker;
private Image(long nativePtr, int format, int width, int height) {
if (needEvictAll)
evictAll();
mNativePtr = nativePtr;
mFormat = format;
mWidth = width;
mHeight = height;
sImageCount.getAndIncrement();
imageList.add(this);
}
public static void lazyEvictAll() {
needEvictAll = true;
}
private static void evictAll() {
(new ArrayList<>(imageList)).forEach(Image::recycle);
needEvictAll = false;
}
/**
* Decode image from {@code InputStream}
*/
@Nullable
public static Image decode(InputStream is, boolean partially) {
if (!(is instanceof BufferedInputStream)) {
is = new BufferedInputStream(is);
}
return nativeDecode(is, partially);
public static Image decode(FileDescriptor fd, boolean partially) {
return nativeDecode(fd);
}
/**
* Decode image from {@code InputStream}
*/
@Nullable
public static Image decode(Integer fd, boolean partially) {
return nativeDecodeFdInt(fd);
}
/**
* Decode image from {@code InputStream}
*/
@Nullable
public static Image decodeAddr(Long addr, boolean partially) {
return nativeDecodeAddr(addr);
}
/**
@@ -94,58 +97,30 @@ public final class Image {
* It is useful for debug.
*/
public static int getImageCount() {
return sImageCount.get();
return imageList.size();
}
/**
* Return all supported image formats, exclude {@link #FORMAT_PLAIN}
*/
public static int[] getSupportedImageFormats() {
return nativeGetSupportedImageFormats();
}
private static native Image nativeDecode(FileDescriptor fd);
/**
* Return decoder description of the image format,
* {@code null} for invalid image format.
*/
public static String getDecoderDescription(int format) {
return nativeGetDecoderDescription(format);
}
private static native Image nativeDecodeFdInt(int fd);
private static native Image nativeDecode(InputStream is, boolean partially);
private static native Image nativeDecodeAddr(long addr);
private static native Image nativeCreate(Bitmap bitmap);
private static native int nativeGetByteCount(long nativePtr, int format);
private static native boolean nativeComplete(long nativePtr, int format);
private static native boolean nativeIsCompleted(long nativePtr, int format);
private static native void nativeRender(long nativePtr, int format,
private static native void nativeRender(long nativePtr,
int srcX, int srcY, Bitmap dst, int dstX, int dstY,
int width, int height, boolean fillBlank, int defaultColor);
int width, int height);
private static native void nativeTexImage(long nativePtr, int format,
boolean init, int offsetX, int offsetY, int width, int height);
private static native void nativeTexImage(long nativePtr, boolean init, int offsetX, int offsetY, int width, int height);
private static native void nativeAdvance(long nativePtr, int format);
private static native void nativeAdvance(long nativePtr);
private static native int nativeGetDelay(long nativePtr, int format);
private static native int nativeGetDelay(long nativePtr);
private static native int nativeFrameCount(long nativePtr, int format);
private static native boolean nativeIsOpaque(long nativePtr);
private static native boolean nativeIsOpaque(long nativePtr, int format);
private static native boolean nativeIsGray(long nativePtr, int format, int error);
private static native void nativeClahe(long nativePtr, int format, boolean toGray);
private static native void nativeRecycle(long nativePtr, int format);
private static native int[] nativeGetSupportedImageFormats();
private static native String nativeGetDecoderDescription(int format);
private static native void nativeRecycle(long nativePtr);
/**
* Return the format of the image
@@ -168,48 +143,19 @@ public final class Image {
return mHeight;
}
/**
* Return the minimum number of bytes that can be used to store this image's pixels.
*/
public int getByteCount() {
checkRecycled();
return nativeGetByteCount(mNativePtr, mFormat);
}
private void checkRecycled() {
if (mNativePtr == 0) {
if (mRecycleTracker != null) {
throw new IllegalStateException("The image is recycled.", mRecycleTracker);
} else {
throw new IllegalStateException("The image is recycled.");
}
throw new IllegalStateException("The image is recycled.");
}
}
/**
* Complete the image decoding
*/
public boolean complete() {
checkRecycled();
return nativeComplete(mNativePtr, mFormat);
}
/**
* Is the image decoding completed
*/
public boolean isCompleted() {
checkRecycled();
return nativeIsCompleted(mNativePtr, mFormat);
}
/**
* Render the image to {@code Bitmap}
*/
public void render(int srcX, int srcY, Bitmap dst, int dstX, int dstY,
int width, int height, boolean fillBlank, int defaultColor) {
checkRecycled();
nativeRender(mNativePtr, mFormat, srcX, srcY, dst, dstX, dstY,
width, height, fillBlank, defaultColor);
nativeRender(mNativePtr, srcX, srcY, dst, dstX, dstY, width, height);
}
/**
@@ -219,7 +165,7 @@ public final class Image {
*/
public void texImage(boolean init, int offsetX, int offsetY, int width, int height) {
checkRecycled();
nativeTexImage(mNativePtr, mFormat, init, offsetX, offsetY, width, height);
nativeTexImage(mNativePtr, init, offsetX, offsetY, width, height);
}
/**
@@ -227,7 +173,7 @@ public final class Image {
*/
public void advance() {
checkRecycled();
nativeAdvance(mNativePtr, mFormat);
nativeAdvance(mNativePtr);
}
/**
@@ -235,40 +181,16 @@ public final class Image {
*/
public int getDelay() {
checkRecycled();
int delay = nativeGetDelay(mNativePtr, mFormat);
int delay = nativeGetDelay(mNativePtr);
return delay <= 10 ? 100 : delay;
}
/**
* Return frame count. 1 for non-animation image
*/
public int getFrameCount() {
checkRecycled();
return nativeFrameCount(mNativePtr, mFormat);
}
/**
* Return is the image opaque
*/
public boolean isOpaque() {
checkRecycled();
return nativeIsOpaque(mNativePtr, mFormat);
}
/**
* Return {@code true} if the image is gray.
*/
public boolean isGray(int error) {
checkRecycled();
return nativeIsGray(mNativePtr, mFormat, error);
}
/**
* Improves contrast in this image with CLAHE.
*/
public void clahe(boolean toGray) {
checkRecycled();
nativeClahe(mNativePtr, mFormat, toGray);
return nativeIsOpaque(mNativePtr);
}
/**
@@ -276,14 +198,11 @@ public final class Image {
* It must be called when the image will not be used.
* The image can't be used after this method is called.
*/
public void recycle() {
public synchronized void recycle() {
if (mNativePtr != 0) {
nativeRecycle(mNativePtr, mFormat);
nativeRecycle(mNativePtr);
mNativePtr = 0;
sImageCount.getAndDecrement();
mRecycleTracker = new Throwable("It's a ImageRecycleTracker");
imageList.remove(this);
}
}
@@ -28,6 +28,7 @@ import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.FileDescriptor;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.Iterator;
@@ -44,8 +45,6 @@ public final class ImageBitmap implements Animatable, Runnable {
private final Bitmap mBitmap;
private final int mFormat;
private final boolean mIsOpaque;
private final int mByteCount;
private final int mFrameCount;
private final Set<WeakReference<Callback>> mCallbackSet = new LinkedHashSet<>();
@Nullable
private Image mImage;
@@ -60,10 +59,8 @@ public final class ImageBitmap implements Animatable, Runnable {
image.render(0, 0, mBitmap, 0, 0, width, height, false, 0);
mFormat = image.getFormat();
mIsOpaque = image.isOpaque();
mByteCount = image.getByteCount();
mFrameCount = image.getFrameCount();
if (mFrameCount > 1) {
if (mFormat == 1) {
// For animated image, save image object
mImage = image;
} else {
@@ -73,19 +70,17 @@ public final class ImageBitmap implements Animatable, Runnable {
}
private ImageBitmap(@NonNull Bitmap bitmap) {
mFormat = Image.FORMAT_PLAIN;
mFormat = Image.FORMAT_NORMAL;
mBitmap = bitmap;
mIsOpaque = !bitmap.hasAlpha();
mByteCount = bitmap.getRowBytes() * bitmap.getHeight();
mFrameCount = 1;
}
/**
* Decode {@code InputStream}, then create image.
*/
@Nullable
public static ImageBitmap decode(@NonNull InputStream is) {
Image image = Image.decode(is, false);
public static ImageBitmap decode(@NonNull FileDescriptor fd) {
Image image = Image.decode(fd, false);
if (image != null) {
return create(image);
} else {
@@ -223,13 +218,6 @@ public final class ImageBitmap implements Animatable, Runnable {
return mIsOpaque;
}
/**
* Return byte count of image
*/
public int getByteCount() {
return mByteCount;
}
/**
* Return image is animated
*/
@@ -237,13 +225,6 @@ public final class ImageBitmap implements Animatable, Runnable {
return mImage != null;
}
/**
* Return image frame count
*/
public int getFrameCount() {
return mFrameCount;
}
/**
* Draw image to canvas
*/
+17
View File
@@ -13,6 +13,20 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Oz -fvisibility=hidden -fvisibility-inlines
-mllvm -polly-detect-profitability-min-per-loop-insts=40 \
-mllvm -polly-invariant-load-hoisting")
# Build GNUTLS libnettle
add_subdirectory(nettle)
# Configure libnettle support for libarchive
include_directories(nettle)
include_directories(.)
set(HAVE_LIBNETTLE 1)
set(HAVE_NETTLE_AES_H 1)
set(HAVE_NETTLE_HMAC_H 1)
set(HAVE_NETTLE_MD5_H 1)
set(HAVE_NETTLE_PBKDF2_H 1)
set(HAVE_NETTLE_RIPEMD160_H 1)
set(HAVE_NETTLE_SHA_H 1)
# Build liblzma
add_subdirectory(liblzma)
@@ -24,6 +38,9 @@ SET(HAVE_LZMA_STREAM_ENCODER_MT 1)
SET(HAVE_LZMADEC_H 1)
SET(HAVE_LIBLZMADEC 1)
# Configure libarchive link's static lib
SET(LIBARCHIVE_CUSTOM_LIBS "liblzma" "nettle")
# Build libarchive
add_subdirectory(libarchive)
+128 -118
View File
@@ -23,6 +23,9 @@
#include <android/log.h>
#include <archive.h>
#include <archive_entry.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdbool.h>
#define LOG_TAG "libarchive_wrapper"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG ,__VA_ARGS__)
@@ -32,155 +35,162 @@
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG ,__VA_ARGS__)
#define BLOCK_SIZE 4096 * 16
static JNIEnv *env;
static int cur_index = 0; // Current entry we haven't read any data from it yet
static struct archive *arc = NULL;
static bool need_encrypt = false;
static char *passwd = NULL;
static void *archiveAddr = NULL;
static jlong archiveSize = 0;
typedef struct archive_inst_sc {
struct archive *archive;
JNIEnv *env;
jobject file;
jmethodID readID;
jmethodID seekID;
jmethodID skipID;
void *read_buffer;
void *output_buffer;
jbyteArray jbr;
} archive_inst;
const char supportExt[9][6] = {
"jpeg",
"jpg",
"png",
"gif",
"webp",
"bmp",
"ico",
"wbmp",
"heif"
};
static int filename_is_playable_file(const char *name) {
const char *dotptr = strrchr(name, '.');
if (dotptr++) {
switch (*dotptr) {
case 'j':
return (strcmp(dotptr, "jpg") == 0) || (strcmp(dotptr, "jpeg") == 0);
case 'p':
return strcmp(dotptr, "png") == 0;
case 'g':
return strcmp(dotptr, "gif") == 0;
default:
return 0;
}
}
return 0;
if (!dotptr++)
return false;
int i;
for (i = 0; i < 9; i++)
if (strcmp(dotptr, supportExt[i]) == 0)
return true;
return false;
}
static long archive_list_all_entries(struct archive_inst_sc *inst) {
struct archive *a = inst->archive;
static long archive_list_all_entries() {
struct archive_entry *entry;
long count = 0;
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
while (archive_read_next_header(arc, &entry) == ARCHIVE_OK) {
if (filename_is_playable_file(archive_entry_pathname(entry)))
count++;
}
if (!count)
LOGE("%s", archive_error_string(inst->archive));
LOGE("%s", archive_error_string(arc));
return count;
}
ssize_t ins_read(struct archive *a, void *client_data_ptr, const void **buff) {
archive_inst *arc = client_data_ptr;
int32_t r = (*arc->env)->CallIntMethod(arc->env, arc->file, arc->readID, arc->jbr);
(*arc->env)->GetByteArrayRegion(arc->env, arc->jbr, 0, r, arc->read_buffer);
*buff = arc->read_buffer;
return r;
static void archive_release() {
if (arc) {
archive_read_close(arc);
archive_read_free(arc);
arc = NULL;
}
}
int ins_close(struct archive *a, void *client_data) {
archive_inst *arc = client_data;
JNIEnv *env = arc->env;
jmethodID rewind = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, arc->file), "rewind",
"()V");
(*arc->env)->CallVoidMethod(arc->env, arc->file, rewind);
return ARCHIVE_OK;
}
la_int64_t ins_seek(struct archive *a, void *client_data, la_int64_t offset, int whence) {
archive_inst *arc = client_data;
jlong ret = (*arc->env)->CallLongMethod(arc->env, arc->file, arc->seekID, offset, whence);
return ret;
}
la_int64_t ins_skip(struct archive *a, void *client_data, la_int64_t request) {
archive_inst *arc = client_data;
jlong ret = (*arc->env)->CallLongMethod(arc->env, arc->file, arc->skipID, request);
return ret;
}
static archive_inst *archive_create_inst(JNIEnv *env, jobject file) {
archive_inst *inst = malloc(sizeof(archive_inst));
inst->archive = archive_read_new();
archive_read_set_seek_callback(inst->archive, ins_seek);
archive_read_set_skip_callback(inst->archive, ins_skip);
archive_read_support_format_all(inst->archive);
archive_read_support_filter_all(inst->archive);
inst->env = env;
inst->file = file;
inst->readID = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, file), "read", "([B)I");
inst->seekID = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, file), "seek", "(JI)J");
inst->skipID = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, file), "skip", "(J)J");
inst->jbr = (*env)->NewByteArray(env, BLOCK_SIZE);
inst->read_buffer = malloc(BLOCK_SIZE);
inst->output_buffer = malloc(BLOCK_SIZE);
return inst;
}
static void archive_destroy_inst(archive_inst *inst) {
JNIEnv *env = inst->env;
archive_read_free(inst->archive);
(*env)->DeleteLocalRef(env, inst->jbr);
free(inst->read_buffer);
free(inst->output_buffer);
free(inst);
static int archive_alloc() {
archive_release();
arc = archive_read_new();
archive_read_support_format_all(arc);
archive_read_support_filter_all(arc);
if (passwd)
archive_read_add_passphrase(arc, passwd);
return archive_read_open_memory(arc, archiveAddr, archiveSize);
}
JNIEXPORT jint JNICALL
Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *env, jobject thiz, jobject osf) {
archive_inst *arc = archive_create_inst(env, osf);
long r = archive_read_open(arc->archive, arc, NULL, ins_read, ins_close);
Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *_, jobject thiz, jlong addr, jlong size) {
archiveAddr = (void *) addr;
archiveSize = size;
env = _;
madvise(archiveAddr, archiveSize, MADV_SEQUENTIAL | MADV_WILLNEED);
long r = archive_alloc();
if (r) {
r = 0;
LOGE("%s%s", "Archive open failed:", archive_error_string(arc->archive));
LOGE("%s%s", "Archive open failed:", archive_error_string(arc));
} else {
r = archive_list_all_entries(arc);
r = archive_list_all_entries();
LOGI("%s%ld%s", "Found ", r, " image entries in archive");
// We must read through the file|vm then we can know whether it is encrypted
int encryptRet = archive_read_has_encrypted_entries(arc);
switch (encryptRet) {
case 1: // At lease 1 encrypted entry
need_encrypt = true;
break;
case 0: // format supports but no encrypted entry found
default:
need_encrypt = false;
}
LOGI("%s%d", "archive_read_has_encrypted_entries returns ", encryptRet);
}
archive_read_close(arc->archive);
archive_destroy_inst(arc);
archive_release();
return r;
}
JNIEXPORT jint JNICALL
Java_com_hippo_UriArchiveAccessor_extracttoOutputStream(JNIEnv *env, jobject thiz, jobject osf,
jint index, jobject os) {
int count = 0;
typedef struct Memarea {
void *buffer;
long size;
} Memarea;
JNIEXPORT jlong JNICALL
Java_com_hippo_UriArchiveAccessor_extracttoOutputStream(JNIEnv *_, jobject thiz, jint index) {
struct archive_entry *entry;
archive_inst *arc = archive_create_inst(env, osf);
la_ssize_t size;
jmethodID writeID = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, os), "write", "([B)V");
jmethodID flushID = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, os), "flush", "()V");
jmethodID closeID = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, os), "close", "()V");
archive_read_open(arc->archive, arc, NULL, ins_read, ins_close);
while (archive_read_next_header(arc->archive, &entry) == ARCHIVE_OK) {
int ret;
if (!arc || index < cur_index) {
archive_alloc();
cur_index = 0;
}
Memarea *memarea = malloc(sizeof(Memarea));
while (archive_read_next_header(arc, &entry) == ARCHIVE_OK) {
if (!filename_is_playable_file(archive_entry_pathname(entry)))
continue;
if (count++ == index) {
for (;;) {
size = archive_read_data(arc->archive, arc->output_buffer, BLOCK_SIZE);
if (size < 0) {
LOGE("%s%s", "Archive read failed:", archive_error_string(arc->archive));
goto free;
}
(*arc->env)->SetByteArrayRegion(env, arc->jbr, 0, size, arc->output_buffer);
(*arc->env)->CallVoidMethod(env, os, writeID, arc->jbr);
(*arc->env)->CallVoidMethod(env, os, flushID);
if (size < BLOCK_SIZE)
break;
}
if (cur_index++ == index) {
memarea->size = archive_entry_size(entry);
memarea->buffer = malloc(memarea->size);
ret = archive_read_data(arc, memarea->buffer, memarea->size);
if (ret != memarea->size)
LOGE("%s", "No enough data read, WTF?");
if (ret < 0)
LOGE("%s%s", "Archive read failed:", archive_error_string(arc));
break;
}
}
free:
(*arc->env)->CallVoidMethod(env, os, closeID);
archive_read_close(arc->archive);
archive_destroy_inst(arc);
return 0;
}
return (jlong) memarea;
}
JNIEXPORT void JNICALL
Java_com_hippo_UriArchiveAccessor_closeArchive(JNIEnv *jniEnv, jobject thiz) {
archive_release();
free(passwd);
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_UriArchiveAccessor_needPassword(JNIEnv *_, jobject thiz) {
return need_encrypt;
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_UriArchiveAccessor_providePassword(JNIEnv *_, jobject thiz, jstring str) {
struct archive_entry *entry;
jboolean ret = true;
int len = (*env)->GetStringUTFLength(env, str);
if (passwd)
free(passwd);
passwd = calloc(len, sizeof(char));
strcpy(passwd, (*env)->GetStringUTFChars(env, str, NULL));
archive_alloc();
while (archive_read_next_header(arc, &entry) == ARCHIVE_OK) {
if (!filename_is_playable_file(archive_entry_pathname(entry)))
continue;
if (!archive_entry_is_encrypted(entry))
continue;
void *tmpBuf = malloc(4096);
if (archive_read_data(arc, tmpBuf, 4096) < ARCHIVE_OK) {
LOGE("%s%s", "Archive read failed:", archive_error_string(arc));
ret = false;
}
free(tmpBuf);
break;
}
archive_release();
return ret;
}
+18 -5
View File
@@ -13,9 +13,22 @@
# limitations under the License.
cmake_minimum_required(VERSION 3.4.1)
project(image C)
add_subdirectory(image)
add_subdirectory(stream)
add_subdirectory(libjpeg-turbo)
add_subdirectory(libpng)
add_subdirectory(giflib)
set(IMAGE_SOURCES
image.c
image_utils.c
java_wrapper.c
)
set(IMAGE_INCLUDES
${CMAKE_CURRENT_SOURCE_DIR}
)
set(IMAGE_LIBRARIES
log
jnigraphics
GLESv2
)
add_library(image STATIC ${IMAGE_SOURCES})
target_include_directories(image PRIVATE ${IMAGE_INCLUDES})
target_link_libraries(image PRIVATE ${IMAGE_LIBRARIES})
@@ -1,27 +0,0 @@
# Copyright 2018 Hippo Seven
#
# 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.
cmake_minimum_required(VERSION 3.4.1)
project(gif C)
set(GIFLIB_SOURCES
giflib/dgif_lib.c
giflib/gif_hash.c
giflib/gifalloc.c
giflib/openbsd-reallocarray.c
)
set(GIFLIB_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/giflib)
add_library(gif STATIC ${GIFLIB_SOURCES})
target_include_directories(gif PUBLIC ${GIFLIB_INCLUDES})
+110
View File
@@ -0,0 +1,110 @@
/*
* Copyright 2022 Tarsin Norbin
*
* This file is part of EhViewer
*
* EhViewer is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* EhViewer is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* EhViewer. If not, see <https://www.gnu.org/licenses/>.
*/
#include <android/data_space.h>
#include <stdlib.h>
#include <string.h>
#include "image.h"
#include "image_utils.h"
void decodeCommon(IMAGE *image, SOURCE *src) {
image->src = src;
AImageDecoder *decoder = src->decoder;
image->isAnimated = AImageDecoder_isAnimated(decoder);
AImageDecoder_setDataSpace(decoder, ADATASPACE_SRGB);
AImageDecoder_setAndroidBitmapFormat(decoder, ANDROID_BITMAP_FORMAT_RGBA_8888);
const AImageDecoderHeaderInfo *headerInfo = AImageDecoder_getHeaderInfo(decoder);
image->height = AImageDecoderHeaderInfo_getHeight(headerInfo);
image->width = AImageDecoderHeaderInfo_getWidth(headerInfo);
image->alpha =
AImageDecoderHeaderInfo_getAlphaFlags(headerInfo) == ANDROID_BITMAP_FLAGS_ALPHA_PREMUL;
src->stride = AImageDecoder_getMinimumStride(decoder);
image->bufferLen = (int) (image->height * src->stride);
image->buffer = malloc(image->bufferLen);
if (image->isAnimated) {
AImageDecoder_setInternallyHandleDisposePrevious(decoder, false);
AImageDecoderFrameInfo *frameInfo = AImageDecoderFrameInfo_create();
AImageDecoder_getFrameInfo(decoder, frameInfo);
image->delay = (int) AImageDecoderFrameInfo_getDuration(frameInfo) / 1000000;
AImageDecoderFrameInfo_delete(frameInfo);
}
AImageDecoder_decodeImage(decoder, image->buffer, src->stride, image->bufferLen);
if (!image->isAnimated) {
recycleSource(image->src);
image->src = NULL;
}
}
IMAGE *createFromFd(int fd) {
AImageDecoder *decoder;
AImageDecoder_createFromFd(fd, &decoder);
IMAGE *image = calloc(1, sizeof(IMAGE));
SOURCE *src = calloc(1, sizeof(SOURCE));
src->decoder = decoder;
src->fd = fd;
decodeCommon(image, src);
return image;
}
IMAGE *createFromAddr(void *addr, long size) {
AImageDecoder *decoder;
AImageDecoder_createFromBuffer((const void *) addr, size, &decoder);
IMAGE *image = calloc(1, sizeof(IMAGE));
SOURCE *src = calloc(1, sizeof(SOURCE));
src->decoder = decoder;
src->srcBuffer = addr;
decodeCommon(image, src);
return image;
}
IMAGE *create(int32_t width, int32_t height, const void *data) {
IMAGE *plain = calloc(1, sizeof(IMAGE));
void *buffer = NULL;
size_t length = width * height * 4;
buffer = malloc(length);
memcpy(buffer, data, length);
plain->width = width;
plain->height = height;
plain->buffer = buffer;
plain->bufferLen = (int) length;
return plain;
}
void render(IMAGE *image, int src_x, int src_y,
void *dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height) {
copy_pixels(image->buffer, image->width, image->height, src_x, src_y, dst, dst_w, dst_h, dst_x,
dst_y, width, height);
}
void advance(IMAGE *image) {
if (image->isAnimated) {
AImageDecoder_advanceFrame(image->src->decoder);
if (AImageDecoder_decodeImage(image->src->decoder, image->buffer, image->src->stride,
image->bufferLen) == ANDROID_IMAGE_DECODER_FINISHED) {
AImageDecoder_rewind(image->src->decoder);
AImageDecoder_decodeImage(image->src->decoder, image->buffer, image->src->stride,
image->bufferLen);
}
}
}
+72
View File
@@ -0,0 +1,72 @@
/*
* Copyright 2022 Tarsin Norbin
*
* This file is part of EhViewer
*
* EhViewer is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* EhViewer is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* EhViewer. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef IMAGE_IMAGE_H
#define IMAGE_IMAGE_H
#include <android/imagedecoder.h>
#include <stdbool.h>
typedef struct {
AImageDecoder *decoder;
void *srcBuffer;
size_t stride;
int fd;
} SOURCE;
typedef struct {
int32_t width;
int32_t height;
void *buffer;
int bufferLen;
bool isAnimated;
bool alpha;
int delay;
SOURCE *src;
} IMAGE;
static void recycleSource(SOURCE *src) {
if (!src)
return;
AImageDecoder_delete(src->decoder);
free(src->srcBuffer);
free(src);
}
static void recycle(IMAGE *image) {
if (!image)
return;
recycleSource(image->src);
free(image->buffer);
free(image);
}
IMAGE *createFromFd(int fd);
IMAGE *createFromAddr(void *addr, long size);
IMAGE *create(int32_t width, int32_t height, const void *data);
void render(IMAGE *image, int src_x, int src_y,
void *dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height);
void advance(IMAGE *image);
#endif /* IMAGE_IMAGE_H */
@@ -1,60 +0,0 @@
# Copyright 2015 Hippo Seven
#
# 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.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
SUPPORT_FORMAT := plain jpeg png gif
LOCAL_MODULE := image
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../stream
LOCAL_SRC_FILES := \
image.c \
image_utils.c \
java_wrapper.c
LOCAL_LDLIBS := -llog -ljnigraphics -lGLESv2
LOCAL_STATIC_LIBRARIES := stream
ifeq ($(filter plain, $(SUPPORT_FORMAT)), plain)
LOCAL_SRC_FILES += image_plain.c
else
LOCAL_CFLAGS += -DIMAGE_NOT_SUPPORT_PLAIN
endif
ifeq ($(filter jpeg, $(SUPPORT_FORMAT)), jpeg)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../libjpeg-turbo
LOCAL_SRC_FILES += image_jpeg.c
LOCAL_STATIC_LIBRARIES += jpeg-turbo
else
LOCAL_CFLAGS += -DIMAGE_NOT_SUPPORT_JPEG
endif
ifeq ($(filter png, $(SUPPORT_FORMAT)), png)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../libpng
LOCAL_SRC_FILES += image_png.c
LOCAL_STATIC_LIBRARIES += png
else
LOCAL_CFLAGS += -DIMAGE_NOT_SUPPORT_PNG
endif
ifeq ($(filter gif, $(SUPPORT_FORMAT)), gif)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../giflib
LOCAL_SRC_FILES += image_gif.c
LOCAL_STATIC_LIBRARIES += gif
else
LOCAL_CFLAGS += -DIMAGE_NOT_SUPPORT_GIF
endif
include $(BUILD_SHARED_LIBRARY)
@@ -1,44 +0,0 @@
# Copyright 2018 Hippo Seven
#
# 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.
cmake_minimum_required(VERSION 3.4.1)
project(image C)
set(IMAGE_SOURCES
image.c
image_utils.c
image_plain.c
image_jpeg.c
image_png.c
image_gif.c
filter/clahe.c
filter/gray.c
java_wrapper.c
)
set(IMAGE_INCLUDES
${CMAKE_CURRENT_SOURCE_DIR}
)
set(IMAGE_LIBRARIES
log
jnigraphics
GLESv2
stream
jpeg
png
gif
)
add_library(image STATIC ${IMAGE_SOURCES})
target_include_directories(image PRIVATE ${IMAGE_INCLUDES})
target_link_libraries(image PRIVATE ${IMAGE_LIBRARIES})
-37
View File
@@ -1,37 +0,0 @@
/*
* Copyright 2015 Hippo Seven
*
* 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.
*/
//
// Created by Hippo on 12/27/2015.
//
#ifndef IMAGE_CONFIG_H
#define IMAGE_CONFIG_H
#ifndef IMAGE_NOT_SUPPORT_PLAIN
#define IMAGE_SUPPORT_PLAIN
#endif
#ifndef IMAGE_NOT_SUPPORT_JPEG
#define IMAGE_SUPPORT_JPEG
#endif
#ifndef IMAGE_NOT_SUPPORT_PNG
#define IMAGE_SUPPORT_PNG
#endif
#ifndef IMAGE_NOT_SUPPORT_GIF
#define IMAGE_SUPPORT_GIF
#endif
#endif //IMAGE_CONFIG_H
@@ -1,184 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* 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 "clahe.h"
#include "pixel.h"
#include "../../utils.h"
#include "../../log.h"
#define TILE_BLOCK 8
#define LIMIT_MULTIPLE 4
#define CL_MAX_LOOP_NUMBER 4
static BYTE clamp(int t) {
t = t > 255 ? 255 : t;
t = t < 0 ? 0 : t;
return (BYTE) t;
}
void IMAGE_clahe(void* data, int width, int height, bool to_gray) {
if (width < TILE_BLOCK || height < TILE_BLOCK) {
return;
}
PIXEL* pixels = data;
if (to_gray) {
for (int i = 0; i < width * height; ++i) {
PIXEL* p = pixels + i;
int t1 = (int) (p->c1 * 0.299f + p->c2 * 0.587f + p->c3 * 0.114f);
p->c1 = clamp(t1);
}
} else {
// RGBA to YUVA
for (int i = 0; i < width * height; ++i) {
PIXEL* p = pixels + i;
int t1 = (int) (p->c1 * 0.299f + p->c2 * 0.587f + p->c3 * 0.114f);
int t2 = (int) (p->c1 * -0.169f + p->c2 * -0.331f + p->c3 * 0.5f + 128);
int t3 = (int) (p->c1 * 0.5f + p->c2 * -0.419f + p->c3 * -0.081f + 128);
p->c1 = clamp(t1);
p->c2 = clamp(t2);
p->c3 = clamp(t3);
}
}
int tile_width = width / TILE_BLOCK;
int tile_height = height / TILE_BLOCK;
int histogram[TILE_BLOCK * TILE_BLOCK][256] = {0};
float accumulation[TILE_BLOCK * TILE_BLOCK][256] = {0.0f};
// Build histogram and accumulation
for (int i = 0; i < TILE_BLOCK; i++) {
for (int j = 0; j < TILE_BLOCK; j++) {
int x_start = tile_width * i;
int x_end = i == TILE_BLOCK - 1 ? width : x_start + tile_width;
int y_start = tile_height * j;
int y_end = j == TILE_BLOCK - 1 ? height : y_start + tile_height;
int tile_size = (x_end - x_start) * (y_end - y_start);
int tile_index = i + TILE_BLOCK * j;
// Count
for(int x = x_start; x < x_end; x++) {
for(int y = y_start; y < y_end ; y++) {
PIXEL* p = pixels + x + y * width;
histogram[tile_index][p->c1] += 1;
}
}
// CL
int limit = tile_size * LIMIT_MULTIPLE / 256;
for (int m = 0; m < CL_MAX_LOOP_NUMBER; m++) {
int steal = 0;
for (int n = 0; n < 256; n++) {
if (histogram[tile_index][n] > limit) {
steal += histogram[tile_index][n] - limit;
histogram[tile_index][n] = limit;
}
}
int bonus = steal / 256;
if (bonus == 0 && steal != 0 && m == 0) {
bonus = 1;
}
if (bonus == 0) {
break;
}
for (int n = 0; n < 256; n++) {
histogram[tile_index][n] += bonus;
}
}
// accumulation
for (int n = 0; n < 256; n++) {
accumulation[tile_index][n] = (float) histogram[tile_index][n] / tile_size;
if (n != 0) {
accumulation[tile_index][n] += accumulation[tile_index][n - 1];
}
}
}
}
// Interpolation
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
PIXEL* p = pixels + x + y * width;
// top-left corner
if (x <= tile_width / 2 && y <= tile_height / 2) {
p->c1 = clamp((int) (accumulation[0][p->c1] * 255));
continue;
}
// top-right corner
if (x > width - tile_width / 2 && y <= tile_height / 2) {
p->c1 = clamp((int) (accumulation[TILE_BLOCK - 1][p->c1] * 255));
continue;
}
// bottom-left corner
if (x <= tile_width / 2 && y > height - tile_height / 2) {
p->c1 = clamp((int) (accumulation[TILE_BLOCK * (TILE_BLOCK - 1)][p->c1] * 255));
continue;
}
// bottom-right corner
if (x > width - tile_width / 2 && y > height - tile_height / 2) {
p->c1 = clamp((int) (accumulation[TILE_BLOCK * TILE_BLOCK - 1][p->c1] * 255));
continue;
}
int tile_x_index = (x - tile_width / 2) / tile_width;
int tile_y_index = (y - tile_height / 2) / tile_height;
int tile_index1 = tile_x_index + TILE_BLOCK * tile_y_index;
int tile_index2 = tile_index1 + 1;
float u = (float) (x - (tile_x_index * tile_width + tile_width / 2)) / tile_width;
// top side and bottom side
if (y <= tile_height / 2 || y > height - tile_height / 2) {
p->c1 = clamp((int) (255 * (accumulation[tile_index1][p->c1] * (1 - u) + accumulation[tile_index2][p->c1] * u)));
continue;
}
int tile_index3 = tile_index1 + TILE_BLOCK;
float v = (float) (y - (tile_y_index * tile_height + tile_height / 2)) / tile_height;
// left side and right side
if (x <= tile_width / 2 || x > width - tile_width / 2) {
p->c1 = clamp((int) (255 * (accumulation[tile_index1][p->c1] * (1 - v) + accumulation[tile_index3][p->c1] * v)));
continue;
}
int tile_index4 = tile_index2 + TILE_BLOCK;
p->c1 = clamp((int) (255 * (
accumulation[tile_index1][p->c1] * (1 - u) * (1 - v)
+ accumulation[tile_index2][p->c1] * u * (1 - v)
+ accumulation[tile_index3][p->c1] * (1 - u) * v
+ accumulation[tile_index4][p->c1] * u * v)));
}
}
if (to_gray) {
for (int i = 0; i < width * height; ++i) {
PIXEL* p = pixels + i;
p->c2 = p->c1;
p->c3 = p->c1;
}
} else {
// YUVA to RGBA
for (int i = 0; i < width * height; ++i) {
PIXEL* p = pixels + i;
int t1 = (int) (p->c1 + (p->c2 - 128) * -0.00093f + (p->c3 - 128) * 1.401687f);
int t2 = (int) (p->c1 + (p->c2 - 128) * -0.3437f + (p->c3 - 128) * -0.71417f);
int t3 = (int) (p->c1 + (p->c2 - 128) * 1.77216f + (p->c3 - 128) * 0.00099f);
p->c1 = clamp(t1);
p->c2 = clamp(t2);
p->c3 = clamp(t3);
}
}
}
@@ -1,24 +0,0 @@
/*
* Copyright 2018 Hippo Seven
*
* 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.
*/
#ifndef IMAGE_CLAHE_H
#define IMAGE_CLAHE_H
#include <stdbool.h>
void IMAGE_clahe(void* data, int width, int height, bool to_gray);
#endif //IMAGE_CLAHE_H
@@ -1,36 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* 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 "gray.h"
#include "pixel.h"
bool IMAGE_is_gray(void* data, int width, int height, int error) {
PIXEL* p = data;
for (int i = 0, n = width * height; i < n; i++, p++) {
int e1 = p->c1 - p->c2;
int e2 = p->c1 - p->c3;
int e3 = p->c2 - p->c3;
if (e1 > error || e1 < -error
|| e2 > error || e2 < -error
|| e3 > error || e3 < -error) {
return false;
}
}
return true;
}
@@ -1,24 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* 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.
*/
#ifndef IMAGE_GRAY_H
#define IMAGE_GRAY_H
#include <stdbool.h>
bool IMAGE_is_gray(void* data, int width, int height, int error);
#endif //IMAGE_GRAY_H
@@ -1,30 +0,0 @@
/*
* Copyright 2019 Hippo Seven
*
* 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.
*/
#ifndef IMAGE_PIXEL_H
#define IMAGE_PIXEL_H
typedef unsigned char BYTE;
typedef struct
{
BYTE c1;
BYTE c2;
BYTE c3;
BYTE c4;
} PIXEL;
#endif //IMAGE_PIXEL_H
-532
View File
@@ -1,532 +0,0 @@
/*
* Copyright 2015 Hippo Seven
*
* 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.
*/
//
// Created by Hippo on 12/27/2015.
//
#include "image.h"
#include "image_plain.h"
#include "image_jpeg.h"
#include "image_png.h"
#include "image_gif.h"
#include "filter/clahe.h"
#include "filter/gray.h"
#include "../log.h"
static int get_format(JNIEnv* env, InputStream* stream)
{
unsigned char temp[2];
int read = read_input_stream(env, stream, temp, 0, 2);
if (read == 2) {
#ifdef IMAGE_SUPPORT_JPEG
if (temp[0] == IMAGE_JPEG_MAGIC_NUMBER_0 && temp[1] == IMAGE_JPEG_MAGIC_NUMBER_1) {
return IMAGE_FORMAT_JPEG;
}
#endif
#ifdef IMAGE_SUPPORT_PNG
if (temp[0] == IMAGE_PNG_MAGIC_NUMBER_0 && temp[1] == IMAGE_PNG_MAGIC_NUMBER_1) {
return IMAGE_FORMAT_PNG;
}
#endif
#ifdef IMAGE_SUPPORT_GIF
if (temp[0] == IMAGE_GIF_MAGIC_NUMBER_0 && temp[1] == IMAGE_GIF_MAGIC_NUMBER_1) {
return IMAGE_FORMAT_GIF;
}
#endif
LOGE(MSG("Can't recognize the two magic number: %d, %d"), temp[0], temp[1]);
} else {
LOGE(MSG("Can't read two magic number from stream"));
}
return IMAGE_FORMAT_UNKNOWN;
}
void* decode(JNIEnv* env, InputStream* stream, bool partially, int* format)
{
unsigned char magic_numbers[2];
PatchHeadInputStream* patch_head_input_stream;
*format = get_format(env, stream);
switch (*format) {
#ifdef IMAGE_SUPPORT_JPEG
case IMAGE_FORMAT_JPEG:
magic_numbers[0] = IMAGE_JPEG_MAGIC_NUMBER_0;
magic_numbers[1] = IMAGE_JPEG_MAGIC_NUMBER_1;
break;
#endif
#ifdef IMAGE_SUPPORT_PNG
case IMAGE_FORMAT_PNG:
magic_numbers[0] = IMAGE_PNG_MAGIC_NUMBER_0;
magic_numbers[1] = IMAGE_PNG_MAGIC_NUMBER_1;
break;
#endif
#ifdef IMAGE_SUPPORT_GIF
case IMAGE_FORMAT_GIF:
magic_numbers[0] = IMAGE_GIF_MAGIC_NUMBER_0;
magic_numbers[1] = IMAGE_GIF_MAGIC_NUMBER_1;
break;
#endif
default:
LOGE(MSG("Can't detect format %d"), *format);
destroy_input_stream(env, &stream);
return NULL;
}
patch_head_input_stream = create_patch_head_input_stream(stream, magic_numbers, 2);
if (patch_head_input_stream == NULL){
WTF_OM;
destroy_input_stream(env, &stream);
return NULL;
}
switch (*format) {
#ifdef IMAGE_SUPPORT_JPEG
case IMAGE_FORMAT_JPEG:
return JPEG_decode(env, patch_head_input_stream, partially);
#endif
#ifdef IMAGE_SUPPORT_PNG
case IMAGE_FORMAT_PNG:
return PNG_decode(env, patch_head_input_stream, partially);
#endif
#ifdef IMAGE_SUPPORT_GIF
case IMAGE_FORMAT_GIF:
return GIF_decode(env, patch_head_input_stream, partially);
#endif
default:
LOGE(MSG("Can't detect format %d"), *format);
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
}
void* create(unsigned int width, unsigned int height, const void* data)
{
#ifdef IMAGE_SUPPORT_PLAIN
return PLAIN_create(width, height, data);
#else
return NULL;
#endif
}
bool complete(JNIEnv* env, void* image, int format)
{
switch (format) {
#ifdef IMAGE_SUPPORT_PLAIN
case IMAGE_FORMAT_PLAIN:
return PLAIN_complete((PLAIN*) image);
#endif
#ifdef IMAGE_SUPPORT_JPEG
case IMAGE_FORMAT_JPEG:
return JPEG_complete((JPEG*) image);
#endif
#ifdef IMAGE_SUPPORT_PNG
case IMAGE_FORMAT_PNG:
return PNG_complete(env, (PNG*) image);
#endif
#ifdef IMAGE_SUPPORT_GIF
case IMAGE_FORMAT_GIF:
return GIF_complete(env, (GIF*) image);
#endif
default:
LOGE(MSG("Can't detect format %d"), format);
return false;
}
}
bool is_completed(void* image, int format)
{
switch (format) {
#ifdef IMAGE_SUPPORT_PLAIN
case IMAGE_FORMAT_PLAIN:
return PLAIN_is_completed((PLAIN*) image);
#endif
#ifdef IMAGE_SUPPORT_JPEG
case IMAGE_FORMAT_JPEG:
return JPEG_is_completed((JPEG*) image);
#endif
#ifdef IMAGE_SUPPORT_PNG
case IMAGE_FORMAT_PNG:
return PNG_is_completed((PNG*) image);
#endif
#ifdef IMAGE_SUPPORT_GIF
case IMAGE_FORMAT_GIF:
return GIF_is_completed((GIF*) image);
#endif
default:
LOGE(MSG("Can't detect format %d"), format);
return false;
}
}
int get_width(void* image, int format)
{
switch (format) {
#ifdef IMAGE_SUPPORT_PLAIN
case IMAGE_FORMAT_PLAIN:
return PLAIN_get_width((PLAIN*) image);
#endif
#ifdef IMAGE_SUPPORT_JPEG
case IMAGE_FORMAT_JPEG:
return JPEG_get_width((JPEG*) image);
#endif
#ifdef IMAGE_SUPPORT_PNG
case IMAGE_FORMAT_PNG:
return PNG_get_width((PNG*) image);
#endif
#ifdef IMAGE_SUPPORT_GIF
case IMAGE_FORMAT_GIF:
return GIF_get_width((GIF*) image);
#endif
default:
LOGE(MSG("Can't detect format %d"), format);
return -1;
}
}
int get_height(void* image, int format)
{
switch (format) {
#ifdef IMAGE_SUPPORT_PLAIN
case IMAGE_FORMAT_PLAIN:
return PLAIN_get_height((PLAIN*) image);
#endif
#ifdef IMAGE_SUPPORT_JPEG
case IMAGE_FORMAT_JPEG:
return JPEG_get_height((JPEG*) image);
#endif
#ifdef IMAGE_SUPPORT_PNG
case IMAGE_FORMAT_PNG:
return PNG_get_height((PNG*) image);
#endif
#ifdef IMAGE_SUPPORT_GIF
case IMAGE_FORMAT_GIF:
return GIF_get_height((GIF*) image);
#endif
default:
LOGE(MSG("Can't detect format %d"), format);
return -1;
}
}
int get_byte_count(void* image, int format)
{
switch (format) {
#ifdef IMAGE_SUPPORT_PLAIN
case IMAGE_FORMAT_PLAIN:
return PLAIN_get_byte_count((PLAIN*) image);
#endif
#ifdef IMAGE_SUPPORT_JPEG
case IMAGE_FORMAT_JPEG:
return JPEG_get_byte_count((JPEG*) image);
#endif
#ifdef IMAGE_SUPPORT_PNG
case IMAGE_FORMAT_PNG:
return PNG_get_byte_count((PNG*) image);
#endif
#ifdef IMAGE_SUPPORT_GIF
case IMAGE_FORMAT_GIF:
return GIF_get_byte_count((GIF*) image);
#endif
default:
LOGE(MSG("Can't detect format %d"), format);
return -1;
}
}
void render(void* image, int format, int src_x, int src_y,
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height, bool fill_blank, int default_color)
{
switch (format) {
#ifdef IMAGE_SUPPORT_PLAIN
case IMAGE_FORMAT_PLAIN:
PLAIN_render((PLAIN*) image, src_x, src_y,
dst, dst_w, dst_h, dst_x, dst_y,
width, height, fill_blank, default_color);
break;
#endif
#ifdef IMAGE_SUPPORT_JPEG
case IMAGE_FORMAT_JPEG:
JPEG_render((JPEG*) image, src_x, src_y,
dst, dst_w, dst_h, dst_x, dst_y,
width, height, fill_blank, default_color);
break;
#endif
#ifdef IMAGE_SUPPORT_PNG
case IMAGE_FORMAT_PNG:
PNG_render((PNG*) image, src_x, src_y,
dst, dst_w, dst_h, dst_x, dst_y,
width, height, fill_blank, default_color);
break;
#endif
#ifdef IMAGE_SUPPORT_GIF
case IMAGE_FORMAT_GIF:
GIF_render((GIF*) image, src_x, src_y,
dst, dst_w, dst_h, dst_x, dst_y,
width, height, fill_blank, default_color);
break;
#endif
default:
LOGE(MSG("Can't detect format %d"), format);
break;
}
}
void advance(void* image, int format)
{
switch (format) {
#ifdef IMAGE_SUPPORT_PLAIN
case IMAGE_FORMAT_PLAIN:
PLAIN_advance((PLAIN*) image);
break;
#endif
#ifdef IMAGE_SUPPORT_JPEG
case IMAGE_FORMAT_JPEG:
JPEG_advance((JPEG*) image);
break;
#endif
#ifdef IMAGE_SUPPORT_PNG
case IMAGE_FORMAT_PNG:
PNG_advance((PNG*) image);
break;
#endif
#ifdef IMAGE_SUPPORT_GIF
case IMAGE_FORMAT_GIF:
GIF_advance((GIF*) image);
break;
#endif
default:
LOGE(MSG("Can't detect format %d"), format);
}
}
int get_delay(void* image, int format)
{
switch (format) {
#ifdef IMAGE_SUPPORT_PLAIN
case IMAGE_FORMAT_PLAIN:
return PLAIN_get_delay((PLAIN*) image);
#endif
#ifdef IMAGE_SUPPORT_JPEG
case IMAGE_FORMAT_JPEG:
return JPEG_get_delay((JPEG*) image);
#endif
#ifdef IMAGE_SUPPORT_PNG
case IMAGE_FORMAT_PNG:
return PNG_get_delay((PNG*) image);
#endif
#ifdef IMAGE_SUPPORT_GIF
case IMAGE_FORMAT_GIF:
return GIF_get_delay((GIF*) image);
#endif
default:
LOGE(MSG("Can't detect format %d"), format);
return false;
}
}
int get_frame_count(void* image, int format)
{
switch (format) {
#ifdef IMAGE_SUPPORT_PLAIN
case IMAGE_FORMAT_PLAIN:
return PLAIN_get_frame_count((PLAIN*) image);
#endif
#ifdef IMAGE_SUPPORT_JPEG
case IMAGE_FORMAT_JPEG:
return JPEG_get_frame_count((JPEG*) image);
#endif
#ifdef IMAGE_SUPPORT_PNG
case IMAGE_FORMAT_PNG:
return PNG_get_frame_count((PNG*) image);
#endif
#ifdef IMAGE_SUPPORT_GIF
case IMAGE_FORMAT_GIF:
return GIF_get_frame_count((GIF*) image);
#endif
default:
LOGE(MSG("Can't detect format %d"), format);
return false;
}
}
bool is_opaque(void* image, int format)
{
switch (format) {
#ifdef IMAGE_SUPPORT_PLAIN
case IMAGE_FORMAT_PLAIN:
return PLAIN_is_opaque((PLAIN*) image);
#endif
#ifdef IMAGE_SUPPORT_JPEG
case IMAGE_FORMAT_JPEG:
return JPEG_is_opaque((JPEG*) image);
#endif
#ifdef IMAGE_SUPPORT_PNG
case IMAGE_FORMAT_PNG:
return PNG_is_opaque((PNG*) image);
#endif
#ifdef IMAGE_SUPPORT_GIF
case IMAGE_FORMAT_GIF:
return GIF_is_opaque((GIF*) image);
#endif
default:
LOGE(MSG("Can't detect format %d"), format);
return false;
}
}
static void get_image_data(void* image, int format, void** pixel, int* width, int* height) {
switch (format) {
#ifdef IMAGE_SUPPORT_PLAIN
case IMAGE_FORMAT_PLAIN: {
PLAIN* plain = (PLAIN*) image;
*pixel = PLAIN_get_pixels(plain);
*width = PLAIN_get_width(plain);
*height = PLAIN_get_height(plain);
break;
}
#endif
#ifdef IMAGE_SUPPORT_JPEG
case IMAGE_FORMAT_JPEG: {
JPEG* jpeg = (JPEG*) image;
*pixel = JPEG_get_pixels(jpeg);
*width = JPEG_get_width(jpeg);
*height = JPEG_get_height(jpeg);
break;
}
#endif
#ifdef IMAGE_SUPPORT_PNG
case IMAGE_FORMAT_PNG: {
PNG* png = (PNG*) image;
*pixel = PNG_get_pixels(png);
*width = PNG_get_width(png);
*height = PNG_get_height(png);
break;
}
#endif
#ifdef IMAGE_SUPPORT_GIF
case IMAGE_FORMAT_GIF: {
GIF* gif = (GIF*) image;
*pixel = GIF_get_pixels(gif);
*width = GIF_get_width(gif);
*height = GIF_get_height(gif);
break;
}
#endif
default:
*pixel = NULL;
*width = 0;
*height = 0;
break;
}
}
bool is_gray(void* image, int format, int error)
{
void* pixel = NULL;
int width = 0;
int height = 0;
get_image_data(image, format, &pixel, &width, &height);
if (pixel == NULL || width == 0 || height == 0) {
return false;
}
return IMAGE_is_gray(pixel, width, height, error);
}
void clahe(void* image, int format, bool to_gray)
{
void* pixel = NULL;
int width = 0;
int height = 0;
get_image_data(image, format, &pixel, &width, &height);
if (pixel == NULL || width == 0 || height == 0) {
return;
}
IMAGE_clahe(pixel, width, height, to_gray);
}
void recycle(JNIEnv *env, void* image, int format)
{
switch (format) {
#ifdef IMAGE_SUPPORT_PLAIN
case IMAGE_FORMAT_PLAIN:
PLAIN_recycle((PLAIN*) image);
break;
#endif
#ifdef IMAGE_SUPPORT_JPEG
case IMAGE_FORMAT_JPEG:
JPEG_recycle((JPEG*) image);
break;
#endif
#ifdef IMAGE_SUPPORT_PNG
case IMAGE_FORMAT_PNG:
PNG_recycle(env, (PNG*) image);
break;
#endif
#ifdef IMAGE_SUPPORT_GIF
case IMAGE_FORMAT_GIF:
GIF_recycle(env, (GIF*) image);
break;
#endif
default:
LOGE(MSG("Can't detect format %d"), format);
}
}
int get_supported_formats(int *formats)
{
int i = 0;
#ifdef IMAGE_SUPPORT_JPEG
formats[i++] = IMAGE_FORMAT_JPEG;
#endif
#ifdef IMAGE_SUPPORT_PNG
formats[i++] = IMAGE_FORMAT_PNG;
#endif
#ifdef IMAGE_SUPPORT_GIF
formats[i++] = IMAGE_FORMAT_GIF;
#endif
return i;
}
const char *get_decoder_description(int format)
{
switch (format) {
#ifdef IMAGE_SUPPORT_JPEG
case IMAGE_FORMAT_JPEG:
return IMAGE_JPEG_DECODER_DESCRIPTION;
#endif
#ifdef IMAGE_SUPPORT_PNG
case IMAGE_FORMAT_PNG:
return IMAGE_PNG_DECODER_DESCRIPTION;
#endif
#ifdef IMAGE_SUPPORT_GIF
case IMAGE_FORMAT_GIF:
return IMAGE_GIF_DECODER_DESCRIPTION;
#endif
default:
return NULL;
}
}
-65
View File
@@ -1,65 +0,0 @@
/*
* Copyright 2015 Hippo Seven
*
* 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.
*/
//
// Created by Hippo on 12/27/2015.
//
#ifndef IMAGE_IMAGE_H
#define IMAGE_IMAGE_H
#include <stdbool.h>
#include "config.h"
#include "input_stream.h"
#define IMAGE_FORMAT_UNKNOWN -1
#ifdef IMAGE_SUPPORT_PLAIN
#define IMAGE_FORMAT_PLAIN 0
#endif
#ifdef IMAGE_SUPPORT_JPEG
#define IMAGE_FORMAT_JPEG 1
#endif
#ifdef IMAGE_SUPPORT_PNG
#define IMAGE_FORMAT_PNG 2
#endif
#ifdef IMAGE_SUPPORT_GIF
#define IMAGE_FORMAT_GIF 3
#endif
#define IMAGE_MAX_SUPPORTED_FORMAT_COUNT 3
void* decode(JNIEnv* env, InputStream* stream, bool partially, int* format);
void* create(unsigned int width, unsigned int height, const void* data);
bool complete(JNIEnv* env, void* image, int format);
bool is_completed(void* image, int format);
int get_width(void* image, int format);
int get_height(void* image, int format);
int get_byte_count(void* image, int format);
void render(void* image, int format, int src_x, int src_y,
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height, bool fill_blank, int default_color);
void advance(void* image, int format);
int get_delay(void* image, int format);
int get_frame_count(void* image, int format);
bool is_opaque(void* image, int format);
bool is_gray(void* image, int format, int error);
void clahe(void* image, int format, bool to_gray);
void recycle(JNIEnv *env, void* image, int format);
int get_supported_formats(int *);
const char *get_decoder_description(int);
#endif //IMAGE_IMAGE_H
-558
View File
@@ -1,558 +0,0 @@
/*
* Copyright 2015 Hippo Seven
*
* 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.
*/
//
// Created by Hippo on 12/27/2015.
//
#include "config.h"
#ifdef IMAGE_SUPPORT_GIF
#include <stdlib.h>
#include <string.h>
#include "image_gif.h"
#include "image_utils.h"
#include "java_wrapper.h"
#include "../log.h"
#include "../utils.h"
static int error_code = 0;
typedef struct {
unsigned char red;
unsigned char green;
unsigned char blue;
unsigned char alpha;
} RGBA;
static int custom_read_fun(GifFileType* gif, GifByteType* bytes, int size) {
bool attach;
int read;
PatchHeadInputStream* patch_head_input_stream = gif->UserData;
JNIEnv *env = obtain_env(&attach);
if (env == NULL) {
LOGE(MSG("Can't get JNIEnv"));
return 0;
}
read = read_patch_head_input_stream(env, patch_head_input_stream, bytes, 0, (size_t) size);
if (attach) {
release_env();
}
return read;
}
static void generate_prepare(GIF_FRAME_INFO* frame_info_array, int count)
{
int disposal = 0xff;
int i;
for (i = 0; i < count; i++){
GIF_FRAME_INFO* frame_info = frame_info_array + i;
switch (disposal) {
case DISPOSAL_UNSPECIFIED:
case DISPOSE_DO_NOT:
frame_info->prepare = IMAGE_GIF_PREPARE_NONE;
break;
case DISPOSE_BACKGROUND:
frame_info->prepare = IMAGE_GIF_PREPARE_BACKGROUND;
break;
case DISPOSE_PREVIOUS:
frame_info->prepare = IMAGE_GIF_PREPARE_USE_BACKUP;
break;
default:
frame_info->prepare = IMAGE_GIF_PREPARE_BACKGROUND;
break;
}
disposal = frame_info->disposal;
}
}
static void read_gcb(GifFileType* gif_file, int index, GIF_FRAME_INFO* frame_info)
{
GraphicsControlBlock gcb;
if (DGifSavedExtensionToGCB(gif_file, index, &gcb) == GIF_OK) {
frame_info->tran = gcb.TransparentColor;
frame_info->delay = gcb.DelayTime * 10;
frame_info->disposal = gcb.DisposalMode;
frame_info->prepare = IMAGE_GIF_PREPARE_UNKNOWN;
} else {
frame_info->tran = -1;
frame_info->delay = 0;
frame_info->disposal = DISPOSE_DO_NOT;
frame_info->prepare = IMAGE_GIF_PREPARE_UNKNOWN;
}
}
static void fix_gif_file(GifFileType* gif_file) {
if (gif_file->ImageCount == 0) {
return;
}
SavedImage last_image = gif_file->SavedImages[gif_file->ImageCount - 1];
// If RasterBits is NULL, discard this frame
if (last_image.RasterBits == NULL) {
gif_file->ImageCount--;
// Free last image
if (last_image.ImageDesc.ColorMap != NULL) {
GifFreeMapObject(last_image.ImageDesc.ColorMap);
last_image.ImageDesc.ColorMap = NULL;
}
GifFreeExtensions(&last_image.ExtensionBlockCount, &last_image.ExtensionBlocks);
}
}
void* GIF_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially)
{
GIF* gif = NULL;
GifFileType* gif_file = NULL;
void* buffer = NULL;
void*shown_buffer = NULL;
GIF_FRAME_INFO* frame_info_array = NULL;
int i;
gif = (GIF*) malloc(sizeof(GIF));
if (gif == NULL) {
WTF_OM;
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
// Open
gif_file = DGifOpen(patch_head_input_stream, &custom_read_fun, &error_code);
if (gif_file == NULL) {
WTF_OM;
free(gif);
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
// Buffer
buffer = malloc(gif_file->SWidth * gif_file->SHeight * sizeof(RGBA));
shown_buffer = malloc(gif_file->SWidth * gif_file->SHeight * sizeof(RGBA));
if (buffer == NULL || shown_buffer == NULL) {
WTF_OM;
free(buffer);
free(shown_buffer);
DGifCloseFile(gif_file, &error_code);
free(gif);
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
if (partially) {
// Glance
if (DGifGlance(gif_file) != GIF_OK) {
LOGE(MSG("GIF error code %d"), error_code);
DGifCloseFile(gif_file, &error_code);
free(buffer);
free(gif);
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
// Frame info
frame_info_array = (GIF_FRAME_INFO*) malloc(sizeof(GIF_FRAME_INFO));
if (frame_info_array == NULL) {
WTF_OM;
DGifCloseFile(gif_file, &error_code);
free(buffer);
free(gif);
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
// Read gcb
read_gcb(gif_file, 0, frame_info_array);
gif->partially = true;
gif->patch_head_input_stream = patch_head_input_stream;
} else {
// Slurp
if (DGifSlurp(gif_file) == GIF_ERROR) {
fix_gif_file(gif_file);
}
if (gif_file->ImageCount <= 0) {
LOGE(MSG("No frame"));
DGifCloseFile(gif_file, &error_code);
free(buffer);
free(gif);
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
// Frame info
frame_info_array = (GIF_FRAME_INFO*) malloc(gif_file->ImageCount * sizeof(GIF_FRAME_INFO));
if (frame_info_array == NULL) {
WTF_OM;
DGifCloseFile(gif_file, &error_code);
free(buffer);
free(gif);
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
// Read gcb
for (i = 0; i < gif_file->ImageCount; i++) {
read_gcb(gif_file, i, frame_info_array + i);
}
// Generate prepare
generate_prepare(frame_info_array, gif_file->ImageCount);
// Close input stream
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
gif->partially = false;
gif->patch_head_input_stream = NULL;
}
gif->gif_file = gif_file;
gif->frame_info_array = frame_info_array;
gif->buffer = buffer;
gif->buffer_index = -1;
gif->backup = NULL;
gif->shown_buffer = shown_buffer;
GIF_advance(gif);
return gif;
}
bool GIF_complete(JNIEnv* env, GIF* gif)
{
int i;
if (!gif->partially) {
return true;
}
if (gif->gif_file == NULL || gif->patch_head_input_stream == NULL) {
LOGE(MSG("Some stuff is NULL"));
return false;
}
if (DGifSlurp(gif->gif_file) == GIF_ERROR) {
fix_gif_file(gif->gif_file);
}
// Close input stream
close_patch_head_input_stream(env, gif->patch_head_input_stream);
destroy_patch_head_input_stream(env, &gif->patch_head_input_stream);
gif->patch_head_input_stream = NULL;
gif->partially = false;
gif->frame_info_array = (GIF_FRAME_INFO*) realloc(gif->frame_info_array,
gif->gif_file->ImageCount * sizeof(GIF_FRAME_INFO));
if (gif->frame_info_array == NULL) {
WTF_OM;
return false;
}
// Read gcb
for (i = 0; i < gif->gif_file->ImageCount; i++) {
read_gcb(gif->gif_file, i, gif->frame_info_array + i);
}
// Generate prepare
generate_prepare(gif->frame_info_array, gif->gif_file->ImageCount);
return true;
}
bool GIF_is_completed(GIF* gif)
{
return !gif->partially;
}
void* GIF_get_pixels(GIF* gif)
{
if (!gif->partially && GIF_get_frame_count(gif) == 1) {
return gif->buffer;
} else {
return NULL;
}
}
int GIF_get_width(GIF* gif)
{
return gif->gif_file->SWidth;
}
int GIF_get_height(GIF* gif)
{
return gif->gif_file->SHeight;
}
int GIF_get_byte_count(GIF* gif)
{
int i;
SavedImage saved_image;
int size = 0;
// Add buffer size
if (gif->buffer != NULL) {
size += gif->gif_file->SWidth * gif->gif_file->SHeight * 4;
}
// Add backup size
if (gif->backup != NULL) {
size += gif->gif_file->SWidth * gif->gif_file->SHeight * 4;
}
// Add shown buffer size
if (gif->shown_buffer != NULL) {
size += gif->gif_file->SWidth * gif->gif_file->SHeight * 4;
}
// Add frames size
if (gif->gif_file->SavedImages != NULL) {
for (i = 0; i < gif->gif_file->ImageCount; i++) {
saved_image = gif->gif_file->SavedImages[i];
if (saved_image.RasterBits != NULL) {
size += saved_image.ImageDesc.Width * saved_image.ImageDesc.Height * 1;
}
}
}
return size;
}
void GIF_render(GIF* gif, int src_x, int src_y,
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height, bool fill_blank, int default_color)
{
copy_pixels(gif->shown_buffer, gif->gif_file->SWidth, gif->gif_file->SHeight, src_x, src_y,
dst, dst_w, dst_h, dst_x, dst_y,
width, height, fill_blank, default_color);
}
static void backup(GIF* gif)
{
if (gif->backup == NULL) {
gif->backup = (unsigned char*) malloc((size_t) (gif->gif_file->SWidth * gif->gif_file->SHeight * 4));
if (gif->backup == NULL) {
WTF_OM;
return;
}
}
memcpy(gif->backup, gif->buffer, (size_t) (gif->gif_file->SWidth * gif->gif_file->SHeight * 4));
}
static void restore(GIF* gif)
{
if (gif->backup == NULL) {
LOGE(MSG("Backup is NULL"));
} else {
memcpy(gif->buffer, gif->backup, (size_t) (gif->gif_file->SWidth * gif->gif_file->SHeight * 4));
}
}
static void switch_buffer_backup(GIF* gif)
{
unsigned char* temp;
if (gif->backup == NULL) {
backup(gif);
} else {
temp = gif->buffer;
gif->buffer = gif->backup;
gif->backup = temp;
}
}
static void clear(RGBA* pixels, int num, RGBA color) {
RGBA* ptr = pixels;
int i;
for (i = 0; i < num; i++) {
*(ptr++) = color;
}
}
static bool get_color_from_table(const ColorMapObject* cmap, int index,
RGBA* color) {
if (cmap == NULL || index < 0 || index >= cmap->ColorCount) {
return false;
} else {
GifColorType gct = cmap->Colors[index];
color->red = gct.Red;
color->green = gct.Green;
color->blue = gct.Blue;
color->alpha = 0xff;
return true;
}
}
static void clear_bg(GifFileType* gif_file, void* pixels)
{
RGBA color;
if (!get_color_from_table(gif_file->SColorMap, gif_file->SBackGroundColor, &color)) {
color.red = 0x00;
color.green = 0x00;
color.blue = 0x00;
color.alpha = 0x00;
}
clear(pixels, gif_file->SWidth * gif_file->SHeight, color);
}
static void copy_line(GifByteType* src, RGBA* dst,
const ColorMapObject* cmap, int tran, int len)
{
for (; len > 0; len--, src++, dst++) {
int index = *src;
if (tran == -1 || index != tran) {
get_color_from_table(cmap, index, dst);
}
}
}
static void blend(GifFileType* gif_file, int index, void* pixels, int tran)
{
int width = gif_file->SWidth;
int height = gif_file->SHeight;
SavedImage cur = gif_file->SavedImages[index];
GifImageDesc desc = cur.ImageDesc;
int copy_width = MIN(width - desc.Left, desc.Width);
int copy_height = MIN(height - desc.Top, desc.Height);
ColorMapObject *cmap = desc.ColorMap;
GifByteType* src = cur.RasterBits;
RGBA* dst = pixels;
GifByteType* src_ptr;
RGBA* dst_ptr;
size_t len;
int i;
if (cmap == NULL) {
cmap = gif_file->SColorMap;
}
if (cmap != NULL) {
for (i = 0; i < copy_height; i++) {
src_ptr = src + (i * desc.Width);
dst_ptr = dst + ((desc.Top + i) * width + desc.Left);
len = (size_t) copy_width;
copy_line(src_ptr, dst_ptr, cmap, tran, len);
}
} else {
LOGW(MSG("Can't find color map"));
}
}
void GIF_advance(GIF* gif)
{
int index;
GIF_FRAME_INFO frame_info;
index = (gif->buffer_index + 1) % gif->gif_file->ImageCount;
if (index != 0 && gif->partially) {
LOGE(MSG("The png is only decoded partially. Only the first frame can be shown."));
return;
}
if (gif->frame_info_array == NULL) {
frame_info.tran = -1;
frame_info.disposal = DISPOSE_DO_NOT;
frame_info.delay = 0;
frame_info.prepare = IMAGE_GIF_PREPARE_BACKGROUND;
} else {
memcpy(&frame_info, gif->frame_info_array + index, sizeof(GIF_FRAME_INFO));
}
if (frame_info.disposal == DISPOSE_PREVIOUS && frame_info.prepare == IMAGE_GIF_PREPARE_USE_BACKUP) {
switch_buffer_backup(gif);
} else {
// Backup
if (frame_info.disposal == DISPOSE_PREVIOUS) {
backup(gif);
}
// Prepare
switch (frame_info.prepare) {
case IMAGE_GIF_PREPARE_NONE:
// Do nothing
break;
default:
case IMAGE_GIF_PREPARE_BACKGROUND:
// Set bg
clear_bg(gif->gif_file, gif->buffer);
break;
case IMAGE_GIF_PREPARE_USE_BACKUP:
restore(gif);
break;
}
}
blend(gif->gif_file, index, gif->buffer, frame_info.tran);
// Copy to shown buffer
memcpy(gif->shown_buffer, gif->buffer, (size_t) (gif->gif_file->SWidth * gif->gif_file->SHeight * 4));
gif->buffer_index = index;
}
int GIF_get_delay(GIF* gif)
{
if (gif->frame_info_array == NULL) {
return 0;
} else {
return gif->frame_info_array[gif->buffer_index].delay;
}
}
int GIF_get_frame_count(GIF* gif)
{
return gif->gif_file->ImageCount;
}
bool GIF_is_opaque(GIF* gif)
{
return gif->frame_info_array->tran < 0;
}
void GIF_recycle(JNIEnv* env, GIF* gif)
{
DGifCloseFile(gif->gif_file, &error_code);
gif->gif_file = NULL;
free(gif->frame_info_array);
gif->frame_info_array = NULL;
free(gif->buffer);
gif->buffer = NULL;
free(gif->backup);
gif->backup = NULL;
free(gif->shown_buffer);
gif->shown_buffer = NULL;
if (gif->patch_head_input_stream != NULL) {
close_patch_head_input_stream(env, gif->patch_head_input_stream);
destroy_patch_head_input_stream(env, &gif->patch_head_input_stream);
gif->patch_head_input_stream = NULL;
}
}
#endif // IMAGE_SUPPORT_GIF
@@ -1,84 +0,0 @@
/*
* Copyright 2015 Hippo Seven
*
* 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.
*/
//
// Created by Hippo on 12/27/2015.
//
#ifndef IMAGE_IMAGE_GIF_H
#define IMAGE_IMAGE_GIF_H
#include "config.h"
#ifdef IMAGE_SUPPORT_GIF
#include <stdbool.h>
#include "patch_head_input_stream.h"
#include "gif_lib.h"
#include "../utils.h"
#define IMAGE_GIF_DECODER_DESCRIPTION ("giflib " MAKESTRING(STRINGIZE, GIFLIB_MAJOR) "." \
MAKESTRING(STRINGIZE, GIFLIB_MINOR) "." \
MAKESTRING(STRINGIZE, GIFLIB_RELEASE))
#define IMAGE_GIF_MAGIC_NUMBER_0 0x47
#define IMAGE_GIF_MAGIC_NUMBER_1 0x49
#define IMAGE_GIF_PREPARE_UNKNOWN 0x00
#define IMAGE_GIF_PREPARE_NONE 0x01
#define IMAGE_GIF_PREPARE_BACKGROUND 0x02
#define IMAGE_GIF_PREPARE_USE_BACKUP 0x03
typedef struct
{
int tran;
int disposal;
int delay;
int prepare;
} GIF_FRAME_INFO;
typedef struct
{
GifFileType* gif_file;
GIF_FRAME_INFO* frame_info_array;
void* buffer;
int buffer_index;
void* backup;
bool partially;
// Use extra buffer to avoid blink
void* shown_buffer;
PatchHeadInputStream* patch_head_input_stream;
} GIF;
void* GIF_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially);
bool GIF_complete(JNIEnv* env, GIF* gif);
bool GIF_is_completed(GIF* gif);
void* GIF_get_pixels(GIF* gif);
int GIF_get_width(GIF* gif);
int GIF_get_height(GIF* gif);
int GIF_get_byte_count(GIF* gif);
void GIF_render(GIF* gif, int src_x, int src_y,
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height, bool fill_blank, int default_color);
void GIF_advance(GIF* gif);
int GIF_get_delay(GIF* gif);
int GIF_get_frame_count(GIF* gif);
bool GIF_is_opaque(GIF* gif);
void GIF_recycle(JNIEnv* env, GIF* gif);
#endif // IMAGE_SUPPORT_GIF
#endif // IMAGE_IMAGE_GIF_H
@@ -1,210 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* 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.
*/
//
// Created by Hippo on 1/3/2016.
//
#include "config.h"
#ifdef IMAGE_SUPPORT_JPEG
#include <setjmp.h>
#include <stdlib.h>
#include "image_jpeg.h"
#include "image_utils.h"
#include "java_wrapper.h"
#include "../log.h"
struct my_error_mgr {
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
};
typedef struct my_error_mgr * my_error_ptr;
static char emsg[JMSG_LENGTH_MAX];
static void my_error_exit(j_common_ptr cinfo)
{
my_error_ptr myerr = (my_error_ptr) cinfo->err;
(*cinfo->err->format_message)(cinfo, emsg);
longjmp(myerr->setjmp_buffer, 1);
}
static size_t custom_read(void * custom_stuff, unsigned char * buffer, size_t size)
{
bool attach;
size_t read;
PatchHeadInputStream* patch_head_input_stream = custom_stuff;
JNIEnv *env = obtain_env(&attach);
if (env == NULL) {
LOGE(MSG("Can't get JNIEnv"));
return 0;
}
read = read_patch_head_input_stream(env, patch_head_input_stream, buffer, 0, size);
if (attach) {
release_env();
}
return read;
}
void* JPEG_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially)
{
JPEG* jpeg = NULL;
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
unsigned char* buffer = NULL;
size_t stride;
unsigned char* line_buffer_array[3];
int read_lines;
jpeg = (JPEG*) malloc(sizeof(JPEG));
if (jpeg == NULL) {
WTF_OM;
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
// Init
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
if (setjmp(jerr.setjmp_buffer)) {
LOGE(MSG("%s"), emsg);
free(jpeg);
free(buffer);
jpeg_destroy_decompress(&cinfo);
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
jpeg_create_decompress(&cinfo);
jpeg_custom_src(&cinfo, &custom_read, patch_head_input_stream);
jpeg_read_header(&cinfo, TRUE);
// Start decompress
cinfo.out_color_space = JCS_EXT_RGBA;
jpeg_start_decompress(&cinfo);
stride = cinfo.output_components * cinfo.output_width;
buffer = malloc(stride * cinfo.output_height);
if (buffer == NULL) {
free(jpeg);
jpeg_destroy_decompress(&cinfo);
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
// Copy buffer
line_buffer_array[0] = buffer;
line_buffer_array[1] = line_buffer_array[0] + stride;
line_buffer_array[2] = line_buffer_array[1] + stride;
while (cinfo.output_scanline < cinfo.output_height) {
read_lines = jpeg_read_scanlines(&cinfo, line_buffer_array, 3);
line_buffer_array[0] += stride * read_lines;
line_buffer_array[1] = line_buffer_array[0] + stride;
line_buffer_array[2] = line_buffer_array[1] + stride;
}
// Finish decompress
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
// Close stream
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
// Fill jpeg
jpeg->width = cinfo.output_width;
jpeg->height = cinfo.output_height;
jpeg->buffer = buffer;
return jpeg;
}
bool JPEG_complete(JPEG* jpeg)
{
return true;
}
bool JPEG_is_completed(JPEG* jpeg)
{
return true;
}
void* JPEG_get_pixels(JPEG* jpeg)
{
return jpeg->buffer;
}
int JPEG_get_width(JPEG* jpeg)
{
return jpeg->width;
}
int JPEG_get_height(JPEG* jpeg)
{
return jpeg->height;
}
int JPEG_get_byte_count(JPEG* jpeg)
{
return jpeg->width * jpeg->height * 4;
}
void JPEG_render(JPEG* jpeg, int src_x, int src_y,
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height, bool fill_blank, int default_color)
{
copy_pixels(jpeg->buffer, jpeg->width, jpeg->height, src_x, src_y,
dst, dst_w, dst_h, dst_x, dst_y,
width, height, fill_blank, default_color);
}
void JPEG_advance(JPEG* jpeg)
{
}
int JPEG_get_delay(JPEG* jpeg)
{
return 0;
}
int JPEG_get_frame_count(JPEG* jpeg)
{
return 1;
}
bool JPEG_is_opaque(JPEG* jpeg)
{
return true;
}
void JPEG_recycle(JPEG* jpeg)
{
free(jpeg->buffer);
jpeg->buffer = NULL;
free(jpeg);
}
#endif // IMAGE_SUPPORT_JPEG
@@ -1,64 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* 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.
*/
//
// Created by Hippo on 1/3/2016.
//
#ifndef IMAGE_IMAGE_JPEG_H
#define IMAGE_IMAGE_JPEG_H
#include "config.h"
#ifdef IMAGE_SUPPORT_JPEG
#include <stdio.h>
#include <stdbool.h>
#include "jpeglib.h"
#include "patch_head_input_stream.h"
#include "../utils.h"
#define IMAGE_JPEG_DECODER_DESCRIPTION ("libjpeg-turbo " MAKESTRING(STRINGIZE, LIBJPEG_TURBO_VERSION))
#define IMAGE_JPEG_MAGIC_NUMBER_0 0xFF
#define IMAGE_JPEG_MAGIC_NUMBER_1 0xD8
typedef struct
{
unsigned int width;
unsigned int height;
void* buffer;
} JPEG;
void* JPEG_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially);
bool JPEG_complete(JPEG* jpeg);
bool JPEG_is_completed(JPEG* jpeg);
void* JPEG_get_pixels(JPEG* jpeg);
int JPEG_get_width(JPEG* jpeg);
int JPEG_get_height(JPEG* jpeg);
int JPEG_get_byte_count(JPEG* jpeg);
void JPEG_render(JPEG* jpeg, int src_x, int src_y,
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height, bool fill_blank, int default_color);
void JPEG_advance(JPEG* jpeg);
int JPEG_get_delay(JPEG* jpeg);
int JPEG_get_frame_count(JPEG* jpeg);
bool JPEG_is_opaque(JPEG* jpeg);
void JPEG_recycle(JPEG* jpeg);
#endif // IMAGE_SUPPORT_JPEG
#endif // IMAGE_IMAGE_JPEG_H
@@ -1,129 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* 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.
*/
//
// Created by Hippo on 5/3/2016.
//
#include "config.h"
#ifdef IMAGE_SUPPORT_PLAIN
#include <stdlib.h>
#include <string.h>
#include "image_plain.h"
#include "image_utils.h"
#include "../log.h"
void* PLAIN_create(unsigned int width, unsigned int height, const void* data)
{
PLAIN* plain = NULL;
void* buffer = NULL;
size_t length;
plain = (PLAIN*) malloc(sizeof(PLAIN));
if (plain == NULL) {
WTF_OM;
return NULL;
}
length = (size_t) (width * height * 4);
buffer = malloc(length);
if (buffer == NULL) {
WTF_OM;
free(plain);
return NULL;
}
memcpy(buffer, data, length);
plain->width = width;
plain->height = height;
plain->buffer = buffer;
return plain;
}
bool PLAIN_complete(PLAIN* plain)
{
return true;
}
bool PLAIN_is_completed(PLAIN* plain)
{
return true;
}
void* PLAIN_get_pixels(PLAIN* plain)
{
return plain->buffer;
}
int PLAIN_get_width(PLAIN* plain)
{
return plain->width;
}
int PLAIN_get_height(PLAIN* plain)
{
return plain->height;
}
int PLAIN_get_byte_count(PLAIN* plain)
{
return plain->width * plain->height * 4;
}
void PLAIN_render(PLAIN* plain, int src_x, int src_y,
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height, bool fill_blank, int default_color)
{
copy_pixels(plain->buffer, plain->width, plain->height, src_x, src_y,
dst, dst_w, dst_h, dst_x, dst_y,
width, height, fill_blank, default_color);
}
void PLAIN_advance(PLAIN* plain)
{
}
int PLAIN_get_delay(PLAIN* plain)
{
return 0;
}
int PLAIN_get_frame_count(PLAIN* plain)
{
return 1;
}
bool PLAIN_is_opaque(PLAIN* plain)
{
// TODO Check plain all alpha
return false;
}
void PLAIN_recycle(PLAIN* plain)
{
free(plain->buffer);
plain->buffer = NULL;
free(plain);
}
#endif // IMAGE_SUPPORT_PLAIN
@@ -1,56 +0,0 @@
/*
* Copyright 2016 Hippo Seven
*
* 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.
*/
//
// Created by Hippo on 5/3/2016.
//
#ifndef IMAGE_IMAGE_PLAIN_H
#define IMAGE_IMAGE_PLAIN_H
#include "config.h"
#ifdef IMAGE_SUPPORT_PLAIN
#include <stdbool.h>
#include "patch_head_input_stream.h"
typedef struct
{
unsigned int width;
unsigned int height;
void* buffer;
} PLAIN;
void* PLAIN_create(unsigned int width, unsigned int height, const void* data);
bool PLAIN_complete(PLAIN* plain);
bool PLAIN_is_completed(PLAIN* plain);
void* PLAIN_get_pixels(PLAIN* plain);
int PLAIN_get_width(PLAIN* plain);
int PLAIN_get_height(PLAIN* plain);
int PLAIN_get_byte_count(PLAIN* plain);
void PLAIN_render(PLAIN* plain, int src_x, int src_y,
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height, bool fill_blank, int default_color);
void PLAIN_advance(PLAIN* plain);
int PLAIN_get_delay(PLAIN* plain);
int PLAIN_get_frame_count(PLAIN* plain);
bool PLAIN_is_opaque(PLAIN* plain);
void PLAIN_recycle(PLAIN* plain);
#endif // IMAGE_SUPPORT_PLAIN
#endif // IMAGE_IMAGE_PLAIN_H
-642
View File
@@ -1,642 +0,0 @@
/*
* Copyright 2015 Hippo Seven
*
* 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.
*/
//
// Created by Hippo on 12/27/2015.
//
#include "config.h"
#ifdef IMAGE_SUPPORT_PNG
#include <stdlib.h>
#include <string.h>
#include "../log.h"
#include "../utils.h"
#include "image_png.h"
#include "image_utils.h"
#include "java_wrapper.h"
static void user_read_fn(png_structp png_ptr,
png_bytep data, png_size_t length)
{
bool attach;
PatchHeadInputStream* patch_head_input_stream = png_get_io_ptr(png_ptr);
JNIEnv *env = obtain_env(&attach);
if (env == NULL) {
LOGE(MSG("Can't get JNIEnv"));
}
read_patch_head_input_stream(env, patch_head_input_stream, data, 0, length);
if (attach) {
release_env();
}
}
static void user_error_fn(png_structp png_ptr,
png_const_charp error_msg)
{
LOGE(MSG("%s"), error_msg);
}
static void user_warn_fn(png_structp png_ptr,
png_const_charp error_msg)
{
LOGW(MSG("%s"), error_msg);
}
static void free_frame_info_array(PNG_FRAME_INFO* frame_info_array, unsigned int count)
{
int i;
if (frame_info_array == NULL) {
return;
}
for (i = 0; i < count; i++) {
PNG_FRAME_INFO* frame_info = frame_info_array + i;
free(frame_info->buffer);
frame_info->buffer = NULL;
}
free(frame_info_array);
}
static bool read_image(png_structp png_ptr, unsigned char* buffer, unsigned int width, unsigned int height)
{
int i;
unsigned char** line_buffer_ptr_array = (unsigned char**) malloc(height * sizeof(unsigned char*));
if (line_buffer_ptr_array == NULL) {
WTF_OM;
return false;
}
for (i = 0; i < height; i++) {
line_buffer_ptr_array[i] = buffer + (width * i * 4);
}
png_read_image(png_ptr, line_buffer_ptr_array);
free(line_buffer_ptr_array);
return true;
}
static void read_frame(png_structp png_ptr, png_infop info_ptr, PNG_FRAME_INFO* frame_info)
{
unsigned int width;
unsigned int height;
unsigned int offset_x;
unsigned int offset_y;
unsigned short delay_num;
unsigned short delay_den;
unsigned char dop;
unsigned char bop;
unsigned int delay;
unsigned char* buffer = NULL;
png_read_frame_head(png_ptr, info_ptr);
png_get_next_frame_fcTL(png_ptr, info_ptr, &width, &height, &offset_x, &offset_y, &delay_num, &delay_den, &dop, &bop);
delay = 1000u * delay_num / delay_den;
frame_info->width = width;
frame_info->height = height;
frame_info->offset_x = offset_x;
frame_info->offset_y = offset_y;
frame_info->delay = delay;
frame_info->dop = dop;
frame_info->bop = bop;
frame_info->pop = IMAGE_PNG_PREPARE_UNKNOWN;
buffer = (unsigned char*) malloc(width * height * 4);
if (buffer == NULL) {
frame_info->buffer = NULL;
return;
}
read_image(png_ptr, buffer, width, height);
frame_info->buffer = buffer;
}
static void generate_pop(PNG_FRAME_INFO* frame_info_array, int count)
{
unsigned char dop = 0xff;
int i;
for (i = 0; i < count; i++){
PNG_FRAME_INFO* frame_info = frame_info_array + i;
switch (dop) {
case PNG_DISPOSE_OP_NONE:
frame_info->pop = IMAGE_PNG_PREPARE_NONE;
break;
case PNG_DISPOSE_OP_BACKGROUND:
frame_info->pop = IMAGE_PNG_PREPARE_BACKGROUND;
break;
case PNG_DISPOSE_OP_PREVIOUS:
frame_info->pop = IMAGE_PNG_PREPARE_USE_BACKUP;
break;
default:
frame_info->pop = IMAGE_PNG_PREPARE_BACKGROUND;
break;
}
dop = frame_info->dop;
}
}
void* PNG_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially)
{
PNG *png = NULL;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
bool apng;
unsigned int width;
unsigned int height;
int color_type;
int bit_depth;
bool is_opaque;
unsigned char* buffer = NULL;
unsigned int frame_count = 0;
bool hide_first_frame = false;
PNG_FRAME_INFO* frame_info_array = NULL;
int i;
png = (PNG *) malloc(sizeof(PNG));
if (png == NULL) {
WTF_OM;
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, &user_error_fn, &user_warn_fn);
if (png_ptr == NULL) {
free(png);
png = NULL;
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
png_destroy_read_struct(&png_ptr, NULL, NULL);
free(png);
png = NULL;
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
if (setjmp(png_jmpbuf(png_ptr))) {
LOGE(MSG("Error in png decode"));
free_frame_info_array(frame_info_array, frame_count);
frame_info_array = NULL;
free(buffer);
buffer = NULL;
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
free(png);
png = NULL;
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
// Set custom read function
png_set_read_fn(png_ptr, patch_head_input_stream, &user_read_fn);
// Get png info
png_read_info(png_ptr, info_ptr);
// Check apng
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL)) {
apng = true;
} else {
apng = false;
}
// PNG info
width = png_get_image_width(png_ptr, info_ptr);
height = png_get_image_height(png_ptr, info_ptr);
color_type = png_get_color_type(png_ptr, info_ptr);
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
// Create buffer
buffer = (unsigned char*) malloc(width * height * 4);
if (buffer == NULL) {
WTF_OM;
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
free(png);
png = NULL;
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
if (apng) {
// Get frame count
frame_count = png_get_num_frames(png_ptr, info_ptr);
hide_first_frame = png_get_first_frame_is_hidden(png_ptr, info_ptr);
if (hide_first_frame) {
frame_count--;
}
// Create frame info array
frame_info_array = (PNG_FRAME_INFO*) calloc(frame_count, sizeof(PNG_FRAME_INFO));
if (frame_info_array == NULL) {
WTF_OM;
free(buffer);
buffer = NULL;
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
free(png);
png = NULL;
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
return NULL;
}
}
// Configure to ARGB
png_set_expand(png_ptr);
if (bit_depth == 16) {
png_set_scale_16(png_ptr);
}
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
png_set_gray_to_rgb(png_ptr);
}
if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
is_opaque = true;
png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
} else {
is_opaque = false;
}
if (apng) {
if (hide_first_frame) {
// Skip first frame
read_image(png_ptr, buffer, width, height);
}
// Read first frame
read_frame(png_ptr, info_ptr, frame_info_array);
// Fix dop
if (frame_info_array->dop == PNG_DISPOSE_OP_PREVIOUS) {
frame_info_array->dop = PNG_DISPOSE_OP_BACKGROUND;
}
if (!partially || frame_count == 1) {
// Read all frame
for (i = 1; i < frame_count; read_frame(png_ptr, info_ptr, frame_info_array + i++));
// Generate pop
generate_pop(frame_info_array, frame_count);
// End read
png_read_end(png_ptr, info_ptr);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
// Close input stream
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
png->partially = false;
png->png_ptr = NULL;
png->info_ptr = NULL;
png->patch_head_input_stream = NULL;
} else {
png->partially = true;
png->png_ptr = png_ptr;
png->info_ptr = info_ptr;
png->patch_head_input_stream = patch_head_input_stream;
}
// Fill PNG
png->width = width;
png->height = height;
png->is_opaque = is_opaque;
png->buffer = buffer;
png->apng = true;
png->buffer_index = -1;
png->frame_info_array = frame_info_array;
png->frame_count = frame_count;
png->backup = NULL;
// Render first frame
PNG_advance(png);
} else {
read_image(png_ptr, buffer, width, height);
// End read
png_read_end(png_ptr, info_ptr);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
// Close input stream
close_patch_head_input_stream(env, patch_head_input_stream);
destroy_patch_head_input_stream(env, &patch_head_input_stream);
// Fill PNG
png->width = width;
png->height = height;
png->buffer = buffer;
png->apng = false;
png->buffer_index = 0;
png->frame_info_array = NULL;
png->frame_count = 0;
png->backup = NULL;
png->partially = false;
png->png_ptr = NULL;
png->info_ptr = NULL;
png->patch_head_input_stream = NULL;
}
return png;
}
bool PNG_complete(JNIEnv *env, PNG* png)
{
int i;
if (!png->partially) {
return true;
}
if (png->png_ptr == NULL || png->info_ptr == NULL || png->patch_head_input_stream == NULL) {
LOGE(MSG("Some stuff is NULL"));
return false;
}
// Read left frames
for (i = 1; i < png->frame_count; read_frame(png->png_ptr, png->info_ptr, png->frame_info_array + i++));
// Generate pop
generate_pop(png->frame_info_array, png->frame_count);
// End read
png_read_end(png->png_ptr, png->info_ptr);
png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
// Close input stream
close_patch_head_input_stream(env, png->patch_head_input_stream);
destroy_patch_head_input_stream(env, &png->patch_head_input_stream);
// Clean up
png->partially = false;
png->png_ptr = NULL;
png->info_ptr = NULL;
png->patch_head_input_stream = NULL;
return true;
}
bool PNG_is_completed(PNG* png)
{
return !png->partially;
}
void* PNG_get_pixels(PNG* png)
{
if (!png->partially && PNG_get_frame_count(png) == 1) {
return png->buffer;
} else {
return NULL;
}
}
int PNG_get_width(PNG* png)
{
return png->width;
}
int PNG_get_height(PNG* png)
{
return png->height;
}
int PNG_get_byte_count(PNG* png)
{
int i;
PNG_FRAME_INFO* frame_info;
int size = 0;
// Add buffer size
if (png->buffer != NULL) {
size += png->width * png->height * 4;
}
// Add backup size
if (png->backup != NULL) {
size += png->width * png->height * 4;
}
// Add frames size
if (png->frame_info_array != NULL) {
for (i = 0; i < png->frame_count; i++) {
frame_info = png->frame_info_array + i;
// Add frame buffer size
if (frame_info->buffer != NULL) {
size += frame_info->width * frame_info->height * 4;
}
}
}
return size;
}
void PNG_render(PNG* png, int src_x, int src_y,
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height, bool fill_blank, int default_color)
{
copy_pixels(png->buffer, png->width, png->height, src_x, src_y,
dst, dst_w, dst_h, dst_x, dst_y,
width, height, fill_blank, default_color);
}
static void backup(PNG* png)
{
if (png->backup == NULL) {
png->backup = (unsigned char*) malloc(png->width * png->height * 4);
if (png->backup == NULL) {
WTF_OM;
return;
}
}
memcpy(png->backup, png->buffer, png->width * png->height * 4);
}
static void restore(PNG* png)
{
if (png->backup == NULL) {
LOGE(MSG("Backup is NULL"));
} else {
memcpy(png->buffer, png->backup, png->width * png->height * 4);
}
}
static void switch_buffer_backup(PNG* png)
{
unsigned char* temp;
if (png->backup == NULL) {
backup(png);
} else {
temp = png->buffer;
png->buffer = png->backup;
png->backup = temp;
}
}
static void blend_over(unsigned char* dp, const unsigned char* sp, size_t len)
{
unsigned int i;
int u, v, al;
for (i = 0; i < len; i += 4, sp += 4, dp += 4) {
if (sp[3] == 255) {
memcpy(dp, sp, 4);
} else if (sp[3] != 0) {
if (dp[3] != 0) {
u = sp[3] * 255;
v = (255 - sp[3]) * dp[3];
al = u + v;
dp[0] = (unsigned char) ((sp[0] * u + dp[0] * v) / al);
dp[1] = (unsigned char) ((sp[1] * u + dp[1] * v) / al);
dp[2] = (unsigned char) ((sp[2] * u + dp[2] * v) / al);
dp[3] = (unsigned char) (al / 255);
} else {
memcpy(dp, sp, 4);
}
}
}
}
static void blend(unsigned char* src, int src_width, int src_height, int offset_x, int offset_y,
unsigned char* dst, int dst_width, int dst_height, bool blend_op_over)
{
int i;
unsigned char* src_ptr;
unsigned char* dst_ptr;
size_t len;
int copy_width = MIN(dst_width - offset_x, src_width);
int copy_height = MIN(dst_height - offset_y, src_height);
for (i = 0; i < copy_height; i++) {
src_ptr = src + (i * src_width * 4);
dst_ptr = dst + (((offset_y + i) * dst_width + offset_x) * 4);
len = (size_t) (copy_width * 4);
if (blend_op_over) {
blend_over(dst_ptr, src_ptr, len);
} else {
memcpy(dst_ptr, src_ptr, len);
}
}
}
void PNG_advance(PNG* png)
{
int index;
if (!png->apng) {
LOGW(MSG("It is not apng, no need to advance"));
return;
}
index = (png->buffer_index + 1) % png->frame_count;
if (index != 0 && png->partially) {
LOGE(MSG("The png is only decoded partially. Only the first frame can be shown."));
return;
}
PNG_FRAME_INFO* frame_info = png->frame_info_array + index;
if (frame_info->dop == PNG_DISPOSE_OP_PREVIOUS && frame_info->pop == IMAGE_PNG_PREPARE_USE_BACKUP) {
switch_buffer_backup(png);
} else {
// Backup
if (frame_info->dop == PNG_DISPOSE_OP_PREVIOUS) {
backup(png);
}
// Prepare
switch (frame_info->pop) {
case IMAGE_PNG_PREPARE_NONE:
// Do nothing
break;
default:
case IMAGE_PNG_PREPARE_BACKGROUND:
// Set transparent
memset(png->buffer, '\0', png->width * png->height * 4);
break;
case IMAGE_PNG_PREPARE_USE_BACKUP:
restore(png);
break;
}
}
blend(frame_info->buffer, frame_info->width, frame_info->height, frame_info->offset_x, frame_info->offset_y,
png->buffer, png->width, png->height, frame_info->bop == PNG_BLEND_OP_OVER);
png->buffer_index = index;
}
int PNG_get_delay(PNG* png)
{
if (png->apng) {
return png->frame_info_array[png->buffer_index].delay;
} else {
return 0;
}
}
int PNG_get_frame_count(PNG* png)
{
if (png->apng) {
return png->frame_count;
} else {
return 1;
}
}
bool PNG_is_opaque(PNG* png)
{
return png->is_opaque;
}
void PNG_recycle(JNIEnv *env, PNG* png)
{
if (png == NULL) {
return;
}
free(png->buffer);
png->buffer = NULL;
free_frame_info_array(png->frame_info_array, png->frame_count);
png->frame_info_array = NULL;
free(png->backup);
png->backup = NULL;
if (png->png_ptr != NULL && png->info_ptr != NULL) {
png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
}
png->png_ptr = NULL;
png->info_ptr = NULL;
if (png->patch_head_input_stream != NULL) {
close_patch_head_input_stream(env, png->patch_head_input_stream);
destroy_patch_head_input_stream(env, &png->patch_head_input_stream);
png->patch_head_input_stream = NULL;
}
}
#endif // IMAGE_SUPPORT_PNG
@@ -1,89 +0,0 @@
/*
* Copyright 2015 Hippo Seven
*
* 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.
*/
//
// Created by Hippo on 12/27/2015.
//
#ifndef IMAGE_IMAGE_PNG_H
#define IMAGE_IMAGE_PNG_H
#include "config.h"
#ifdef IMAGE_SUPPORT_PNG
#include <stdbool.h>
#include "png.h"
#include "patch_head_input_stream.h"
#define IMAGE_PNG_DECODER_DESCRIPTION ("libpng " PNG_LIBPNG_VER_STRING)
#define IMAGE_PNG_MAGIC_NUMBER_0 0x89
#define IMAGE_PNG_MAGIC_NUMBER_1 0x50
#define IMAGE_PNG_PREPARE_UNKNOWN 0x00
#define IMAGE_PNG_PREPARE_NONE 0x01
#define IMAGE_PNG_PREPARE_BACKGROUND 0x02
#define IMAGE_PNG_PREPARE_USE_BACKUP 0x03
typedef struct {
unsigned char* buffer;
unsigned int width;
unsigned int height;
unsigned int offset_x;
unsigned int offset_y;
unsigned int delay; // ms
unsigned char dop;
unsigned char bop;
unsigned char pop;
} PNG_FRAME_INFO;
typedef struct
{
unsigned int width;
unsigned int height;
bool is_opaque;
unsigned char* buffer;
bool apng;
int buffer_index;
PNG_FRAME_INFO* frame_info_array;
unsigned int frame_count;
unsigned char* backup;
bool partially;
png_structp png_ptr;
png_infop info_ptr;
PatchHeadInputStream* patch_head_input_stream;
} PNG;
void* PNG_decode(JNIEnv* env, PatchHeadInputStream* patch_head_input_stream, bool partially);
bool PNG_complete(JNIEnv* env, PNG* png);
bool PNG_is_completed(PNG* png);
void* PNG_get_pixels(PNG* png);
int PNG_get_width(PNG* png);
int PNG_get_height(PNG* png);
int PNG_get_byte_count(PNG* png);
void PNG_render(PNG* png, int src_x, int src_y,
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height, bool fill_blank, int default_color);
void PNG_advance(PNG* png);
int PNG_get_delay(PNG* png);
int PNG_get_frame_count(PNG* png);
bool PNG_is_opaque(PNG* png);
void PNG_recycle(JNIEnv* env, PNG* png);
#endif // IMAGE_SUPPORT_PNG
#endif // IMAGE_IMAGE_PNG_H
@@ -1,185 +0,0 @@
/*
* Copyright 2015 Hippo Seven
*
* 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.
*/
//
// Created by Hippo on 12/27/2015.
//
#include "image_utils.h"
#include <stdlib.h>
#include <string.h>
#define LITTLE_ENDIAN 0x00
#define BIG_ENDIAN 0x01
static unsigned int endian = ~0U;
static unsigned int get_endian() {
unsigned int x = 1;
if (endian == ~0U) {
if (1 == ((char *)&x)[0]) {
endian = LITTLE_ENDIAN;
} else {
endian = BIG_ENDIAN;
}
}
return endian;
}
static int convert_color(int origin) {
int result;
unsigned char* orPtr = (unsigned char *) &origin;
unsigned char* rePtr = (unsigned char *) &result;
if (get_endian() == BIG_ENDIAN) {
rePtr[0] = orPtr[1];
rePtr[1] = orPtr[2];
rePtr[2] = orPtr[3];
rePtr[3] = orPtr[0];
} else {
rePtr[0] = orPtr[2];
rePtr[1] = orPtr[1];
rePtr[2] = orPtr[0];
rePtr[3] = orPtr[3];
}
return result;
}
static void memset_int(int* dst, int val, size_t size) {
int* maxPtr = dst + size;
int* ptr = dst;
while(ptr < maxPtr)
*ptr++ = val;
}
bool copy_pixels_internal(const void* src, int src_w, int src_h, int src_x, int src_y,
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height, bool fill_blank, int color)
{
int left;
int line;
size_t line_stride;
int src_stride;
int src_pos;
int dst_pos;
size_t dst_blank_length;
// Sanitize
if (src_x < 0) {
width -= src_x;
dst_x -= src_x;
src_x = 0;
}
if (dst_x < 0) {
width -= dst_x;
src_x -= dst_x;
dst_x = 0;
}
if (width <= 0) {
return false;
}
if (src_y < 0) {
height -= src_y;
dst_y -= src_y;
src_y = 0;
}
if (dst_y < 0) {
height -= dst_y;
src_y -= dst_y;
dst_y = 0;
}
if (height <= 0) {
return false;
}
left = src_x + width - src_w;
if (left > 0) {
width -= left;
}
left = dst_x + width - dst_w;
if (left > 0) {
width -= left;
}
if (width <= 0) {
return false;
}
left = src_y + height - src_h;
if (left > 0) {
height -= left;
}
left = dst_y + height - dst_h;
if (left > 0) {
height -= left;
}
if (height <= 0) {
return false;
}
// Init
line_stride = (size_t) (width * 4);
src_stride = src_w * 4;
src_pos = src_y * src_stride + src_x * 4;
dst_pos = 0;
dst_blank_length = (size_t) (dst_y * dst_w + dst_x) * 4;
// Fill start blank if necessary
if (fill_blank) {
memset_int((int *) (dst + dst_pos), color, dst_blank_length / 4);
}
// First line
dst_pos += dst_blank_length;
memcpy(dst + dst_pos, src + src_pos, line_stride);
dst_pos += line_stride;
src_pos += src_stride;
// Other lines
dst_blank_length = (size_t) ((dst_w - width) * 4);
for (line = 1; line < height; line++) {
if (fill_blank) {
memset_int((int *) (dst + dst_pos), color, dst_blank_length / 4);
}
dst_pos += dst_blank_length;
memcpy(dst + dst_pos, src + src_pos, line_stride);
dst_pos += line_stride;
src_pos += src_stride;
}
// Fill left blank if necessary
if (fill_blank) {
memset_int((int *) (dst + dst_pos), color, (size_t) (dst_w * dst_h * 4 - dst_pos) / 4);
}
return true;
}
void copy_pixels(const void* src, int src_w, int src_h, int src_x, int src_y,
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height, bool fill_blank, int default_color)
{
int color = 0;
if (fill_blank) {
color = convert_color(default_color);
}
if (!copy_pixels_internal(src, src_w, src_h, src_x, src_y, dst, dst_w, dst_h, dst_x,
dst_y, width, height, fill_blank, color) && fill_blank) {
memset_int(dst, color, (size_t) (dst_w * dst_h));
}
}
@@ -1,304 +0,0 @@
/*
* Copyright 2015 Hippo Seven
*
* 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.
*/
//
// Created by Hippo on 12/27/2015.
//
#include <stdlib.h>
#include <android/bitmap.h>
#include <GLES2/gl2.h>
#include "java_wrapper.h"
#include "input_stream.h"
#include "image.h"
#include "../log.h"
static JavaVM* jvm;
static void* tile_buffer;
JNIEnv *obtain_env(bool *attach)
{
JNIEnv *env;
switch ((*jvm)->GetEnv(jvm, (void**) &env, JNI_VERSION_1_6)) {
case JNI_EDETACHED:
if ((*jvm)->AttachCurrentThread(jvm, &env, NULL) == JNI_OK) {
*attach = true;
return env;
} else {
return NULL;
}
case JNI_OK:
*attach = false;
return env;
default:
case JNI_EVERSION:
return NULL;
}
}
void release_env()
{
(*jvm)->DetachCurrentThread(jvm);
}
jobject create_image_object(JNIEnv* env, void* ptr, int format, int width, int height)
{
jclass image_clazz;
jmethodID constructor;
image_clazz = (*env)->FindClass(env, "com/hippo/image/Image");
constructor = (*env)->GetMethodID(env, image_clazz, "<init>", "(JIII)V");
if (constructor == 0) {
LOGE(MSG("Can't find Image object constructor"));
return NULL;
} else {
return (*env)->NewObject(env, image_clazz, constructor,
(jlong) (uintptr_t) ptr, (jint) format, (jint) width, (jint) height);
}
}
JNIEXPORT jobject JNICALL
Java_com_hippo_image_Image_nativeDecode(JNIEnv* env,
jclass clazz, jobject is, jboolean partially)
{
InputStream* input_stream;
int format;
void* image;
jobject image_object;
input_stream = create_input_stream(env, is);
if (input_stream == NULL) {
return NULL;
}
image = decode(env, input_stream, partially, &format);
if (image == NULL) {
return NULL;
}
image_object = create_image_object(env, image, format,
get_width(image, format), get_height(image, format));
if (image_object == NULL) {
recycle(env, image, format);
return NULL;
} else {
return image_object;
}
}
JNIEXPORT jobject JNICALL
Java_com_hippo_image_Image_nativeCreate(JNIEnv* env,
jclass clazz, jobject bitmap)
{
#ifdef IMAGE_SUPPORT_PLAIN
AndroidBitmapInfo info;
void *pixels = NULL;
void* image = NULL;
jobject image_object;
AndroidBitmap_getInfo(env, bitmap, &info);
AndroidBitmap_lockPixels(env, bitmap, &pixels);
if (pixels == NULL) {
LOGE(MSG("Can't lock bitmap pixels"));
return NULL;
}
image = create(info.width, info.height, pixels);
AndroidBitmap_unlockPixels(env, bitmap);
if (image == NULL) {
return NULL;
}
image_object = create_image_object(env, image, IMAGE_FORMAT_PLAIN,
info.width, info.height);
if (image_object == NULL) {
recycle(env, image, IMAGE_FORMAT_PLAIN);
return NULL;
} else {
return image_object;
}
#else
return NULL;
#endif
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_image_Image_nativeComplete(JNIEnv* env,
jclass clazz, jlong ptr, jint format)
{
return (jboolean) complete(env, (void*) (intptr_t) ptr, format);
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_image_Image_nativeIsCompleted(JNIEnv* env,
jclass clazz, jlong ptr, jint format)
{
return (jboolean) is_completed((void*) (intptr_t) ptr, format);
}
JNIEXPORT jint JNICALL
Java_com_hippo_image_Image_nativeGetByteCount(JNIEnv* env,
jclass clazz, jlong ptr, jint format)
{
return (jint) get_byte_count((void*) (intptr_t) ptr, format);
}
JNIEXPORT void JNICALL
Java_com_hippo_image_Image_nativeRender(JNIEnv* env,
jclass clazz, jlong ptr, jint format,
jint src_x, jint src_y, jobject dst, jint dst_x, jint dst_y,
jint width, jint height, jboolean fill_blank, jint default_color)
{
AndroidBitmapInfo info;
void *pixels = NULL;
AndroidBitmap_getInfo(env, dst, &info);
AndroidBitmap_lockPixels(env, dst, &pixels);
if (pixels == NULL) {
LOGE(MSG("Can't lock bitmap pixels"));
return;
}
render((void*) (intptr_t) ptr, format, src_x, src_y,
pixels, info.width, info.height, dst_x, dst_y,
width, height, fill_blank, default_color);
AndroidBitmap_unlockPixels(env, dst);
return;
}
JNIEXPORT void JNICALL
Java_com_hippo_image_Image_nativeTexImage(JNIEnv* env,
jclass clazz, jlong ptr, jint format, jboolean init,
jint src_x, jint src_y, jint width, jint height)
{
// Check tile_buffer NULL
if (NULL == tile_buffer) {
return;
}
// Check render size
if (width * height > IMAGE_TILE_MAX_SIZE) {
return;
}
render((void*) (intptr_t) ptr, format, src_x, src_y,
tile_buffer, width, height, 0, 0,
width, height, false, 0);
if (init) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height,
0, GL_RGBA, GL_UNSIGNED_BYTE, tile_buffer);
} else {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height,
GL_RGBA, GL_UNSIGNED_BYTE, tile_buffer);
}
}
JNIEXPORT void JNICALL
Java_com_hippo_image_Image_nativeAdvance(JNIEnv* env,
jclass clazz, jlong ptr, jint format)
{
advance((void*) (intptr_t) ptr, format);
}
JNIEXPORT jint JNICALL
Java_com_hippo_image_Image_nativeGetDelay(JNIEnv* env,
jclass clazz, jlong ptr, jint format)
{
return (jint) get_delay((void*) (intptr_t) ptr, format);
}
JNIEXPORT jint JNICALL
Java_com_hippo_image_Image_nativeFrameCount(JNIEnv* env,
jclass clazz, jlong ptr, jint format)
{
return (jint) get_frame_count((void*) (intptr_t) ptr, format);
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_image_Image_nativeIsOpaque(JNIEnv* env,
jclass clazz, jlong ptr, jint format)
{
return (jboolean) is_opaque((void*) (intptr_t) ptr, format);
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_image_Image_nativeIsGray(JNIEnv* env,
jclass clazz, jlong ptr, jint format, jint error)
{
return (jboolean) is_gray((void*) (intptr_t) ptr, format, error);
}
JNIEXPORT void JNICALL
Java_com_hippo_image_Image_nativeClahe(JNIEnv* env,
jclass clazz, jlong ptr, jint format, jboolean to_gray)
{
clahe((void*) (intptr_t) ptr, format, to_gray);
}
JNIEXPORT void JNICALL
Java_com_hippo_image_Image_nativeRecycle(JNIEnv* env,
jclass clazz, jlong ptr, jint format)
{
recycle(env, (void*) (intptr_t) ptr, format);
}
JNIEXPORT jintArray JNICALL
Java_com_hippo_image_Image_nativeGetSupportedImageFormats(
JNIEnv *env, jclass clazz)
{
int formats[IMAGE_MAX_SUPPORTED_FORMAT_COUNT];
int count = get_supported_formats(formats);
jintArray array = (*env)->NewIntArray(env, count);
if (array == NULL) {
return NULL;
}
(*env)->SetIntArrayRegion(env, array, 0, count, formats);
return array;
}
JNIEXPORT jstring JNICALL
Java_com_hippo_image_Image_nativeGetDecoderDescription(
JNIEnv *env, jclass clazz, jint format)
{
const char *description = get_decoder_description(format);
if (description == NULL) {
return NULL;
} else {
return (*env)->NewStringUTF(env, description);
}
}
bool image_onLoad(JavaVM *vm)
{
jvm = vm;
tile_buffer = malloc(IMAGE_TILE_MAX_SIZE * 4);
return true;
}
JNIEXPORT void JNICALL
JNI_OnUnload(JavaVM *vm, void *reserved)
{
free(tile_buffer);
tile_buffer = NULL;
}
+107
View File
@@ -0,0 +1,107 @@
/*
* Copyright 2015 Hippo Seven
*
* 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_utils.h"
#include <stdlib.h>
#include <string.h>
bool copy_pixels(const void *src, int src_w, int src_h, int src_x, int src_y,
void *dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height) {
int left;
int line;
size_t line_stride;
int src_stride;
int src_pos;
int dst_pos;
size_t dst_blank_length;
// Sanitize
if (src_x < 0) {
width -= src_x;
dst_x -= src_x;
src_x = 0;
}
if (dst_x < 0) {
width -= dst_x;
src_x -= dst_x;
dst_x = 0;
}
if (width <= 0) {
return false;
}
if (src_y < 0) {
height -= src_y;
dst_y -= src_y;
src_y = 0;
}
if (dst_y < 0) {
height -= dst_y;
src_y -= dst_y;
dst_y = 0;
}
if (height <= 0) {
return false;
}
left = src_x + width - src_w;
if (left > 0) {
width -= left;
}
left = dst_x + width - dst_w;
if (left > 0) {
width -= left;
}
if (width <= 0) {
return false;
}
left = src_y + height - src_h;
if (left > 0) {
height -= left;
}
left = dst_y + height - dst_h;
if (left > 0) {
height -= left;
}
if (height <= 0) {
return false;
}
// Init
line_stride = (size_t) (width * 4);
src_stride = src_w * 4;
src_pos = src_y * src_stride + src_x * 4;
dst_pos = 0;
dst_blank_length = (size_t) (dst_y * dst_w + dst_x) * 4;
// First line
dst_pos += dst_blank_length;
memcpy(dst + dst_pos, src + src_pos, line_stride);
dst_pos += line_stride;
src_pos += src_stride;
// Other lines
dst_blank_length = (size_t) ((dst_w - width) * 4);
for (line = 1; line < height; line++) {
dst_pos += dst_blank_length;
memcpy(dst + dst_pos, src + src_pos, line_stride);
dst_pos += line_stride;
src_pos += src_stride;
}
return true;
}
@@ -17,17 +17,13 @@
* EhViewer. If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by Hippo on 12/27/2015.
//
#ifndef IMAGE_IMAGE_UTILS_H
#define IMAGE_IMAGE_UTILS_H
#include <stdbool.h>
void copy_pixels(const void* src, int src_w, int src_h, int src_x, int src_y,
void* dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height, bool fill_blank, int default_color);
bool copy_pixels(const void *src, int src_w, int src_h, int src_x, int src_y,
void *dst, int dst_w, int dst_h, int dst_x, int dst_y,
int width, int height);
#endif //IMAGE_IMAGE_UTILS_H
#endif /* IMAGE_IMAGE_UTILS_H */
+148
View File
@@ -0,0 +1,148 @@
/*
* Copyright 2015 Hippo Seven
*
* 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 <stdlib.h>
#include <android/bitmap.h>
#include <GLES2/gl2.h>
#include <jni.h>
#include "java_wrapper.h"
#include "image.h"
#include "log.h"
static void *tile_buffer = NULL;
static int jniGetFDFromFileDescriptor(JNIEnv *env, jobject fileDescriptor) {
jint fd = -1;
jclass fdClass = (*env)->FindClass(env, "java/io/FileDescriptor");
if (fdClass != NULL) {
jfieldID fdClassDescriptorFieldID = (*env)->GetFieldID(env, fdClass, "descriptor", "I");
if (fdClassDescriptorFieldID != NULL && fileDescriptor != NULL) {
fd = (*env)->GetIntField(env, fileDescriptor, fdClassDescriptorFieldID);
}
}
return fd;
}
jobject create_image_object(JNIEnv *env, void *ptr, int format, int width, int height) {
jclass image_clazz;
jmethodID constructor;
image_clazz = (*env)->FindClass(env, "com/hippo/image/Image");
constructor = (*env)->GetMethodID(env, image_clazz, "<init>", "(JIII)V");
return (*env)->NewObject(env, image_clazz, constructor,
(jlong) (uintptr_t) ptr, (jint) format, (jint) width,
(jint) height);
}
JNIEXPORT jobject JNICALL
Java_com_hippo_image_Image_nativeDecodeFdInt(JNIEnv *env, jclass clazz, jint fd) {
IMAGE *image = createFromFd(fd);
return create_image_object(env, image, image->isAnimated, image->width, image->height);
}
JNIEXPORT jobject JNICALL
Java_com_hippo_image_Image_nativeDecode(JNIEnv *env, jclass clazz, jobject fd) {
return Java_com_hippo_image_Image_nativeDecodeFdInt(env, clazz,
jniGetFDFromFileDescriptor(env, fd));
}
JNIEXPORT jobject JNICALL
Java_com_hippo_image_Image_nativeCreate(JNIEnv *env, jclass clazz, jobject bitmap) {
AndroidBitmapInfo info;
void *pixels = NULL;
void *image = NULL;
AndroidBitmap_getInfo(env, bitmap, &info);
AndroidBitmap_lockPixels(env, bitmap, &pixels);
image = create(info.width, info.height, pixels);
AndroidBitmap_unlockPixels(env, bitmap);
return create_image_object(env, image, 0, info.width, info.height);
}
JNIEXPORT void JNICALL
Java_com_hippo_image_Image_nativeRender(JNIEnv *env, jclass clazz, jlong ptr, jint src_x,
jint src_y, jobject dst, jint dst_x, jint dst_y, jint width,
jint height) {
AndroidBitmapInfo info;
void *pixels = NULL;
AndroidBitmap_getInfo(env, dst, &info);
AndroidBitmap_lockPixels(env, dst, &pixels);
render((IMAGE *) ptr, src_x, src_y, pixels, info.width, info.height, dst_x, dst_y, width,
height);
AndroidBitmap_unlockPixels(env, dst);
}
JNIEXPORT void JNICALL
Java_com_hippo_image_Image_nativeTexImage(JNIEnv *env, jclass clazz, jlong ptr, jboolean init,
jint src_x, jint src_y, jint width, jint height) {
if (width * height > IMAGE_TILE_MAX_SIZE)
return;
render((IMAGE *) ptr, src_x, src_y, tile_buffer, width, height, 0, 0, width, height);
if (init) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
tile_buffer);
} else {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE,
tile_buffer);
}
}
JNIEXPORT void JNICALL
Java_com_hippo_image_Image_nativeAdvance(JNIEnv *env, jclass clazz, jlong ptr) {
advance((IMAGE *) ptr);
}
JNIEXPORT jint JNICALL
Java_com_hippo_image_Image_nativeGetDelay(JNIEnv *env, jclass clazz, jlong ptr) {
return ((IMAGE *) ptr)->delay;
}
JNIEXPORT jboolean JNICALL
Java_com_hippo_image_Image_nativeIsOpaque(JNIEnv *env, jclass clazz, jlong ptr) {
return ((IMAGE *) ptr)->alpha;
}
JNIEXPORT void JNICALL
Java_com_hippo_image_Image_nativeRecycle(JNIEnv *env, jclass clazz, jlong ptr) {
recycle((IMAGE *) ptr);
}
bool image_onLoad() {
tile_buffer = malloc(IMAGE_TILE_MAX_SIZE * 4);
return tile_buffer != NULL;
}
void image_onUnload() {
free(tile_buffer);
tile_buffer = NULL;
}
JNIEXPORT jobject JNICALL
Java_com_hippo_image_Image_nativeDecodeAddr(JNIEnv *env, jclass clazz, jlong addr) {
Memarea *memarea = (Memarea *) addr;
IMAGE *image = createFromAddr(memarea->buffer, memarea->size);
free(memarea);
return create_image_object(env, image, image->isAnimated, image->width, image->height);
}
@@ -17,22 +17,20 @@
* EhViewer. If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by Hippo on 12/28/2015.
//
#ifndef IMAGE_JAVA_WRAPPER_H
#define IMAGE_JAVA_WRAPPER_H
#include <jni.h>
#include <stdbool.h>
#define IMAGE_TILE_MAX_SIZE (512 * 512)
bool image_onLoad(JavaVM *vm);
typedef struct Memarea {
void *buffer;
long size;
} Memarea;
JNIEnv *obtain_env(bool *attach);
bool image_onLoad();
void release_env();
void image_onUnload();
#endif // IMAGE_JAVA_WRAPPER_H
#endif /* IMAGE_JAVA_WRAPPER_H */
@@ -1,211 +0,0 @@
# Copyright 2018 Hippo Seven
#
# 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.
cmake_minimum_required(VERSION 3.4.1)
project(jpeg C)
set(LIBJPEG_TURBO_SOURCES
libjpeg-turbo/jaricom.c
libjpeg-turbo/jcomapi.c
libjpeg-turbo/jdapimin.c
libjpeg-turbo/jdapistd.c
libjpeg-turbo/jdarith.c
libjpeg-turbo/jdatasrc.c
libjpeg-turbo/jdatasrc-tj.c
libjpeg-turbo/jdcoefct.c
libjpeg-turbo/jdcolor.c
libjpeg-turbo/jddctmgr.c
libjpeg-turbo/jdhuff.c
libjpeg-turbo/jdinput.c
libjpeg-turbo/jdmainct.c
libjpeg-turbo/jdmarker.c
libjpeg-turbo/jdmaster.c
libjpeg-turbo/jdmerge.c
libjpeg-turbo/jdphuff.c
libjpeg-turbo/jdpostct.c
libjpeg-turbo/jdsample.c
libjpeg-turbo/jdtrans.c
libjpeg-turbo/jerror.c
libjpeg-turbo/jfdctflt.c
libjpeg-turbo/jfdctfst.c
libjpeg-turbo/jfdctint.c
libjpeg-turbo/jidctflt.c
libjpeg-turbo/jidctfst.c
libjpeg-turbo/jidctint.c
libjpeg-turbo/jidctred.c
libjpeg-turbo/jmemmgr.c
libjpeg-turbo/jmemnobs.c
libjpeg-turbo/jquant1.c
libjpeg-turbo/jquant2.c
libjpeg-turbo/jutils.c
)
set(LIBJPEG_TURBO_INCLUDES
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo
)
if (${ANDROID_ABI} STREQUAL "armeabi-v7a")
enable_language(ASM)
add_definitions(-DNEON_INTRINSICS)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/arm/jccolor-neon.c
libjpeg-turbo/simd/arm/jidctred-neon.c
libjpeg-turbo/simd/arm/jquanti-neon.c
libjpeg-turbo/simd/arm/jidctint-neon.c
libjpeg-turbo/simd/arm/jidctfst-neon.c
libjpeg-turbo/simd/arm/jfdctint-neon.c
libjpeg-turbo/simd/arm/jfdctfst-neon.c
libjpeg-turbo/simd/arm/jdsample-neon.c
libjpeg-turbo/simd/arm/jdmerge-neon.c
libjpeg-turbo/simd/arm/jdcolor-neon.c
libjpeg-turbo/simd/arm/jcsample-neon.c
libjpeg-turbo/simd/arm/jcphuff-neon.c
libjpeg-turbo/simd/arm/jcgray-neon.c
libjpeg-turbo/simd/arm/aarch32/jchuff-neon.c
libjpeg-turbo/simd/arm/aarch32/jsimd.c
)
elseif (${ANDROID_ABI} STREQUAL "arm64-v8a")
enable_language(ASM)
add_definitions(-DNEON_INTRINSICS)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/arm/jccolor-neon.c
libjpeg-turbo/simd/arm/jidctred-neon.c
libjpeg-turbo/simd/arm/jquanti-neon.c
libjpeg-turbo/simd/arm/jidctint-neon.c
libjpeg-turbo/simd/arm/jidctfst-neon.c
libjpeg-turbo/simd/arm/jfdctint-neon.c
libjpeg-turbo/simd/arm/jfdctfst-neon.c
libjpeg-turbo/simd/arm/jdsample-neon.c
libjpeg-turbo/simd/arm/jdmerge-neon.c
libjpeg-turbo/simd/arm/jdcolor-neon.c
libjpeg-turbo/simd/arm/jcsample-neon.c
libjpeg-turbo/simd/arm/jcphuff-neon.c
libjpeg-turbo/simd/arm/jcgray-neon.c
libjpeg-turbo/simd/arm/aarch64/jchuff-neon.c
libjpeg-turbo/simd/arm/aarch64/jsimd.c
)
elseif (${ANDROID_ABI} STREQUAL "x86")
enable_language(ASM_NASM)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/i386/jsimd.c
libjpeg-turbo/simd/i386/jsimdcpu.asm
libjpeg-turbo/simd/i386/jfdctflt-3dn.asm
libjpeg-turbo/simd/i386/jidctflt-3dn.asm
libjpeg-turbo/simd/i386/jquant-3dn.asm
libjpeg-turbo/simd/i386/jccolor-mmx.asm
libjpeg-turbo/simd/i386/jcgray-mmx.asm
libjpeg-turbo/simd/i386/jcsample-mmx.asm
libjpeg-turbo/simd/i386/jdcolor-mmx.asm
libjpeg-turbo/simd/i386/jdmerge-mmx.asm
libjpeg-turbo/simd/i386/jdsample-mmx.asm
libjpeg-turbo/simd/i386/jfdctfst-mmx.asm
libjpeg-turbo/simd/i386/jfdctint-mmx.asm
libjpeg-turbo/simd/i386/jidctfst-mmx.asm
libjpeg-turbo/simd/i386/jidctint-mmx.asm
libjpeg-turbo/simd/i386/jidctred-mmx.asm
libjpeg-turbo/simd/i386/jquant-mmx.asm
libjpeg-turbo/simd/i386/jfdctflt-sse.asm
libjpeg-turbo/simd/i386/jidctflt-sse.asm
libjpeg-turbo/simd/i386/jquant-sse.asm
libjpeg-turbo/simd/i386/jccolor-sse2.asm
libjpeg-turbo/simd/i386/jcgray-sse2.asm
libjpeg-turbo/simd/i386/jchuff-sse2.asm
libjpeg-turbo/simd/i386/jcphuff-sse2.asm
libjpeg-turbo/simd/i386/jcsample-sse2.asm
libjpeg-turbo/simd/i386/jdcolor-sse2.asm
libjpeg-turbo/simd/i386/jdmerge-sse2.asm
libjpeg-turbo/simd/i386/jdsample-sse2.asm
libjpeg-turbo/simd/i386/jfdctfst-sse2.asm
libjpeg-turbo/simd/i386/jfdctint-sse2.asm
libjpeg-turbo/simd/i386/jidctflt-sse2.asm
libjpeg-turbo/simd/i386/jidctfst-sse2.asm
libjpeg-turbo/simd/i386/jidctint-sse2.asm
libjpeg-turbo/simd/i386/jidctred-sse2.asm
libjpeg-turbo/simd/i386/jquantf-sse2.asm
libjpeg-turbo/simd/i386/jquanti-sse2.asm
libjpeg-turbo/simd/i386/jccolor-avx2.asm
libjpeg-turbo/simd/i386/jcgray-avx2.asm
libjpeg-turbo/simd/i386/jcsample-avx2.asm
libjpeg-turbo/simd/i386/jdcolor-avx2.asm
libjpeg-turbo/simd/i386/jdmerge-avx2.asm
libjpeg-turbo/simd/i386/jdsample-avx2.asm
libjpeg-turbo/simd/i386/jfdctint-avx2.asm
libjpeg-turbo/simd/i386/jidctint-avx2.asm
libjpeg-turbo/simd/i386/jquanti-avx2.asm
)
set(CMAKE_ASM_NASM_FLAGS "-DELF -DPIC -I${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo/simd/nasm")
elseif (${ANDROID_ABI} STREQUAL "x86_64")
enable_language(ASM_NASM)
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/simd/x86_64/jsimd.c
libjpeg-turbo/simd/x86_64/jsimdcpu.asm
libjpeg-turbo/simd/x86_64/jfdctflt-sse.asm
libjpeg-turbo/simd/x86_64/jccolor-sse2.asm
libjpeg-turbo/simd/x86_64/jcgray-sse2.asm
libjpeg-turbo/simd/x86_64/jchuff-sse2.asm
libjpeg-turbo/simd/x86_64/jcphuff-sse2.asm
libjpeg-turbo/simd/x86_64/jcsample-sse2.asm
libjpeg-turbo/simd/x86_64/jdcolor-sse2.asm
libjpeg-turbo/simd/x86_64/jdmerge-sse2.asm
libjpeg-turbo/simd/x86_64/jdsample-sse2.asm
libjpeg-turbo/simd/x86_64/jfdctfst-sse2.asm
libjpeg-turbo/simd/x86_64/jfdctint-sse2.asm
libjpeg-turbo/simd/x86_64/jidctflt-sse2.asm
libjpeg-turbo/simd/x86_64/jidctfst-sse2.asm
libjpeg-turbo/simd/x86_64/jidctint-sse2.asm
libjpeg-turbo/simd/x86_64/jidctred-sse2.asm
libjpeg-turbo/simd/x86_64/jquantf-sse2.asm
libjpeg-turbo/simd/x86_64/jquanti-sse2.asm
libjpeg-turbo/simd/x86_64/jccolor-avx2.asm
libjpeg-turbo/simd/x86_64/jcgray-avx2.asm
libjpeg-turbo/simd/x86_64/jcsample-avx2.asm
libjpeg-turbo/simd/x86_64/jdcolor-avx2.asm
libjpeg-turbo/simd/x86_64/jdmerge-avx2.asm
libjpeg-turbo/simd/x86_64/jdsample-avx2.asm
libjpeg-turbo/simd/x86_64/jfdctint-avx2.asm
libjpeg-turbo/simd/x86_64/jidctint-avx2.asm
libjpeg-turbo/simd/x86_64/jquanti-avx2.asm
)
set(CMAKE_ASM_NASM_FLAGS "-D__x86_64__ -DELF -DPIC -I${CMAKE_CURRENT_SOURCE_DIR}/libjpeg-turbo/simd/nasm")
else ()
set(LIBJPEG_TURBO_SOURCES
${LIBJPEG_TURBO_SOURCES}
libjpeg-turbo/jsimd_none.c
)
endif ()
add_library(jpeg STATIC ${LIBJPEG_TURBO_SOURCES})
target_include_directories(jpeg PUBLIC ${LIBJPEG_TURBO_INCLUDES})
@@ -1,37 +0,0 @@
/* Version ID for the JPEG library.
* Might be useful for tests like "#if JPEG_LIB_VERSION >= 60".
*/
#define JPEG_LIB_VERSION 62
/* libjpeg-turbo version */
#define LIBJPEG_TURBO_VERSION 2.1.3
/* libjpeg-turbo version in integer form */
#define LIBJPEG_TURBO_VERSION_NUMBER 2001003
/* Support arithmetic encoding */
#define C_ARITH_CODING_SUPPORTED 1
/* Support arithmetic decoding */
#define D_ARITH_CODING_SUPPORTED 1
/* Support in-memory source/destination managers */
#define MEM_SRCDST_SUPPORTED 1
/* Use accelerated SIMD routines. */
#define WITH_SIMD 1
/*
* Define BITS_IN_JSAMPLE as either
* 8 for 8-bit sample values (the usual setting)
* 12 for 12-bit sample values
* Only 8 and 12 are legal data precisions for lossy JPEG according to the
* JPEG standard, and the IJG code does not support anything else!
* We do not support run-time selection of data precision, sorry.
*/
#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */
/* Define if your (broken) compiler shifts signed values as if they were
unsigned. */
/* #undef RIGHT_SHIFT_IS_UNSIGNED */
@@ -1,63 +0,0 @@
/*
* Copyright 2022 Tarsin Norbin
*
* This file is part of EhViewer
*
* EhViewer is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* EhViewer is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* EhViewer. If not, see <https://www.gnu.org/licenses/>.
*/
/* libjpeg-turbo build number */
#define BUILD "20220523"
/* Compiler's inline keyword */
#undef inline
/* How to obtain function inlining. */
#define INLINE __inline__ __attribute__((always_inline))
/* How to obtain thread-local storage */
#define THREAD_LOCAL __thread
/* Define to the full name of this package. */
#define PACKAGE_NAME "libjpeg-turbo"
/* Version number of package */
#define VERSION "2.1.3"
/* The size of `size_t', as computed by sizeof. */
#define SIZEOF_SIZE_T __SIZEOF_SIZE_T__
/* Define if your compiler has __builtin_ctzl() and sizeof(unsigned long) == sizeof(size_t). */
#define HAVE_BUILTIN_CTZL
/* Define to 1 if you have the <intrin.h> header file. */
/* #undef HAVE_INTRIN_H */
#if defined(_MSC_VER) && defined(HAVE_INTRIN_H)
#if (SIZEOF_SIZE_T == 8)
#define HAVE_BITSCANFORWARD64
#elif (SIZEOF_SIZE_T == 4)
#define HAVE_BITSCANFORWARD
#endif
#endif
#if defined(__has_attribute)
#if __has_attribute(fallthrough)
#define FALLTHROUGH __attribute__((fallthrough));
#else
#define FALLTHROUGH
#endif
#else
#define FALLTHROUGH
#endif
@@ -1,72 +0,0 @@
/*
* Copyright 2022 Tarsin Norbin
*
* This file is part of EhViewer
*
* EhViewer is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* EhViewer is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* EhViewer. If not, see <https://www.gnu.org/licenses/>.
*/
/*
* jversion.h
*
* This file was part of the Independent JPEG Group's software:
* Copyright (C) 1991-2020, Thomas G. Lane, Guido Vollbeding.
* libjpeg-turbo Modifications:
* Copyright (C) 2010, 2012-2022, D. R. Commander.
* For conditions of distribution and use, see the accompanying README.ijg
* file.
*
* This file contains software version identification.
*/
#if JPEG_LIB_VERSION >= 80
#define JVERSION "8d 15-Jan-2012"
#elif JPEG_LIB_VERSION >= 70
#define JVERSION "7 27-Jun-2009"
#else
#define JVERSION "6b 27-Mar-1998"
#endif
/*
* NOTE: It is our convention to place the authors in the following order:
* - libjpeg-turbo authors (2009-) in descending order of the date of their
* most recent contribution to the project, then in ascending order of the
* date of their first contribution to the project, then in alphabetical
* order
* - Upstream authors in descending order of the date of the first inclusion of
* their code
*/
#define JCOPYRIGHT \
"Copyright (C) 2009-2022 D. R. Commander\n" \
"Copyright (C) 2015, 2020 Google, Inc.\n" \
"Copyright (C) 2019-2020 Arm Limited\n" \
"Copyright (C) 2015-2016, 2018 Matthieu Darbois\n" \
"Copyright (C) 2011-2016 Siarhei Siamashka\n" \
"Copyright (C) 2015 Intel Corporation\n" \
"Copyright (C) 2013-2014 Linaro Limited\n" \
"Copyright (C) 2013-2014 MIPS Technologies, Inc.\n" \
"Copyright (C) 2009, 2012 Pierre Ossman for Cendio AB\n" \
"Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies)\n" \
"Copyright (C) 1999-2006 MIYASAKA Masaru\n" \
"Copyright (C) 1991-2020 Thomas G. Lane, Guido Vollbeding"
#define JCOPYRIGHT_SHORT \
"Copyright (C) 1991-2022 The libjpeg-turbo Project and many others"
@@ -1,37 +0,0 @@
/*
* Copyright (C) 2020, D. R. Commander. All Rights Reserved.
* Copyright (C) 2020-2021, Arm Limited. All Rights Reserved.
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#define HAVE_VLD1_S16_X3
#define HAVE_VLD1_U16_X2
#define HAVE_VLD1Q_U8_X4
/* Define compiler-independent count-leading-zeros and byte-swap macros */
#if defined(_MSC_VER) && !defined(__clang__)
#define BUILTIN_CLZ(x) _CountLeadingZeros(x)
#define BUILTIN_CLZLL(x) _CountLeadingZeros64(x)
#define BUILTIN_BSWAP64(x) _byteswap_uint64(x)
#elif defined(__clang__) || defined(__GNUC__)
#define BUILTIN_CLZ(x) __builtin_clz(x)
#define BUILTIN_CLZLL(x) __builtin_clzll(x)
#define BUILTIN_BSWAP64(x) __builtin_bswap64(x)
#else
#error "Unknown compiler"
#endif
@@ -1,97 +0,0 @@
# Copyright 2018 Hippo Seven
#
# 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.
cmake_minimum_required(VERSION 3.4.1)
project(png C)
set(LIBPNG_SOURCES
libpng/png.c
libpng/pngerror.c
libpng/pngget.c
libpng/pngmem.c
libpng/pngpread.c
libpng/pngread.c
libpng/pngrio.c
libpng/pngrtran.c
libpng/pngrutil.c
libpng/pngset.c
libpng/pngtrans.c
)
set(LIBPNG_INCLUDES
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/libpng
)
set(LIBPNG_DEFINITIONS)
set(LIBPNG_LIBRARIES z)
if (${ANDROID_ABI} STREQUAL "armeabi-v7a")
enable_language(ASM)
set(LIBPNG_SOURCES
${LIBPNG_SOURCES}
libpng/arm/arm_init.c
libpng/arm/filter_neon.S
${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c
)
set(LIBPNG_INCLUDES
${LIBPNG_INCLUDES}
${ANDROID_NDK}/sources/android/cpufeatures
)
set(LIBPNG_DEFINITIONS
${LIBPNG_DEFINITIONS}
-DPNG_ARM_NEON_API_SUPPORTED
-DPNG_ARM_NEON_CHECK_SUPPORTED
-DPNG_ARM_NEON_OPT=1
-DPNG_ARM_NEON_IMPLEMENTATION=2
-DPNG_ARM_NEON_FILE=\"contrib/arm-neon/android-ndk.c\"
)
set(LIBPNG_LIBRARIES
${LIBPNG_LIBRARIES}
)
elseif (${ANDROID_ABI} STREQUAL "arm64-v8a")
enable_language(ASM)
set(LIBPNG_SOURCES
${LIBPNG_SOURCES}
libpng/arm/arm_init.c
libpng/arm/filter_neon_intrinsics.c
libpng/arm/palette_neon_intrinsics.c
${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c
)
set(LIBPNG_INCLUDES
${LIBPNG_INCLUDES}
${ANDROID_NDK}/sources/android/cpufeatures
)
set(LIBPNG_DEFINITIONS
${LIBPNG_DEFINITIONS}
-DPNG_ARM_NEON_API_SUPPORTED
-DPNG_ARM_NEON_CHECK_SUPPORTED
-DPNG_ARM_NEON_OPT=1
-DPNG_ARM_NEON_IMPLEMENTATION=1
-DPNG_ARM_NEON_FILE=\"contrib/arm-neon/android-ndk.c\"
)
set(LIBPNG_LIBRARIES
${LIBPNG_LIBRARIES}
)
endif ()
add_library(png STATIC ${LIBPNG_SOURCES})
target_include_directories(png PUBLIC ${LIBPNG_INCLUDES})
target_compile_definitions(png PRIVATE ${LIBPNG_DEFINITIONS})
target_link_libraries(png PRIVATE ${LIBPNG_LIBRARIES})
@@ -1,246 +0,0 @@
/*
* Copyright 2022 Tarsin Norbin
*
* This file is part of EhViewer
*
* EhViewer is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* EhViewer is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* EhViewer. If not, see <https://www.gnu.org/licenses/>.
*/
/* libpng 1.6.34 STANDARD API DEFINITION */
/* pnglibconf.h - library build configuration */
/* Libpng version 1.6.34 - September 29, 2017 */
/* Copyright (c) 1998-2017 Glenn Randers-Pehrson */
/* This code is released under the libpng license. */
/* For conditions of distribution and use, see the disclaimer */
/* and license in png.h */
/* pnglibconf.h */
/* Machine generated file: DO NOT EDIT */
/* Derived from: scripts/pnglibconf.dfa */
#ifndef PNGLCONF_H
#define PNGLCONF_H
/* options */
#define PNG_16BIT_SUPPORTED
#define PNG_ALIGNED_MEMORY_SUPPORTED
/*#undef PNG_ARM_NEON_API_SUPPORTED*/
/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/
/*#undef PNG_POWERPC_VSX_API_SUPPORTED*/
/*#undef PNG_POWERPC_VSX_CHECK_SUPPORTED*/
#define PNG_BENIGN_ERRORS_SUPPORTED
#define PNG_BENIGN_READ_ERRORS_SUPPORTED
/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/
#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
#define PNG_COLORSPACE_SUPPORTED
#define PNG_CONSOLE_IO_SUPPORTED
#define PNG_CONVERT_tIME_SUPPORTED
#define PNG_EASY_ACCESS_SUPPORTED
/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/
#define PNG_ERROR_TEXT_SUPPORTED
#define PNG_FIXED_POINT_SUPPORTED
#define PNG_FLOATING_ARITHMETIC_SUPPORTED
#define PNG_FLOATING_POINT_SUPPORTED
#define PNG_FORMAT_AFIRST_SUPPORTED
#define PNG_FORMAT_BGR_SUPPORTED
#define PNG_GAMMA_SUPPORTED
#define PNG_GET_PALETTE_MAX_SUPPORTED
#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED
#define PNG_INCH_CONVERSIONS_SUPPORTED
#define PNG_INFO_IMAGE_SUPPORTED
#define PNG_IO_STATE_SUPPORTED
#define PNG_MNG_FEATURES_SUPPORTED
#define PNG_POINTER_INDEXING_SUPPORTED
#define PNG_PROGRESSIVE_READ_SUPPORTED
#define PNG_READ_16BIT_SUPPORTED
#define PNG_READ_ALPHA_MODE_SUPPORTED
#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
#define PNG_READ_BACKGROUND_SUPPORTED
#define PNG_READ_BGR_SUPPORTED
#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
#define PNG_READ_COMPOSITE_NODIV_SUPPORTED
#define PNG_READ_COMPRESSED_TEXT_SUPPORTED
#define PNG_READ_EXPAND_16_SUPPORTED
#define PNG_READ_EXPAND_SUPPORTED
#define PNG_READ_FILLER_SUPPORTED
#define PNG_READ_GAMMA_SUPPORTED
#define PNG_READ_GET_PALETTE_MAX_SUPPORTED
#define PNG_READ_GRAY_TO_RGB_SUPPORTED
#define PNG_READ_INTERLACING_SUPPORTED
#define PNG_READ_INT_FUNCTIONS_SUPPORTED
#define PNG_READ_INVERT_ALPHA_SUPPORTED
#define PNG_READ_INVERT_SUPPORTED
#define PNG_READ_OPT_PLTE_SUPPORTED
#define PNG_READ_PACKSWAP_SUPPORTED
#define PNG_READ_PACK_SUPPORTED
#define PNG_READ_QUANTIZE_SUPPORTED
#define PNG_READ_RGB_TO_GRAY_SUPPORTED
#define PNG_READ_SCALE_16_TO_8_SUPPORTED
#define PNG_READ_SHIFT_SUPPORTED
#define PNG_READ_STRIP_16_TO_8_SUPPORTED
#define PNG_READ_STRIP_ALPHA_SUPPORTED
#define PNG_READ_SUPPORTED
#define PNG_READ_SWAP_ALPHA_SUPPORTED
#define PNG_READ_SWAP_SUPPORTED
#define PNG_READ_TEXT_SUPPORTED
#define PNG_READ_TRANSFORMS_SUPPORTED
#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_READ_USER_CHUNKS_SUPPORTED
#define PNG_READ_USER_TRANSFORM_SUPPORTED
#define PNG_READ_bKGD_SUPPORTED
#define PNG_READ_cHRM_SUPPORTED
#define PNG_READ_eXIf_SUPPORTED
#define PNG_READ_gAMA_SUPPORTED
#define PNG_READ_hIST_SUPPORTED
#define PNG_READ_iCCP_SUPPORTED
#define PNG_READ_iTXt_SUPPORTED
#define PNG_READ_oFFs_SUPPORTED
#define PNG_READ_pCAL_SUPPORTED
#define PNG_READ_pHYs_SUPPORTED
#define PNG_READ_sBIT_SUPPORTED
#define PNG_READ_sCAL_SUPPORTED
#define PNG_READ_sPLT_SUPPORTED
#define PNG_READ_sRGB_SUPPORTED
#define PNG_READ_tEXt_SUPPORTED
#define PNG_READ_tIME_SUPPORTED
#define PNG_READ_tRNS_SUPPORTED
#define PNG_READ_zTXt_SUPPORTED
/*#undef PNG_SAVE_INT_32_SUPPORTED*/
#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_SEQUENTIAL_READ_SUPPORTED
#define PNG_SETJMP_SUPPORTED
#define PNG_SET_OPTION_SUPPORTED
#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_SET_USER_LIMITS_SUPPORTED
/*#undef PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED*/
/*#undef PNG_SIMPLIFIED_READ_BGR_SUPPORTED*/
/*#undef PNG_SIMPLIFIED_READ_SUPPORTED*/
/*#undef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED*/
/*#undef PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED*/
/*#undef PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED*/
/*#undef PNG_SIMPLIFIED_WRITE_SUPPORTED*/
/*#undef PNG_STDIO_SUPPORTED*/
#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_TEXT_SUPPORTED
#define PNG_TIME_RFC1123_SUPPORTED
#define PNG_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_USER_CHUNKS_SUPPORTED
#define PNG_USER_LIMITS_SUPPORTED
#define PNG_USER_MEM_SUPPORTED
#define PNG_USER_TRANSFORM_INFO_SUPPORTED
#define PNG_USER_TRANSFORM_PTR_SUPPORTED
#define PNG_WARNINGS_SUPPORTED
/*
#define PNG_WRITE_16BIT_SUPPORTED
#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
#define PNG_WRITE_BGR_SUPPORTED
#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
#define PNG_WRITE_FILLER_SUPPORTED
#define PNG_WRITE_FILTER_SUPPORTED
#define PNG_WRITE_FLUSH_SUPPORTED
#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED
#define PNG_WRITE_INTERLACING_SUPPORTED
#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED
#define PNG_WRITE_INVERT_ALPHA_SUPPORTED
#define PNG_WRITE_INVERT_SUPPORTED
#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
#define PNG_WRITE_PACKSWAP_SUPPORTED
#define PNG_WRITE_PACK_SUPPORTED
#define PNG_WRITE_SHIFT_SUPPORTED
#define PNG_WRITE_SUPPORTED
#define PNG_WRITE_SWAP_ALPHA_SUPPORTED
#define PNG_WRITE_SWAP_SUPPORTED
#define PNG_WRITE_TEXT_SUPPORTED
#define PNG_WRITE_TRANSFORMS_SUPPORTED
#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_WRITE_USER_TRANSFORM_SUPPORTED
#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
#define PNG_WRITE_bKGD_SUPPORTED
#define PNG_WRITE_cHRM_SUPPORTED
#define PNG_WRITE_eXIf_SUPPORTED
#define PNG_WRITE_gAMA_SUPPORTED
#define PNG_WRITE_hIST_SUPPORTED
#define PNG_WRITE_iCCP_SUPPORTED
#define PNG_WRITE_iTXt_SUPPORTED
#define PNG_WRITE_oFFs_SUPPORTED
#define PNG_WRITE_pCAL_SUPPORTED
#define PNG_WRITE_pHYs_SUPPORTED
#define PNG_WRITE_sBIT_SUPPORTED
#define PNG_WRITE_sCAL_SUPPORTED
#define PNG_WRITE_sPLT_SUPPORTED
#define PNG_WRITE_sRGB_SUPPORTED
#define PNG_WRITE_tEXt_SUPPORTED
#define PNG_WRITE_tIME_SUPPORTED
#define PNG_WRITE_tRNS_SUPPORTED
#define PNG_WRITE_zTXt_SUPPORTED
*/
#define PNG_bKGD_SUPPORTED
#define PNG_cHRM_SUPPORTED
#define PNG_eXIf_SUPPORTED
#define PNG_gAMA_SUPPORTED
#define PNG_hIST_SUPPORTED
#define PNG_iCCP_SUPPORTED
#define PNG_iTXt_SUPPORTED
#define PNG_oFFs_SUPPORTED
#define PNG_pCAL_SUPPORTED
#define PNG_pHYs_SUPPORTED
#define PNG_sBIT_SUPPORTED
#define PNG_sCAL_SUPPORTED
#define PNG_sPLT_SUPPORTED
#define PNG_sRGB_SUPPORTED
#define PNG_tEXt_SUPPORTED
#define PNG_tIME_SUPPORTED
#define PNG_tRNS_SUPPORTED
#define PNG_zTXt_SUPPORTED
/* end of options */
/* apng */
#define PNG_APNG_SUPPORTED
#define PNG_READ_APNG_SUPPORTED
/*#undef PNG_WRITE_APNG_SUPPORTED*/
/* end of apng */
/* settings */
#define PNG_API_RULE 0
#define PNG_DEFAULT_READ_MACROS 1
#define PNG_GAMMA_THRESHOLD_FIXED 5000
#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE
#define PNG_INFLATE_BUF_SIZE 1024
#define PNG_LINKAGE_API extern
#define PNG_LINKAGE_CALLBACK extern
#define PNG_LINKAGE_DATA extern
#define PNG_LINKAGE_FUNCTION extern
#define PNG_MAX_GAMMA_8 11
#define PNG_QUANTIZE_BLUE_BITS 5
#define PNG_QUANTIZE_GREEN_BITS 5
#define PNG_QUANTIZE_RED_BITS 5
#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1)
#define PNG_TEXT_Z_DEFAULT_STRATEGY 0
#define PNG_USER_CHUNK_CACHE_MAX 1000
#define PNG_USER_CHUNK_MALLOC_MAX 8000000
#define PNG_USER_HEIGHT_MAX 1000000
#define PNG_USER_WIDTH_MAX 1000000
#define PNG_ZBUF_SIZE 8192
#define PNG_ZLIB_VERNUM 0 /* unknown */
#define PNG_Z_DEFAULT_COMPRESSION (-1)
#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0
#define PNG_Z_DEFAULT_STRATEGY 1
#define PNG_sCAL_PRECISION 5
#define PNG_sRGB_PROFILE_CHECKS 2
/* end of settings */
#endif /* PNGLCONF_H */
+2 -15
View File
@@ -14,18 +14,12 @@
* limitations under the License.
*/
//
// Created by Hippo on 10/19/2015.
//
#ifndef LOG_H
#define LOG_H
#include <android/log.h>
#include "utils.h"
#define TAG "Image"
#define TAG "ImageDecoder_wrapper"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG ,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG ,__VA_ARGS__)
@@ -33,11 +27,4 @@
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, TAG ,__VA_ARGS__)
#define FILE_LINE MAKESTRING(STRINGIZE, __LINE__ )
#define MSG(msg) __FILE__ "(" FILE_LINE "): "msg
#define OUT_OF_MEMORY "Out of memory"
#define WTF_OM LOGE(MSG(OUT_OF_MEMORY))
#endif //LOG_H
#endif /* LOG_H */
Loaded 100 of 124 files, more files were not shown because too many files have changed in this diff. Show more