mirror of
https://github.com/oxen-io/session-android.git
synced 2025-12-31 10:16:38 +00:00
Add support for rendering APNGs.
This commit is contained in:
committed by
Cody Henthorne
parent
1d2ffe56fb
commit
250402e9b9
73
app/src/main/java/org/thoughtcrime/securesms/glide/cache/ApngBufferCacheDecoder.java
vendored
Normal file
73
app/src/main/java/org/thoughtcrime/securesms/glide/cache/ApngBufferCacheDecoder.java
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
package org.thoughtcrime.securesms.glide.cache;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.bumptech.glide.load.Options;
|
||||
import com.bumptech.glide.load.ResourceDecoder;
|
||||
import com.bumptech.glide.load.engine.Resource;
|
||||
|
||||
import org.signal.glide.apng.decode.APNGDecoder;
|
||||
import org.signal.glide.apng.decode.APNGParser;
|
||||
import org.signal.glide.common.io.ByteBufferReader;
|
||||
import org.signal.glide.common.loader.ByteBufferLoader;
|
||||
import org.signal.glide.common.loader.Loader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class ApngBufferCacheDecoder implements ResourceDecoder<ByteBuffer, APNGDecoder> {
|
||||
|
||||
@Override
|
||||
public boolean handles(@NonNull ByteBuffer source, @NonNull Options options) {
|
||||
return APNGParser.isAPNG(new ByteBufferReader(source));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Resource<APNGDecoder> decode(@NonNull final ByteBuffer source, int width, int height, @NonNull Options options) throws IOException {
|
||||
if (!APNGParser.isAPNG(new ByteBufferReader(source))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Loader loader = new ByteBufferLoader() {
|
||||
@Override
|
||||
public ByteBuffer getByteBuffer() {
|
||||
source.position(0);
|
||||
return source;
|
||||
}
|
||||
};
|
||||
|
||||
return new FrameSeqDecoderResource(new APNGDecoder(loader, null), source.limit());
|
||||
}
|
||||
|
||||
private static class FrameSeqDecoderResource implements Resource<APNGDecoder> {
|
||||
private final APNGDecoder decoder;
|
||||
private final int size;
|
||||
|
||||
FrameSeqDecoderResource(@NonNull APNGDecoder decoder, int size) {
|
||||
this.decoder = decoder;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Class<APNGDecoder> getResourceClass() {
|
||||
return APNGDecoder.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull APNGDecoder get() {
|
||||
return this.decoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle() {
|
||||
this.decoder.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
48
app/src/main/java/org/thoughtcrime/securesms/glide/cache/ApngFrameDrawableTranscoder.java
vendored
Normal file
48
app/src/main/java/org/thoughtcrime/securesms/glide/cache/ApngFrameDrawableTranscoder.java
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
package org.thoughtcrime.securesms.glide.cache;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.bumptech.glide.load.Options;
|
||||
import com.bumptech.glide.load.engine.Resource;
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableResource;
|
||||
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
|
||||
|
||||
import org.signal.glide.apng.APNGDrawable;
|
||||
import org.signal.glide.apng.decode.APNGDecoder;
|
||||
|
||||
public class ApngFrameDrawableTranscoder implements ResourceTranscoder<APNGDecoder, Drawable> {
|
||||
|
||||
@Override
|
||||
public @Nullable Resource<Drawable> transcode(@NonNull Resource<APNGDecoder> toTranscode, @NonNull Options options) {
|
||||
APNGDecoder decoder = toTranscode.get();
|
||||
APNGDrawable drawable = new APNGDrawable(decoder);
|
||||
|
||||
drawable.setAutoPlay(false);
|
||||
drawable.setLoopLimit(0);
|
||||
|
||||
return new DrawableResource<Drawable>(drawable) {
|
||||
@Override
|
||||
public @NonNull Class<Drawable> getResourceClass() {
|
||||
return Drawable.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
super.initialize();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
44
app/src/main/java/org/thoughtcrime/securesms/glide/cache/ApngStreamCacheDecoder.java
vendored
Normal file
44
app/src/main/java/org/thoughtcrime/securesms/glide/cache/ApngStreamCacheDecoder.java
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package org.thoughtcrime.securesms.glide.cache;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.bumptech.glide.load.Options;
|
||||
import com.bumptech.glide.load.ResourceDecoder;
|
||||
import com.bumptech.glide.load.engine.Resource;
|
||||
|
||||
import org.signal.glide.apng.decode.APNGDecoder;
|
||||
import org.signal.glide.apng.decode.APNGParser;
|
||||
import org.signal.glide.common.io.StreamReader;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class ApngStreamCacheDecoder implements ResourceDecoder<InputStream, APNGDecoder> {
|
||||
|
||||
private final ResourceDecoder<ByteBuffer, APNGDecoder> byteBufferDecoder;
|
||||
|
||||
public ApngStreamCacheDecoder(ResourceDecoder<ByteBuffer, APNGDecoder> byteBufferDecoder) {
|
||||
this.byteBufferDecoder = byteBufferDecoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handles(@NonNull InputStream source, @NonNull Options options) {
|
||||
return APNGParser.isAPNG(new StreamReader(source));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Resource<APNGDecoder> decode(@NonNull final InputStream source, int width, int height, @NonNull Options options) throws IOException {
|
||||
byte[] data = Util.readFully(source);
|
||||
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(data);
|
||||
return byteBufferDecoder.decode(byteBuffer, width, height, options);
|
||||
}
|
||||
}
|
||||
|
||||
51
app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedApngCacheEncoder.java
vendored
Normal file
51
app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedApngCacheEncoder.java
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package org.thoughtcrime.securesms.glide.cache;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.bumptech.glide.load.EncodeStrategy;
|
||||
import com.bumptech.glide.load.Options;
|
||||
import com.bumptech.glide.load.ResourceEncoder;
|
||||
import com.bumptech.glide.load.engine.Resource;
|
||||
|
||||
import org.signal.glide.apng.decode.APNGDecoder;
|
||||
import org.signal.glide.common.loader.Loader;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class EncryptedApngCacheEncoder extends EncryptedCoder implements ResourceEncoder<APNGDecoder> {
|
||||
|
||||
private static final String TAG = Log.tag(EncryptedApngCacheEncoder.class);
|
||||
|
||||
private final byte[] secret;
|
||||
|
||||
public EncryptedApngCacheEncoder(@NonNull byte[] secret) {
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull EncodeStrategy getEncodeStrategy(@NonNull Options options) {
|
||||
return EncodeStrategy.SOURCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean encode(@NonNull Resource<APNGDecoder> data, @NonNull File file, @NonNull Options options) {
|
||||
try {
|
||||
Loader loader = data.get().getLoader();
|
||||
InputStream input = loader.obtain().toInputStream();
|
||||
OutputStream output = createEncryptedOutputStream(secret, file);
|
||||
|
||||
Util.copy(input, output);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package org.thoughtcrime.securesms.glide.cache;
|
||||
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import com.bumptech.glide.load.Options;
|
||||
import com.bumptech.glide.load.ResourceDecoder;
|
||||
import com.bumptech.glide.load.engine.Resource;
|
||||
import com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class EncryptedBitmapCacheDecoder extends EncryptedCoder implements ResourceDecoder<File, Bitmap> {
|
||||
|
||||
private static final String TAG = EncryptedBitmapCacheDecoder.class.getSimpleName();
|
||||
|
||||
private final StreamBitmapDecoder streamBitmapDecoder;
|
||||
private final byte[] secret;
|
||||
|
||||
public EncryptedBitmapCacheDecoder(@NonNull byte[] secret, @NonNull StreamBitmapDecoder streamBitmapDecoder) {
|
||||
this.secret = secret;
|
||||
this.streamBitmapDecoder = streamBitmapDecoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handles(@NonNull File source, @NonNull Options options)
|
||||
throws IOException
|
||||
{
|
||||
try (InputStream inputStream = createEncryptedInputStream(secret, source)) {
|
||||
return streamBitmapDecoder.handles(inputStream, options);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Resource<Bitmap> decode(@NonNull File source, int width, int height, @NonNull Options options)
|
||||
throws IOException
|
||||
{
|
||||
try (InputStream inputStream = createEncryptedInputStream(secret, source)) {
|
||||
return streamBitmapDecoder.decode(inputStream, width, height, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,8 +33,6 @@ public class EncryptedBitmapResourceEncoder extends EncryptedCoder implements Re
|
||||
@SuppressWarnings("EmptyCatchBlock")
|
||||
@Override
|
||||
public boolean encode(@NonNull Resource<Bitmap> data, @NonNull File file, @NonNull Options options) {
|
||||
Log.i(TAG, "Encrypted resource encoder running: " + file.toString());
|
||||
|
||||
Bitmap bitmap = data.get();
|
||||
Bitmap.CompressFormat format = getFormat(bitmap, options);
|
||||
int quality = options.get(BitmapEncoder.COMPRESSION_QUALITY);
|
||||
|
||||
44
app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedCacheDecoder.java
vendored
Normal file
44
app/src/main/java/org/thoughtcrime/securesms/glide/cache/EncryptedCacheDecoder.java
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package org.thoughtcrime.securesms.glide.cache;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.bumptech.glide.load.Options;
|
||||
import com.bumptech.glide.load.ResourceDecoder;
|
||||
import com.bumptech.glide.load.engine.Resource;
|
||||
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class EncryptedCacheDecoder<DecodeType> extends EncryptedCoder implements ResourceDecoder<File, DecodeType> {
|
||||
|
||||
private static final String TAG = Log.tag(EncryptedCacheDecoder.class);
|
||||
|
||||
private final byte[] secret;
|
||||
private final ResourceDecoder<InputStream, DecodeType> decoder;
|
||||
|
||||
public EncryptedCacheDecoder(byte[] secret, ResourceDecoder<InputStream, DecodeType> decoder) {
|
||||
this.secret = secret;
|
||||
this.decoder = decoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handles(@NonNull File source, @NonNull Options options) throws IOException {
|
||||
try (InputStream inputStream = createEncryptedInputStream(secret, source)) {
|
||||
return decoder.handles(inputStream, options);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Resource<DecodeType> decode(@NonNull File source, int width, int height, @NonNull Options options) throws IOException {
|
||||
try (InputStream inputStream = createEncryptedInputStream(secret, source)) {
|
||||
return decoder.decode(inputStream, width, height, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,8 +30,6 @@ public class EncryptedCacheEncoder extends EncryptedCoder implements Encoder<Inp
|
||||
@SuppressWarnings("EmptyCatchBlock")
|
||||
@Override
|
||||
public boolean encode(@NonNull InputStream data, @NonNull File file, @NonNull Options options) {
|
||||
Log.i(TAG, "Encrypted cache encoder running: " + file.toString());
|
||||
|
||||
byte[] buffer = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
|
||||
|
||||
try (OutputStream outputStream = createEncryptedOutputStream(secret, file)) {
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.thoughtcrime.securesms.glide.cache;
|
||||
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import com.bumptech.glide.load.Options;
|
||||
import com.bumptech.glide.load.ResourceDecoder;
|
||||
import com.bumptech.glide.load.engine.Resource;
|
||||
import com.bumptech.glide.load.resource.gif.GifDrawable;
|
||||
import com.bumptech.glide.load.resource.gif.StreamGifDecoder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class EncryptedGifCacheDecoder extends EncryptedCoder implements ResourceDecoder<File, GifDrawable> {
|
||||
|
||||
private static final String TAG = EncryptedGifCacheDecoder.class.getSimpleName();
|
||||
|
||||
private final byte[] secret;
|
||||
private final StreamGifDecoder gifDecoder;
|
||||
|
||||
public EncryptedGifCacheDecoder(@NonNull byte[] secret, @NonNull StreamGifDecoder gifDecoder) {
|
||||
this.secret = secret;
|
||||
this.gifDecoder = gifDecoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handles(@NonNull File source, @NonNull Options options) {
|
||||
try (InputStream inputStream = createEncryptedInputStream(secret, source)) {
|
||||
return gifDecoder.handles(inputStream, options);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Resource<GifDrawable> decode(@NonNull File source, int width, int height, @NonNull Options options) throws IOException {
|
||||
Log.i(TAG, "Encrypted GIF cache decoder running...");
|
||||
try (InputStream inputStream = createEncryptedInputStream(secret, source)) {
|
||||
return gifDecoder.decode(inputStream, width, height, options);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user