parent
80d0ba31ca
commit
8d6f1341f1
|
@ -83,7 +83,7 @@ final class Bisect {
|
|||
inBoundsValue = nextValueToTry;
|
||||
|
||||
// if first success or closer to out of bounds than the current closest
|
||||
if (!haveResult || Math.abs(nextValueToTry) < Math.abs(successValue)) {
|
||||
if (!haveResult || Math.abs(nextValueToTry - outOfBoundsValue) < Math.abs(successValue - outOfBoundsValue)) {
|
||||
haveResult = true;
|
||||
successValue = nextValueToTry;
|
||||
closestSuccessful.set(elementMatrix);
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.graphics.PointF;
|
|||
import android.graphics.RectF;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
@ -524,7 +525,7 @@ public final class EditorModel implements Parcelable, RendererContext.Ready {
|
|||
* Blocking render of the model.
|
||||
*/
|
||||
@WorkerThread
|
||||
public Bitmap render(@NonNull Context context) {
|
||||
public @NonNull Bitmap render(@NonNull Context context) {
|
||||
EditorElement image = editorElementHierarchy.getFlipRotate();
|
||||
RectF cropRect = editorElementHierarchy.getCropRect();
|
||||
Point outputSize = getOutputSize();
|
||||
|
@ -573,11 +574,15 @@ public final class EditorModel implements Parcelable, RendererContext.Ready {
|
|||
@Override
|
||||
public void onReady(@NonNull Renderer renderer, @Nullable Matrix cropMatrix, @Nullable Point size) {
|
||||
if (cropMatrix != null && size != null && isRendererOfMainImage(renderer)) {
|
||||
boolean changedBefore = isChanged();
|
||||
Matrix imageCropMatrix = editorElementHierarchy.getImageCrop().getLocalMatrix();
|
||||
this.size.set(size.x, size.y);
|
||||
if (imageCropMatrix.isIdentity()) {
|
||||
imageCropMatrix.set(cropMatrix);
|
||||
editorElementHierarchy.doneCrop(visibleViewPort, null);
|
||||
if (!changedBefore) {
|
||||
undoRedoStacks.clear(editorElementHierarchy.getRoot());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.thoughtcrime.securesms.mediasend;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.PorterDuff;
|
||||
|
@ -9,14 +8,6 @@ import android.graphics.Rect;
|
|||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.view.ContextThemeWrapper;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.KeyEvent;
|
||||
|
@ -28,6 +19,16 @@ import android.view.WindowManager;
|
|||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.view.ContextThemeWrapper;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.TransportOption;
|
||||
import org.thoughtcrime.securesms.components.ComposeText;
|
||||
|
@ -425,7 +426,7 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
|||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private void processMedia(@NonNull List<Media> mediaList, @NonNull Map<Uri, Object> savedState) {
|
||||
Map<Media, ListenableFuture<Bitmap>> futures = new HashMap<>();
|
||||
Map<Media, EditorModel> modelsToRender = new HashMap<>();
|
||||
|
||||
for (Media media : mediaList) {
|
||||
Object state = savedState.get(media.getUri());
|
||||
|
@ -433,7 +434,7 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
|||
if (state instanceof ImageEditorFragment.Data) {
|
||||
EditorModel model = ((ImageEditorFragment.Data) state).readModel();
|
||||
if (model != null && model.isChanged()) {
|
||||
futures.put(media, render(requireContext(), model));
|
||||
modelsToRender.put(media, model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -461,28 +462,32 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
|||
|
||||
@Override
|
||||
protected List<Media> doInBackground(Void... voids) {
|
||||
Context context = requireContext();
|
||||
List<Media> updatedMedia = new ArrayList<>(mediaList.size());
|
||||
Context context = requireContext();
|
||||
List<Media> updatedMedia = new ArrayList<>(mediaList.size());
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
for (Media media : mediaList) {
|
||||
if (futures.containsKey(media)) {
|
||||
EditorModel modelToRender = modelsToRender.get(media);
|
||||
if (modelToRender != null) {
|
||||
Bitmap bitmap = modelToRender.render(context);
|
||||
try {
|
||||
Bitmap bitmap = futures.get(media).get();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos);
|
||||
outputStream.reset();
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outputStream);
|
||||
|
||||
Uri uri = BlobProvider.getInstance()
|
||||
.forData(baos.toByteArray())
|
||||
.forData(outputStream.toByteArray())
|
||||
.withMimeType(MediaUtil.IMAGE_JPEG)
|
||||
.createForSingleSessionOnDisk(context, e -> Log.w(TAG, "Failed to write to disk.", e));
|
||||
|
||||
Media updated = new Media(uri, MediaUtil.IMAGE_JPEG, media.getDate(), bitmap.getWidth(), bitmap.getHeight(), baos.size(), media.getBucketId(), media.getCaption());
|
||||
Media updated = new Media(uri, MediaUtil.IMAGE_JPEG, media.getDate(), bitmap.getWidth(), bitmap.getHeight(), outputStream.size(), media.getBucketId(), media.getCaption());
|
||||
|
||||
updatedMedia.add(updated);
|
||||
renderTimer.split("item");
|
||||
} catch (InterruptedException | ExecutionException | IOException e) {
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed to render image. Using base image.");
|
||||
updatedMedia.add(media);
|
||||
} finally {
|
||||
bitmap.recycle();
|
||||
}
|
||||
} else {
|
||||
updatedMedia.add(media);
|
||||
|
@ -503,14 +508,6 @@ public class MediaSendFragment extends Fragment implements ViewTreeObserver.OnGl
|
|||
}.execute();
|
||||
}
|
||||
|
||||
private static ListenableFuture<Bitmap> render(@NonNull Context context, @NonNull EditorModel model) {
|
||||
SettableFuture<Bitmap> future = new SettableFuture<>();
|
||||
|
||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> future.set(model.render(context)));
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
public void onRequestFullScreen(boolean fullScreen) {
|
||||
captionAndRail.setVisibility(fullScreen ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
|
|
@ -6,13 +6,15 @@ import android.graphics.Matrix;
|
|||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.request.target.SimpleTarget;
|
||||
import com.bumptech.glide.request.target.CustomTarget;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
|
||||
import org.thoughtcrime.securesms.imageeditor.Bounds;
|
||||
|
@ -31,6 +33,8 @@ import java.util.concurrent.ExecutionException;
|
|||
*/
|
||||
final class UriGlideRenderer implements Renderer {
|
||||
|
||||
private static final int PREVIEW_DIMENSION_LIMIT = 2048;
|
||||
|
||||
private final Uri imageUri;
|
||||
private final Paint paint = new Paint();
|
||||
private final Matrix imageProjectionMatrix = new Matrix();
|
||||
|
@ -57,7 +61,7 @@ final class UriGlideRenderer implements Renderer {
|
|||
if (getBitmap() == null) {
|
||||
if (rendererContext.isBlockingLoad()) {
|
||||
try {
|
||||
Bitmap bitmap = getBitmapGlideRequest(rendererContext.context).submit().get();
|
||||
Bitmap bitmap = getBitmapGlideRequest(rendererContext.context, false).submit().get();
|
||||
setBitmap(rendererContext, bitmap);
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
@ -65,11 +69,16 @@ final class UriGlideRenderer implements Renderer {
|
|||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
getBitmapGlideRequest(rendererContext.context).into(new SimpleTarget<Bitmap>() {
|
||||
getBitmapGlideRequest(rendererContext.context, true).into(new CustomTarget<Bitmap>() {
|
||||
@Override
|
||||
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
|
||||
setBitmap(rendererContext, resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadCleared(@Nullable Drawable placeholder) {
|
||||
bitmap = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -96,11 +105,19 @@ final class UriGlideRenderer implements Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
private GlideRequest<Bitmap> getBitmapGlideRequest(@NonNull Context context) {
|
||||
private GlideRequest<Bitmap> getBitmapGlideRequest(@NonNull Context context, boolean preview) {
|
||||
int width = this.maxWidth;
|
||||
int height = this.maxHeight;
|
||||
|
||||
if (preview) {
|
||||
width = Math.min(width, PREVIEW_DIMENSION_LIMIT);
|
||||
height = Math.min(height, PREVIEW_DIMENSION_LIMIT);
|
||||
}
|
||||
|
||||
return GlideApp.with(context)
|
||||
.asBitmap()
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.override(maxWidth, maxHeight)
|
||||
.override(width, height)
|
||||
.centerInside()
|
||||
.load(decryptable ? new DecryptableStreamUriLoader.DecryptableUri(imageUri) : imageUri);
|
||||
}
|
||||
|
@ -130,8 +147,11 @@ final class UriGlideRenderer implements Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Bitmap getBitmap() {
|
||||
/**
|
||||
* Always use this getter, as Bitmap is kept in Glide's LRUCache, so it could have been recycled
|
||||
* by Glide. If it has, or was never set, this method returns null.
|
||||
*/
|
||||
private @Nullable Bitmap getBitmap() {
|
||||
if (bitmap != null && bitmap.isRecycled()) {
|
||||
bitmap = null;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue