Compare commits

..
1 Commits
Author SHA1 Message Date
tarsin 34ee9d39ad Release 1.7.28.3
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-07-19 19:41:19 +08:00
125 changed files with 6999 additions and 2600 deletions

No files matched your search

@@ -1,6 +1,6 @@
name: CI
name: EhViewer
on: [push]
on: [push, pull_request]
jobs:
linux:
@@ -32,7 +32,7 @@ jobs:
- name: Update ARM64
uses: actions/upload-artifact@v3
with:
name: arm64-v8a-${{ github.sha }}
name: arm64-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
- name: Update x86_64
-54
View File
@@ -1,54 +0,0 @@
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,7 +36,6 @@ 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
+10 -4
View File
@@ -1,9 +1,15 @@
[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://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
url = https://git.tukaani.org/xz.git
+19 -16
View File
@@ -4,8 +4,8 @@
</h1>
<p align="center">
<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 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>
<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,31 +21,30 @@
</a>
</p>
# Description
致力于轻量化与良好性能表现的 EhViewer 分支
## Description
这是一个 E-Hentai Android 平台的浏览器
已针对性适配 Material Design 3 及其动态取色系统
动态配色仅供 Android 12 及以上平台使用!
EhViewer fork dedicated to lightweight and high-performance
with Material Design 3 and Dynamic Color Support
An E-Hentai Application for Android.
# Download
with Material Design 3 and Dynamic Color Support.
<a href="https://github.com/Ehviewer-Overhauled/Ehviewer/releases"><img alt="Get it on Google Play" src="art/get-it-on-github.svg" width="200px"/></a>
Dynamic Color only support Android S and above!
App 分为 1.8 与 1.7 两个版本。1.8 版本适配 Android 12 及以上系统,为开发主线;1.7 版本适配 Android 11 及以下版本,削减了 Android 12 及以上系统的独有功能,使其能在低版本 Android 上正常工作
## Download
App is divided into two versions: 1.7 and 1.8 . Version 1.8 support Android 12 and above, also the develop mainline. Version 1.7 support Android 11 and below, drop down some spicetify functions for Android 12 to make app useable on lower Android System
[Github 下载](https://github.com/Ehviewer-Overhauled/Ehviewer/releases)
# Screenshot
## Screenshot
![screenshot-01](art/screenshot-01.png)
![screenshot-02](art/screenshot-02.png)
# Thanks
## Thanks
本项目受到了诸多开源项目的帮助
@@ -53,11 +52,13 @@ 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)
@@ -65,7 +66,7 @@ Here is the libraries
- [XZ Utils](https://tukaani.org/xz/)
# License
## License
Copyright 2014-2019 Hippo Seven
Copyright 2020-2022 NekoInverter
@@ -76,3 +77,5 @@ 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)
+6 -5
View File
@@ -32,10 +32,10 @@ android {
defaultConfig {
applicationId "moe.tarsin.ehviewer"
minSdkVersion 31
minSdkVersion 26
targetSdkVersion 33
versionCode 180004
versionName "1.8.0.4"
versionCode 114530
versionName "1.7.28.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-alpha02"
def collection_version = "1.3.0-alpha01"
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.1"
def fragment_version = "1.5.0"
implementation "androidx.fragment:fragment:$fragment_version"
implementation "androidx.fragment:fragment-ktx:$fragment_version"
@@ -148,6 +148,7 @@ 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'
+30 -18
View File
@@ -26,11 +26,9 @@
<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.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-feature
android:glEsVersion="0x00020000"
@@ -121,21 +119,35 @@
<data android:mimeType="application/x-7z-compressed" />
<data android:mimeType="application/x-rar-compressed" />
<data android:mimeType="application/x-zip-compressed" />
<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>
<!-- 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>
<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" />
</intent-filter>
</activity>
@@ -49,7 +49,7 @@ class EditTextDialogBuilder constructor(
return mDialog as AlertDialog
}
override fun onEditorAction(v: TextView?, p1: Int, event: KeyEvent?): Boolean {
override fun onEditorAction(v: TextView, actionId: Int, event: KeyEvent): Boolean {
return if (mDialog != null) {
val button = mDialog!!.getButton(DialogInterface.BUTTON_POSITIVE)
button?.performClick()
@@ -167,7 +167,6 @@ 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;
@@ -176,7 +175,21 @@ 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
@@ -231,21 +244,7 @@ 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,7 +23,6 @@ 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> {
@@ -35,8 +34,7 @@ public class ImageBitmapHelper implements ValueHelper<ImageBitmap> {
public ImageBitmap decode(@NonNull InputStreamPipe isPipe) {
try {
isPipe.obtain();
FileInputStream is = (FileInputStream) isPipe.open();
return ImageBitmap.decode(is.getFD());
return ImageBitmap.decode(isPipe.open());
} catch (OutOfMemoryError | IOException e) {
return null;
} finally {
@@ -41,7 +41,6 @@ import com.hippo.yorozuya.NumberUtils;
import java.io.File;
import java.util.Locale;
import java.util.Set;
public class Settings {
@@ -272,11 +271,6 @@ 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;
@@ -390,21 +384,6 @@ 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);
@@ -1160,12 +1139,4 @@ 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,7 +18,6 @@ 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;
@@ -27,29 +26,26 @@ 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, Long> streams = new LinkedHashMap<>();
private final LinkedHashMap<Integer, InputStream> streams = new LinkedHashMap<>();
private final AtomicInteger decodingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX);
private Thread archiveThread;
private Thread decodeThread;
@@ -60,7 +56,7 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
UriArchiveAccessor archiveAccessor1;
try {
archiveAccessor1 = new UriArchiveAccessor(context, uri);
} catch (Exception e) {
} catch (FileNotFoundException e) {
archiveAccessor1 = null;
e.printStackTrace();
}
@@ -94,11 +90,6 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
decodeThread.interrupt();
decodeThread = null;
}
try {
archiveAccessor.close();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
@@ -168,27 +159,9 @@ 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() {
try {
size = archiveAccessor.open();
} catch (Exception e) {
e.printStackTrace();
size = 0;
}
size = archiveAccessor.open();
if (size <= 0) {
size = STATE_ERROR;
error = GetText.getString(R.string.error_reading_failed);
@@ -198,34 +171,6 @@ 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) {
@@ -249,18 +194,17 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
continue;
}
Pipe pipe = new Pipe(4096 * 16);
synchronized (streams) {
if (streams.get(index) != null) {
continue;
}
}
long addr = archiveAccessor.extracttoOutputStream(index);
synchronized (streams) {
streams.put(index, addr);
streams.put(index, pipe.getInputStream());
streams.notify();
}
archiveAccessor.extractTargetIndexToOutputStream(index, pipe.getOutputStream());
}
}
}
@@ -270,7 +214,7 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
int index;
Long addr;
InputStream stream;
synchronized (streams) {
if (streams.isEmpty()) {
try {
@@ -282,16 +226,16 @@ public class ArchiveGalleryProvider extends GalleryProvider2 {
continue;
}
Iterator<Map.Entry<Integer, Long>> iterator = streams.entrySet().iterator();
Map.Entry<Integer, Long> entry = iterator.next();
Iterator<Map.Entry<Integer, InputStream>> iterator = streams.entrySet().iterator();
Map.Entry<Integer, InputStream> entry = iterator.next();
iterator.remove();
index = entry.getKey();
addr = entry.getValue();
stream = entry.getValue();
decodingIndex.lazySet(index);
}
try {
Image image = Image.decodeAddr(addr, true);
Image image = Image.decode(stream, true);
if (image != null) {
notifyPageSucceed(index, image);
} else {
@@ -34,7 +34,6 @@ 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;
@@ -259,7 +258,7 @@ public class DirGalleryProvider extends GalleryProvider2 implements Runnable {
InputStream is = null;
try {
is = files[index].openInputStream();
Image image = Image.decode(((FileInputStream) is).getFD(), true);
Image image = Image.decode(is, true);
mDecodingIndex.lazySet(GalleryPageView.INVALID_INDEX);
if (image != null) {
notifyPageSucceed(index, image);
@@ -0,0 +1,156 @@
/*
* 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,27 +41,6 @@ class VersionPreference @JvmOverloads constructor(
override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {
super.onPrepareDialogBuilder(builder)
builder.setTitle(R.string.show_library_version)
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
}
builder.setMessage(Native.getlibarchiveVersion() + "\nlzma(xz-utils) " + Native.getliblzmaVersion() + "\nzlib " + Native.getzlibVersion() + "\nlibjpeg-turbo " + Native.getlibjpeg_turboVersion())
}
}
@@ -66,7 +66,6 @@ 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;
@@ -1097,6 +1096,33 @@ 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;
@@ -1667,7 +1693,7 @@ public final class SpiderQueen implements Runnable {
pipe.obtain();
try {
is = pipe.open();
is = new AutoCloseInputStream(pipe, pipe.open());
} catch (IOException e) {
// Can't open pipe
error = GetText.getString(R.string.error_reading_failed);
@@ -1677,17 +1703,7 @@ public final class SpiderQueen implements Runnable {
}
if (is != null) {
try {
image = Image.decode(((FileInputStream) is).getFD(), true);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
image = Image.decode(is, true);
if (image == null) {
error = GetText.getString(R.string.error_decoding_failed);
}
@@ -37,8 +37,6 @@ 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;
@@ -69,7 +67,6 @@ 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;
@@ -106,7 +103,6 @@ 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;
@@ -226,9 +222,6 @@ 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) {
@@ -290,16 +283,7 @@ 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) {
@@ -330,19 +314,10 @@ 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
@@ -489,7 +464,16 @@ 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() {
@@ -1049,66 +1033,6 @@ 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;
@@ -1370,4 +1294,6 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
}
}
@@ -22,6 +22,7 @@ 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,6 +55,8 @@ 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;
@@ -388,9 +390,90 @@ 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,6 +45,8 @@ 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;
@@ -309,9 +311,40 @@ 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,6 +59,8 @@ 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;
@@ -632,9 +634,40 @@ 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,7 +36,6 @@ 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;
@@ -54,6 +53,7 @@ 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 > 1 && mHelper.canGoTo()) {
if (pages > 0 && 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.setValueTo(mPages);
mSlider.setValue(mCurrentPage + 1);
mSlider.setRange(1, mPages);
mSlider.setProgress(mCurrentPage + 1);
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(this);
dialog.setOnDismissListener(this);
@@ -431,7 +431,7 @@ public class GalleryPreviewsScene extends ToolbarScene {
return;
}
int page = (int) (mSlider.getValue() - 1);
int page = mSlider.getProgress() - 1;
if (page >= 0 && page < mPages && mHelper != null) {
mHelper.goTo(page);
if (mDialog != null) {
@@ -0,0 +1,229 @@
/*
* 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));
}
}
}
@@ -1,189 +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.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,7 +57,6 @@ public abstract class GalleryProvider {
public void stop() {
OSUtils.checkMainLoop();
mImageCache.evictAll();
Image.lazyEvictAll();
}
public void setGLRoot(GLRoot glRoot) {
@@ -246,14 +245,18 @@ public abstract class GalleryProvider {
}
public void add(Integer key, ImageWrapper value) {
if (value.getFormat() != Image.FORMAT_ANIMATED)
if (value.obtain())
put(key, value);
if (value.getFormat() != Image.FORMAT_GIF && value.obtain()) {
put(key, value);
}
}
@Override
protected int sizeOf(Integer key, ImageWrapper value) {
return value.getWidth() * value.getHeight() * 4;
int size = value.getWidth() * value.getHeight() * 4;
if (value.getFormat() == Image.FORMAT_GIF) {
size *= 5;
}
return size;
}
@Override
@@ -153,6 +153,12 @@ 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() {
@@ -233,7 +239,7 @@ public class ImageTexture implements Texture, Animatable {
}
boolean end = mReleased.get() || mImage.isImageRecycled() || mNeedRelease.get() ||
(mImage.getFormat() == 0) || mRunning.get();
(mImage.isCompleted() && mImage.getFrameCount() <= 1) || mRunning.get();
synchronized (mImage) {
mImageBusy = false;
@@ -591,11 +597,25 @@ 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() || mImage.getFormat() == 0) {
if (mNeedRelease.get() || frameCount <= 1) {
mAnimateRunnable = null;
return;
}
@@ -122,6 +122,22 @@ 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)
*/
@@ -152,6 +168,13 @@ public class ImageWrapper {
return mImage.getDelay();
}
/**
* @see Image#getFrameCount()
*/
public int getFrameCount() {
return mImage.getFrameCount();
}
/**
* @see Image#isOpaque()
*/
@@ -33,7 +33,6 @@ 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,7 +41,6 @@ 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,13 +82,6 @@ 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;
@@ -104,6 +97,13 @@ public class BatteryView extends AppCompatTextView {
}
};
private void startCharger() {
if (!mIsChargerWorking) {
getHandler().post(mCharger);
mIsChargerWorking = true;
}
}
private void stopCharger() {
if (mIsChargerWorking) {
getHandler().removeCallbacks(mCharger);
@@ -145,7 +145,6 @@ public class BatteryView extends AppCompatTextView {
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
@@ -0,0 +1,515 @@
/*
* 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,10 +97,6 @@ public class TextClock extends AppCompatTextView {
} else {
mTime = Calendar.getInstance();
}
}
public CharSequence getFormat12Hour() {
return mFormat12;
} private final Runnable mTicker = new Runnable() {
@Override
public void run() {
@@ -113,6 +109,10 @@ public class TextClock extends AppCompatTextView {
}
};
public CharSequence getFormat12Hour() {
return mFormat12;
}
public void setFormat12Hour(CharSequence format) {
mFormat12 = format;
@@ -144,20 +144,6 @@ public class TextClock extends AppCompatTextView {
createTime(timeZone);
onTimeChanged();
}
/**
* Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()}
* depending on whether the user has selected 24-hour format.
* <p>
* Calling this method does not schedule or unschedule the time ticker.
*/
private void chooseFormat() {
chooseFormat(true);
}
public CharSequence getFormat() {
return mFormat;
} private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
@@ -172,6 +158,20 @@ public class TextClock extends AppCompatTextView {
}
};
/**
* Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()}
* depending on whether the user has selected 24-hour format.
* <p>
* Calling this method does not schedule or unschedule the time ticker.
*/
private void chooseFormat() {
chooseFormat(true);
}
public CharSequence getFormat() {
return mFormat;
}
/**
* Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()}
* depending on whether the user has selected 24-hour format.
@@ -1,38 +0,0 @@
<?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>
@@ -1,191 +0,0 @@
<?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>
@@ -1,263 +0,0 @@
<?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,20 +1,17 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2022 hathlife
~
~ This file is part of EhViewer
~ 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
~
~ 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/>.
~ 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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -1,20 +1,17 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2022 hathlife
~
~ This file is part of EhViewer
~ 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
~
~ 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/>.
~ 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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -39,7 +36,7 @@
<ImageView
android:layout_width="257dp"
android:layout_height="match_parent"
android:layout_height="518dp"
android:layout_gravity="center_horizontal"
android:padding="@dimen/keyline_margin"
app:srcCompat="@mipmap/ic_launcher" />
@@ -1,20 +1,17 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2022 hathlife
~
~ This file is part of EhViewer
~ 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
~
~ 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/>.
~ 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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -1,20 +1,17 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2022 hathlife
~
~ This file is part of EhViewer
~ 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
~
~ 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/>.
~ 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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+3 -4
View File
@@ -28,14 +28,13 @@
android:layout_gravity="center_vertical"
android:gravity="center_horizontal" />
<com.google.android.material.slider.Slider
<com.hippo.widget.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:stepSize="1"
android:valueFrom="1" />
android:layout_weight="1" />
<TextView
android:id="@+id/end"
@@ -64,18 +64,16 @@
android:layout_marginBottom="@dimen/keyline_margin"
app:fitsSystemWindowsInsets="top">
<androidx.constraintlayout.widget.ConstraintLayout
<RelativeLayout
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"
style="@style/Widget.Material3.CardView.Elevated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
android:layout_height="wrap_content">
<com.hippo.widget.LoadImageView
android:id="@+id/thumb"
@@ -89,82 +87,69 @@
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"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
<TableLayout
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="start"
android:singleLine="true"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TableRow>
<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/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"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end"
android:singleLine="true" />
<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>
<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" />
<TableRow android:paddingTop="8dp">
<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/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" />
<TextView
android:id="@+id/pages"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end"
android:singleLine="true" />
</TableRow>
</androidx.constraintlayout.widget.ConstraintLayout>
<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>
</com.google.android.material.card.MaterialCardView>
@@ -173,31 +158,30 @@
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:layout_constraintBottom_toBottomOf="@id/thumb_card"
app:layout_constraintEnd_toEndOf="parent" />
app:ensureMinTouchTargetSize="true" />
<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:chipSpacingHorizontal="8dp"
app:ensureMinTouchTargetSize="true"
app:layout_constraintBottom_toTopOf="@id/uploader"
app:layout_constraintEnd_toEndOf="parent" />
app:ensureMinTouchTargetSize="true" />
</androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>
</com.google.android.material.card.MaterialCardView>
@@ -214,17 +198,19 @@
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"
+8 -11
View File
@@ -95,30 +95,27 @@
android:layout_height="wrap_content"
android:clipToPadding="false"
android:gravity="bottom"
android:orientation="horizontal"
android:orientation="vertical"
android:paddingLeft="12dp"
android:paddingTop="4dp"
android:paddingRight="12dp"
android:paddingBottom="4dp">
<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" />
<Button
android:id="@+id/register"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/register" />
</LinearLayout>
<LinearLayout
@@ -12,8 +12,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
@@ -140,22 +139,13 @@
android:minHeight="0dp"
android:text="@string/search_sr" />
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
<Spinner
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/advance_search_table_item_margin"
android:layout_marginLeft="@dimen/advance_search_table_item_margin"
android:layout_weight="1"
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>
android:entries="@array/search_min_rating" />
</LinearLayout>
<LinearLayout
@@ -18,5 +18,4 @@
<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,5 +18,4 @@
<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
@@ -0,0 +1,21 @@
<?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
@@ -0,0 +1,21 @@
<?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
@@ -0,0 +1,19 @@
<?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>
@@ -0,0 +1,21 @@
<?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>
@@ -0,0 +1,20 @@
<?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,9 +764,4 @@
<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,9 +828,4 @@
<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,6 +51,12 @@
<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.
-55
View File
@@ -1,55 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="646" height="250" id="svg8502" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="get-it-on-github.svg" inkscape:export-filename="/home/nixdorf/Lokaler Speicher/Build/andOTP/assets/badges/get-it-on-github.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90">
<defs id="defs8504" />
<sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="0.50000001" inkscape:cx="-223.13537" inkscape:cy="546.37524" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" fit-margin-top="0" fit-margin-left="0" fit-margin-right="0" fit-margin-bottom="0" inkscape:window-width="1920" inkscape:window-height="1021" inkscape:window-x="0" inkscape:window-y="31" inkscape:window-maximized="1" />
<metadata id="metadata8507">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:creator>
<cc:Agent>
<dc:title>Andrew Nayenko</dc:title>
</cc:Agent>
</dc:creator>
<dc:publisher>
<cc:Agent>
<dc:title>https://f-droid.org</dc:title>
</cc:Agent>
</dc:publisher>
<cc:license rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" />
<dc:contributor>
<cc:Agent>
<dc:title />
</cc:Agent>
</dc:contributor>
</cc:Work>
<cc:License rdf:about="http://creativecommons.org/licenses/by-sa/3.0/">
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(-289,-312.36218)">
<rect style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#a6a6a6;stroke-width:4;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" id="rect3007" width="560" height="164" x="332" y="355.36218" rx="20" ry="20" />
<text sodipodi:linespacing="100%" id="text3013" y="402.36697" x="508.95129" style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.39519119px;line-height:100%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;enable-background:accumulate" xml:space="preserve"><tspan y="402.36697" x="508.95129" id="tspan3015" sodipodi:role="line" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:34.125px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans'">GET IT ON</tspan></text>
<text xml:space="preserve" style="color:black;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:29.70882034px;line-height:100%;font-family:Rokkitt;-inkscape-font-specification:'Rokkitt Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:white;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;enable-background:accumulate" x="508.21259" y="489.36108" id="text5902" sodipodi:linespacing="100%"><tspan sodipodi:role="line" id="tspan5904" x="508.21259" y="489.36108" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:87.5px;line-height:100%;font-family:'Roboto Slab';-inkscape-font-specification:'Roboto Slab Bold';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:white;fill-opacity:1">GitHub</tspan></text>
<g transform="matrix(1.0017681,0,0,1.0017681,68.770756,92.660249)" id="g5136">
<path style="fill:white;fill-opacity:1;fill-rule:evenodd;stroke:none" d="m 350.60937,282.4375 c -33.34765,0 -60.38671,27.03516 -60.38671,60.38672 0,26.67969 17.30078,49.3164 41.29687,57.30078 3.01563,0.55859 4.125,-1.30859 4.125,-2.90625 0,-1.4375 -0.0547,-6.19531 -0.082,-11.24219 -16.80078,3.65235 -20.34375,-7.125 -20.34375,-7.125 -2.75,-6.98047 -6.70703,-8.83594 -6.70703,-8.83594 -5.48047,-3.74609 0.41406,-3.67187 0.41406,-3.67187 6.0625,0.42578 9.25781,6.22266 9.25781,6.22266 5.38282,9.23437 14.125,6.5664 17.57032,5.02343 0.54296,-3.90234 2.10937,-6.57031 3.83593,-8.07812 -13.41406,-1.52735 -27.51562,-6.70313 -27.51562,-29.84375 0,-6.59375 2.35937,-11.98047 6.22265,-16.20703 -0.625,-1.52344 -2.6914,-7.66407 0.58594,-15.98047 0,0 5.07031,-1.625 16.60938,6.1875 4.82031,-1.33594 9.98437,-2.00781 15.11718,-2.03125 5.13282,0.0234 10.30079,0.69531 15.12891,2.03125 11.52344,-7.8125 16.58984,-6.1875 16.58984,-6.1875 3.28516,8.3164 1.21875,14.45703 0.58985,15.98047 3.87109,4.22656 6.21484,9.61328 6.21484,16.20703 0,23.19531 -14.125,28.30078 -27.57422,29.79687 2.16797,1.875 4.09766,5.55078 4.09766,11.1836 0,8.07812 -0.0703,14.58203 -0.0703,16.57031 0,1.60937 1.08593,3.49219 4.14843,2.89844 23.98047,-7.9961 41.26172,-30.6211 41.26172,-57.29297 0,-33.35156 -27.03515,-60.38672 -60.38672,-60.38672" id="path4937" inkscape:connector-curvature="0" />
<path style="fill:white;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 313.09375,369.14062 c -0.13281,0.30079 -0.60547,0.38672 -1.03516,0.1836 -0.4375,-0.19922 -0.68359,-0.60547 -0.54297,-0.91016 0.12891,-0.30859 0.60157,-0.39062 1.03907,-0.1875 0.4414,0.19922 0.6914,0.61328 0.53906,0.91406" id="path4939" inkscape:connector-curvature="0" />
<path style="fill:white;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 315.53906,371.86719 c -0.28906,0.26562 -0.85156,0.14453 -1.23437,-0.27735 -0.39453,-0.42187 -0.46875,-0.98437 -0.17578,-1.25781 0.29687,-0.26562 0.84375,-0.14062 1.23828,0.28125 0.39453,0.42578 0.47265,0.98438 0.17187,1.25391" id="path4941" inkscape:connector-curvature="0" />
<path style="fill:white;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 317.92187,375.34375 c -0.37109,0.25781 -0.97656,0.0156 -1.35156,-0.51953 -0.37109,-0.53906 -0.37109,-1.17969 0.008,-1.4375 0.375,-0.25781 0.97266,-0.0274 1.35157,0.5039 0.36718,0.54688 0.36718,1.19141 -0.008,1.45313" id="path4943" inkscape:connector-curvature="0" />
<path style="fill:white;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 321.18359,378.70312 c -0.33203,0.36719 -1.03906,0.26954 -1.55468,-0.23046 -0.52735,-0.48438 -0.67579,-1.17579 -0.34375,-1.54297 0.33593,-0.36328 1.04687,-0.26172 1.5664,0.23047 0.52344,0.48828 0.6836,1.18359 0.33203,1.54296" id="path4945-3" inkscape:connector-curvature="0" />
<path style="fill:white;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 325.68359,380.65625 c -0.14843,0.47266 -0.82812,0.6875 -1.51172,0.48828 -0.68359,-0.20703 -1.1289,-0.76172 -0.99218,-1.24219 0.14453,-0.47265 0.82422,-0.69922 1.51562,-0.48437 0.67969,0.20703 1.12891,0.75781 0.98828,1.23828" id="path4947-6" inkscape:connector-curvature="0" />
<path style="fill:white;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 330.625,381.01953 c 0.0156,0.49609 -0.5625,0.91016 -1.28125,0.91797 -0.72266,0.0156 -1.30859,-0.38672 -1.31641,-0.875 0,-0.50391 0.57032,-0.91406 1.28907,-0.92578 0.71875,-0.0156 1.30859,0.38672 1.30859,0.88281" id="path4949" inkscape:connector-curvature="0" />
<path style="fill:white;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 335.22266,380.23437 c 0.0859,0.48829 -0.41407,0.98438 -1.125,1.11719 -0.70313,0.12891 -1.35157,-0.17187 -1.44141,-0.65234 -0.0859,-0.5 0.42187,-0.9961 1.12109,-1.125 0.71485,-0.125 1.35547,0.16797 1.44532,0.66015" id="path4951-7" inkscape:connector-curvature="0" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.5 KiB

+1 -1
View File
@@ -9,7 +9,7 @@ android {
ndkVersion "25.0.8775105"
defaultConfig {
minSdkVersion 31
minSdkVersion 26
targetSdkVersion 33
consumerProguardFiles 'proguard-rules.pro'
+4 -2
View File
@@ -28,7 +28,9 @@ public class Native {
public static native String getliblzmaVersion();
public static native String getzlibVersion();
public static native String getlibzstdVersion();
public static native String getnettleVersion();
public static native String getlibjpeg_turboVersion();
public static native String getzlibVersion();
}
@@ -21,44 +21,103 @@ 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 {
ParcelFileDescriptor pfd;
FileDescriptor fd;
long archiveAddr;
Context ctx;
Uri uri;
public class UriArchiveAccessor implements Closeable {
public static int OPEN_OK = 1;
public static int OPEN_ERR = 2;
private final OsReadableFile osf;
public UriArchiveAccessor(Context ctx, Uri uri) {
this.ctx = ctx;
this.uri = uri;
public UriArchiveAccessor(Context ctx, Uri uri) throws FileNotFoundException {
osf = new OsReadableFile(ctx, uri);
}
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 int open() {
return openArchive(osf);
}
private native int openArchive(long addr, long size);
public void extractTargetIndexToOutputStream(int index, OutputStream os) {
extracttoOutputStream(osf, index, os);
}
public native long extracttoOutputStream(int index);
private native int openArchive(OsReadableFile osf);
public native boolean needPassword();
private native int extracttoOutputStream(OsReadableFile osf, int index, OutputStream os);
public native boolean providePassword(String str);
@Override
public void close() throws IOException {
osf.close();
}
private native void closeArchive();
static class OsReadableFile {
ParcelFileDescriptor pfd;
FileDescriptor fd;
public void close() throws Exception {
closeArchive();
Os.munmap(archiveAddr, pfd.getStatSize());
Os.close(fd);
pfd.close();
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);
}
}
}
}
+138 -57
View File
@@ -20,68 +20,65 @@ import android.graphics.Bitmap;
import androidx.annotation.Nullable;
import java.io.FileDescriptor;
import java.util.ArrayList;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicInteger;
/**
* The {@code Image} is a image which stored pixel data in native heap
*/
public final class Image {
public static final int FORMAT_NORMAL = 0;
public static final int FORMAT_ANIMATED = 1;
/**
* Unknown image format
*/
public static final int FORMAT_UNKNOWN = -1;
private static final ArrayList<Image> imageList = new ArrayList<>();
private static boolean needEvictAll = false;
/**
* Plain image format, for {@code Image} from {@link #create(Bitmap)}
*/
public static final int FORMAT_PLAIN = 0;
/**
* 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;
imageList.add(this);
}
public static void lazyEvictAll() {
needEvictAll = true;
}
private static void evictAll() {
(new ArrayList<>(imageList)).forEach(Image::recycle);
needEvictAll = false;
sImageCount.getAndIncrement();
}
/**
* Decode image from {@code InputStream}
*/
@Nullable
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);
public static Image decode(InputStream is, boolean partially) {
if (!(is instanceof BufferedInputStream)) {
is = new BufferedInputStream(is);
}
return nativeDecode(is, partially);
}
/**
@@ -97,30 +94,58 @@ public final class Image {
* It is useful for debug.
*/
public static int getImageCount() {
return imageList.size();
return sImageCount.get();
}
private static native Image nativeDecode(FileDescriptor fd);
/**
* Return all supported image formats, exclude {@link #FORMAT_PLAIN}
*/
public static int[] getSupportedImageFormats() {
return nativeGetSupportedImageFormats();
}
private static native Image nativeDecodeFdInt(int 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 nativeDecodeAddr(long addr);
private static native Image nativeDecode(InputStream is, boolean partially);
private static native Image nativeCreate(Bitmap bitmap);
private static native void nativeRender(long nativePtr,
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,
int srcX, int srcY, Bitmap dst, int dstX, int dstY,
int width, int height);
int width, int height, boolean fillBlank, int defaultColor);
private static native void nativeTexImage(long nativePtr, boolean init, int offsetX, int offsetY, 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 nativeAdvance(long nativePtr);
private static native void nativeAdvance(long nativePtr, int format);
private static native int nativeGetDelay(long nativePtr);
private static native int nativeGetDelay(long nativePtr, int format);
private static native boolean nativeIsOpaque(long nativePtr);
private static native int nativeFrameCount(long nativePtr, int format);
private static native void nativeRecycle(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);
/**
* Return the format of the image
@@ -143,19 +168,48 @@ 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) {
throw new IllegalStateException("The image is recycled.");
if (mRecycleTracker != null) {
throw new IllegalStateException("The image is recycled.", mRecycleTracker);
} else {
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, srcX, srcY, dst, dstX, dstY, width, height);
nativeRender(mNativePtr, mFormat, srcX, srcY, dst, dstX, dstY,
width, height, fillBlank, defaultColor);
}
/**
@@ -165,7 +219,7 @@ public final class Image {
*/
public void texImage(boolean init, int offsetX, int offsetY, int width, int height) {
checkRecycled();
nativeTexImage(mNativePtr, init, offsetX, offsetY, width, height);
nativeTexImage(mNativePtr, mFormat, init, offsetX, offsetY, width, height);
}
/**
@@ -173,7 +227,7 @@ public final class Image {
*/
public void advance() {
checkRecycled();
nativeAdvance(mNativePtr);
nativeAdvance(mNativePtr, mFormat);
}
/**
@@ -181,16 +235,40 @@ public final class Image {
*/
public int getDelay() {
checkRecycled();
int delay = nativeGetDelay(mNativePtr);
int delay = nativeGetDelay(mNativePtr, mFormat);
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);
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);
}
/**
@@ -198,11 +276,14 @@ 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 synchronized void recycle() {
public void recycle() {
if (mNativePtr != 0) {
nativeRecycle(mNativePtr);
nativeRecycle(mNativePtr, mFormat);
mNativePtr = 0;
imageList.remove(this);
sImageCount.getAndDecrement();
mRecycleTracker = new Throwable("It's a ImageRecycleTracker");
}
}
@@ -28,7 +28,6 @@ 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;
@@ -45,6 +44,8 @@ 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;
@@ -59,8 +60,10 @@ 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 (mFormat == 1) {
if (mFrameCount > 1) {
// For animated image, save image object
mImage = image;
} else {
@@ -70,17 +73,19 @@ public final class ImageBitmap implements Animatable, Runnable {
}
private ImageBitmap(@NonNull Bitmap bitmap) {
mFormat = Image.FORMAT_NORMAL;
mFormat = Image.FORMAT_PLAIN;
mBitmap = bitmap;
mIsOpaque = !bitmap.hasAlpha();
mByteCount = bitmap.getRowBytes() * bitmap.getHeight();
mFrameCount = 1;
}
/**
* Decode {@code InputStream}, then create image.
*/
@Nullable
public static ImageBitmap decode(@NonNull FileDescriptor fd) {
Image image = Image.decode(fd, false);
public static ImageBitmap decode(@NonNull InputStream is) {
Image image = Image.decode(is, false);
if (image != null) {
return create(image);
} else {
@@ -218,6 +223,13 @@ public final class ImageBitmap implements Animatable, Runnable {
return mIsOpaque;
}
/**
* Return byte count of image
*/
public int getByteCount() {
return mByteCount;
}
/**
* Return image is animated
*/
@@ -225,6 +237,13 @@ 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,20 +13,6 @@ 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)
@@ -38,9 +24,6 @@ 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)
+127 -137
View File
@@ -23,9 +23,6 @@
#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__)
@@ -35,162 +32,155 @@
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG ,__VA_ARGS__)
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;
#define BLOCK_SIZE 4096 * 16
const char supportExt[9][6] = {
"jpeg",
"jpg",
"png",
"gif",
"webp",
"bmp",
"ico",
"wbmp",
"heif"
};
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;
static int filename_is_playable_file(const char *name) {
const char *dotptr = strrchr(name, '.');
if (!dotptr++)
return false;
int i;
for (i = 0; i < 9; i++)
if (strcmp(dotptr, supportExt[i]) == 0)
return true;
return false;
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;
}
static long archive_list_all_entries() {
static long archive_list_all_entries(struct archive_inst_sc *inst) {
struct archive *a = inst->archive;
struct archive_entry *entry;
long count = 0;
while (archive_read_next_header(arc, &entry) == ARCHIVE_OK) {
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
if (filename_is_playable_file(archive_entry_pathname(entry)))
count++;
}
if (!count)
LOGE("%s", archive_error_string(arc));
LOGE("%s", archive_error_string(inst->archive));
return count;
}
static void archive_release() {
if (arc) {
archive_read_close(arc);
archive_read_free(arc);
arc = NULL;
}
}
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 *_, 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));
} else {
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_release();
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;
}
typedef struct Memarea {
void *buffer;
long size;
} Memarea;
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;
}
JNIEXPORT jlong JNICALL
Java_com_hippo_UriArchiveAccessor_extracttoOutputStream(JNIEnv *_, jobject thiz, jint index) {
struct archive_entry *entry;
int ret;
if (!arc || index < cur_index) {
archive_alloc();
cur_index = 0;
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);
}
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);
if (r) {
r = 0;
LOGE("%s%s", "Archive open failed:", archive_error_string(arc->archive));
} else {
r = archive_list_all_entries(arc);
LOGI("%s%ld%s", "Found ", r, " image entries in archive");
}
Memarea *memarea = malloc(sizeof(Memarea));
while (archive_read_next_header(arc, &entry) == ARCHIVE_OK) {
archive_read_close(arc->archive);
archive_destroy_inst(arc);
return r;
}
JNIEXPORT jint JNICALL
Java_com_hippo_UriArchiveAccessor_extracttoOutputStream(JNIEnv *env, jobject thiz, jobject osf,
jint index, jobject os) {
int count = 0;
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) {
if (!filename_is_playable_file(archive_entry_pathname(entry)))
continue;
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));
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;
}
break;
}
}
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;
}
free:
(*arc->env)->CallVoidMethod(env, os, closeID);
archive_read_close(arc->archive);
archive_destroy_inst(arc);
return 0;
}
+5 -18
View File
@@ -13,22 +13,9 @@
# limitations under the License.
cmake_minimum_required(VERSION 3.4.1)
project(image C)
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})
add_subdirectory(image)
add_subdirectory(stream)
add_subdirectory(libjpeg-turbo)
add_subdirectory(libpng)
add_subdirectory(giflib)
@@ -0,0 +1,27 @@
# 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})
-111
View File
@@ -1,111 +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/>.
*/
#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 = NULL;
if (AImageDecoder_createFromFd(fd, &decoder) != ANDROID_IMAGE_DECODER_SUCCESS)
return NULL;
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
@@ -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/>.
*/
#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 */
@@ -0,0 +1,60 @@
# 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)
@@ -0,0 +1,44 @@
# 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
@@ -0,0 +1,37 @@
/*
* 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
@@ -0,0 +1,184 @@
/*
* 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);
}
}
}
@@ -0,0 +1,24 @@
/*
* 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
@@ -0,0 +1,36 @@
/*
* 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;
}
@@ -0,0 +1,24 @@
/*
* 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
@@ -0,0 +1,30 @@
/*
* 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
@@ -0,0 +1,532 @@
/*
* 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
@@ -0,0 +1,65 @@
/*
* 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
@@ -0,0 +1,558 @@
/*
* 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
@@ -0,0 +1,84 @@
/*
* 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
@@ -0,0 +1,210 @@
/*
* 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
@@ -0,0 +1,64 @@
/*
* 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
@@ -0,0 +1,129 @@
/*
* 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
@@ -0,0 +1,56 @@
/*
* 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
@@ -0,0 +1,642 @@
/*
* 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
@@ -0,0 +1,89 @@
/*
* 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
@@ -0,0 +1,185 @@
/*
* 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));
}
}
@@ -17,13 +17,17 @@
* 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>
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);
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);
#endif /* IMAGE_IMAGE_UTILS_H */
#endif //IMAGE_IMAGE_UTILS_H
@@ -0,0 +1,304 @@
/*
* 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;
}
@@ -17,20 +17,22 @@
* 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)
typedef struct Memarea {
void *buffer;
long size;
} Memarea;
bool image_onLoad(JavaVM *vm);
bool image_onLoad();
JNIEnv *obtain_env(bool *attach);
void image_onUnload();
void release_env();
#endif /* IMAGE_JAVA_WRAPPER_H */
#endif // IMAGE_JAVA_WRAPPER_H
-107
View File
@@ -1,107 +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.
*/
#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;
}
-150
View File
@@ -1,150 +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.
*/
#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);
if (!image)
return NULL;
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);
}
@@ -0,0 +1,211 @@
# 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})
@@ -0,0 +1,37 @@
/* 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 */
@@ -0,0 +1,63 @@
/*
* 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
@@ -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/>.
*/
/*
* 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"
@@ -0,0 +1,37 @@
/*
* 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
@@ -0,0 +1,97 @@
# 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})
@@ -0,0 +1,246 @@
/*
* 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 */
Loaded 100 of 125 files, more files were not shown because too many files have changed in this diff. Show more