Revision e4237c21
Added by Leszek Koltunski over 9 years ago
| src/main/java/org/distorted/examples/save/SaveActivity.java | ||
|---|---|---|
| 57 | 57 |
@Override |
| 58 | 58 |
protected void onPause() |
| 59 | 59 |
{
|
| 60 |
SaveRenderer.stopThread(); |
|
| 61 |
|
|
| 60 | 62 |
GLSurfaceView mView = (GLSurfaceView) this.findViewById(R.id.saveSurfaceView); |
| 61 | 63 |
mView.onPause(); |
| 62 | 64 |
|
| ... | ... | |
| 72 | 74 |
|
| 73 | 75 |
GLSurfaceView mView = (GLSurfaceView) this.findViewById(R.id.saveSurfaceView); |
| 74 | 76 |
mView.onResume(); |
| 77 |
|
|
| 78 |
SaveRenderer.startThread(this); |
|
| 75 | 79 |
} |
| 76 | 80 |
|
| 77 | 81 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
| src/main/java/org/distorted/examples/save/SaveRenderer.java | ||
|---|---|---|
| 22 | 22 |
import java.io.File; |
| 23 | 23 |
import java.io.IOException; |
| 24 | 24 |
import java.io.InputStream; |
| 25 |
import java.util.Vector; |
|
| 25 |
import java.nio.ByteBuffer; |
|
| 26 |
import java.nio.ByteOrder; |
|
| 26 | 27 |
|
| 27 | 28 |
import javax.microedition.khronos.egl.EGLConfig; |
| 28 | 29 |
import javax.microedition.khronos.opengles.GL10; |
| ... | ... | |
| 31 | 32 |
|
| 32 | 33 |
import org.distorted.library.Distorted; |
| 33 | 34 |
import org.distorted.library.DistortedBitmap; |
| 34 |
import org.distorted.library.message.EffectListener; |
|
| 35 |
import org.distorted.library.message.EffectMessage; |
|
| 36 | 35 |
import org.distorted.library.EffectTypes; |
| 37 | 36 |
import org.distorted.library.type.Float1D; |
| 38 | 37 |
import org.distorted.library.type.Float2D; |
| ... | ... | |
| 45 | 44 |
import android.opengl.GLES20; |
| 46 | 45 |
import android.opengl.GLSurfaceView; |
| 47 | 46 |
import android.os.Environment; |
| 48 |
import android.widget.Toast; |
|
| 49 | 47 |
|
| 50 | 48 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
| 51 | 49 |
|
| 52 |
class SaveRenderer implements GLSurfaceView.Renderer ,EffectListener
|
|
| 50 |
class SaveRenderer implements GLSurfaceView.Renderer |
|
| 53 | 51 |
{
|
| 54 | 52 |
private GLSurfaceView mView; |
| 55 | 53 |
private static DistortedBitmap mGirl; |
| ... | ... | |
| 61 | 59 |
private int bmpHeight, bmpWidth; |
| 62 | 60 |
private static int scrHeight, scrWidth; |
| 63 | 61 |
private static float boobsSink; |
| 62 |
private static boolean isSaving = false; |
|
| 63 |
private static String mPath; |
|
| 64 | 64 |
|
| 65 |
private static class SaveInfo |
|
| 66 |
{
|
|
| 67 |
private long id; |
|
| 68 |
private String path; |
|
| 69 |
|
|
| 70 |
SaveInfo(long id, String path) |
|
| 71 |
{
|
|
| 72 |
this.id = id; |
|
| 73 |
this.path = path; |
|
| 74 |
} |
|
| 75 |
} |
|
| 76 |
|
|
| 77 |
private static Vector<SaveInfo> mSaveInfo; // Vector keeping all the information about the PATH |
|
| 78 |
// where current 'SAVE' effects are saving to and also |
|
| 79 |
// the IDs of the effects. |
|
| 80 |
// We need to have a whole Vector because there can be |
|
| 81 |
// many concurrent 'SAVE' operations (click 'SAVE', |
|
| 82 |
// don't wait for the Toast to appear, immediately |
|
| 83 |
// click again! ) |
|
| 65 |
private static SaveWorkerThread mWorkerThread =null; |
|
| 84 | 66 |
|
| 85 | 67 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
| 86 | 68 |
|
| ... | ... | |
| 101 | 83 |
diSink.setCount(0.5f); |
| 102 | 84 |
diSink.setDuration(0); |
| 103 | 85 |
diSink.add(s0); |
| 104 |
|
|
| 105 |
mSaveInfo = new Vector<>(); |
|
| 106 | 86 |
} |
| 107 | 87 |
|
| 108 | 88 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
| 109 | 89 |
|
| 110 |
public static void setSize(float s)
|
|
| 90 |
public static void startThread(Activity act)
|
|
| 111 | 91 |
{
|
| 112 |
boobsSink = s; |
|
| 113 |
s0.set(boobsSink); |
|
| 92 |
if( mWorkerThread ==null ) mWorkerThread = new SaveWorkerThread(); |
|
| 93 |
|
|
| 94 |
mWorkerThread.startSending(act); |
|
| 114 | 95 |
} |
| 115 | 96 |
|
| 116 | 97 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
| 117 | 98 |
|
| 118 |
public static void Save()
|
|
| 99 |
public static void stopThread()
|
|
| 119 | 100 |
{
|
| 120 |
File file;
|
|
| 121 |
int lowestNotFound = 1;
|
|
| 122 |
String path;
|
|
| 101 |
mWorkerThread.stopSending();
|
|
| 102 |
mWorkerThread = null;
|
|
| 103 |
}
|
|
| 123 | 104 |
|
| 124 |
while(true) |
|
| 125 |
{
|
|
| 126 |
path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/girl" + lowestNotFound +".png"; |
|
| 127 |
file = new File(path); |
|
| 105 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 128 | 106 |
|
| 129 |
if( !file.exists() ) |
|
| 130 |
{
|
|
| 131 |
long id= mGirl.savePNG(file.getAbsolutePath(),0,0,scrWidth,scrHeight); |
|
| 132 |
mSaveInfo.add(new SaveInfo(id,path)); |
|
| 133 |
break; |
|
| 134 |
} |
|
| 135 |
lowestNotFound++; |
|
| 136 |
} |
|
| 107 |
public static void setSize(float s) |
|
| 108 |
{
|
|
| 109 |
boobsSink = s; |
|
| 110 |
s0.set(boobsSink); |
|
| 137 | 111 |
} |
| 138 | 112 |
|
| 139 | 113 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
| 140 |
// the library sending messages to us. This does not run on this UI thread! |
|
| 141 | 114 |
|
| 142 |
public void effectMessage(final EffectMessage em, final long effectID, final int effectName, final long bitmapID, final String message)
|
|
| 115 |
public static void Save()
|
|
| 143 | 116 |
{
|
| 144 |
int numSaves = mSaveInfo.size(); |
|
| 145 |
int found = -1; |
|
| 146 |
|
|
| 147 |
for(int i=0; i<numSaves; i++) // doesn't need to be synchronized with Save() |
|
| 148 |
{ // because over there we only add() and here we only
|
|
| 149 |
if( effectID == mSaveInfo.get(i).id ) // remove() - and even if we add() in between of |
|
| 150 |
{ // computing the 'numSaves' above and this loop,
|
|
| 151 |
found = i; // it still doesn't matter :) |
|
| 152 |
break; |
|
| 153 |
} |
|
| 154 |
} |
|
| 155 |
|
|
| 156 |
if( found>=0 ) // the message really is about one of the 'SAVE' effects in execution now |
|
| 117 |
if( isSaving==false ) |
|
| 157 | 118 |
{
|
| 158 |
Activity act = (Activity)mView.getContext(); |
|
| 159 |
final String path = mSaveInfo.get(found).path; |
|
| 160 |
mSaveInfo.remove(found); |
|
| 119 |
isSaving = true; |
|
| 161 | 120 |
|
| 162 |
switch(em) |
|
| 121 |
File file; |
|
| 122 |
int lowestNotFound = 1; |
|
| 123 |
|
|
| 124 |
while(true) |
|
| 163 | 125 |
{
|
| 164 |
case EFFECT_FINISHED: act.runOnUiThread(new Runnable() |
|
| 165 |
{
|
|
| 166 |
public void run() |
|
| 167 |
{
|
|
| 168 |
Toast.makeText(mView.getContext(), |
|
| 169 |
"Saving to \n\n"+path+"\n\nsuccessful" , |
|
| 170 |
Toast.LENGTH_LONG).show(); |
|
| 171 |
} |
|
| 172 |
}); |
|
| 173 |
break; |
|
| 174 |
|
|
| 175 |
case EFFECT_FAILED : act.runOnUiThread(new Runnable() |
|
| 176 |
{
|
|
| 177 |
public void run() |
|
| 178 |
{
|
|
| 179 |
Toast.makeText(mView.getContext(), |
|
| 180 |
"Saving to \n\n"+path+"\n\n failed: "+"\n\n"+message , |
|
| 181 |
Toast.LENGTH_LONG).show(); |
|
| 182 |
} |
|
| 183 |
}); |
|
| 184 |
break; |
|
| 185 |
|
|
| 186 |
default : break; |
|
| 126 |
mPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/girl" + lowestNotFound +".png"; |
|
| 127 |
file = new File(mPath); |
|
| 128 |
|
|
| 129 |
if( !file.exists() ) break; |
|
| 130 |
|
|
| 131 |
lowestNotFound++; |
|
| 187 | 132 |
} |
| 188 | 133 |
} |
| 189 | 134 |
} |
| ... | ... | |
| 196 | 141 |
GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); |
| 197 | 142 |
|
| 198 | 143 |
mGirl.draw(System.currentTimeMillis()); |
| 144 |
|
|
| 145 |
if( isSaving ) // we should save the buffer to location pointed at by mPath |
|
| 146 |
{
|
|
| 147 |
ByteBuffer buf = ByteBuffer.allocateDirect( scrWidth*scrHeight*4 ); |
|
| 148 |
buf.order(ByteOrder.LITTLE_ENDIAN); |
|
| 149 |
GLES20.glReadPixels( 0, 0, scrWidth, scrHeight , GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf); |
|
| 150 |
|
|
| 151 |
mWorkerThread.newBuffer(buf,scrWidth,scrHeight,mPath); |
|
| 152 |
|
|
| 153 |
isSaving = false; |
|
| 154 |
} |
|
| 199 | 155 |
} |
| 200 | 156 |
|
| 201 | 157 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
| ... | ... | |
| 247 | 203 |
bmpWidth = bitmap.getWidth(); |
| 248 | 204 |
|
| 249 | 205 |
mGirl = new DistortedBitmap(bitmap, 30); |
| 250 |
mGirl.addEventListener(this); |
|
| 251 | 206 |
|
| 252 | 207 |
mGirl.sink( diSink, sinkRegion, pLeft ); |
| 253 | 208 |
mGirl.sink( diSink, sinkRegion, pRight); |
| src/main/java/org/distorted/examples/save/SaveWorkerThread.java | ||
|---|---|---|
| 1 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 2 |
// Copyright 2016 Leszek Koltunski // |
|
| 3 |
// // |
|
| 4 |
// This file is part of Distorted. // |
|
| 5 |
// // |
|
| 6 |
// Distorted is free software: you can redistribute it and/or modify // |
|
| 7 |
// it under the terms of the GNU General Public License as published by // |
|
| 8 |
// the Free Software Foundation, either version 2 of the License, or // |
|
| 9 |
// (at your option) any later version. // |
|
| 10 |
// // |
|
| 11 |
// Distorted is distributed in the hope that it will be useful, // |
|
| 12 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of // |
|
| 13 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // |
|
| 14 |
// GNU General Public License for more details. // |
|
| 15 |
// // |
|
| 16 |
// You should have received a copy of the GNU General Public License // |
|
| 17 |
// along with Distorted. If not, see <http://www.gnu.org/licenses/>. // |
|
| 18 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 19 |
|
|
| 20 |
package org.distorted.examples.save; |
|
| 21 |
|
|
| 22 |
import android.app.Activity; |
|
| 23 |
import android.graphics.Bitmap; |
|
| 24 |
import android.widget.Toast; |
|
| 25 |
|
|
| 26 |
import java.io.BufferedOutputStream; |
|
| 27 |
import java.io.FileOutputStream; |
|
| 28 |
import java.io.IOException; |
|
| 29 |
import java.lang.ref.WeakReference; |
|
| 30 |
import java.nio.ByteBuffer; |
|
| 31 |
import java.util.Vector; |
|
| 32 |
|
|
| 33 |
public class SaveWorkerThread extends Thread |
|
| 34 |
{
|
|
| 35 |
private static Vector<WorkLoad> mBuffers; |
|
| 36 |
private static SaveWorkerThread mThis=null; |
|
| 37 |
private static volatile boolean mPaused; |
|
| 38 |
private static WeakReference<Activity> mWeakAct; |
|
| 39 |
|
|
| 40 |
private static class WorkLoad |
|
| 41 |
{
|
|
| 42 |
ByteBuffer buffer; |
|
| 43 |
int width; |
|
| 44 |
int height; |
|
| 45 |
String filename; |
|
| 46 |
|
|
| 47 |
WorkLoad(ByteBuffer buf, int w, int h, String name) |
|
| 48 |
{
|
|
| 49 |
buffer = buf; |
|
| 50 |
width = w; |
|
| 51 |
height = h; |
|
| 52 |
filename = name; |
|
| 53 |
} |
|
| 54 |
} |
|
| 55 |
|
|
| 56 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 57 |
|
|
| 58 |
public SaveWorkerThread() |
|
| 59 |
{
|
|
| 60 |
} |
|
| 61 |
|
|
| 62 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 63 |
|
|
| 64 |
static void startSending(Activity act) |
|
| 65 |
{
|
|
| 66 |
mWeakAct = new WeakReference(act); |
|
| 67 |
|
|
| 68 |
mPaused = false; |
|
| 69 |
|
|
| 70 |
if( mThis==null ) |
|
| 71 |
{
|
|
| 72 |
mBuffers = new Vector<>(); |
|
| 73 |
mThis = new SaveWorkerThread(); |
|
| 74 |
mThis.start(); |
|
| 75 |
} |
|
| 76 |
else |
|
| 77 |
{
|
|
| 78 |
synchronized(mThis) |
|
| 79 |
{
|
|
| 80 |
mThis.notify(); |
|
| 81 |
} |
|
| 82 |
} |
|
| 83 |
} |
|
| 84 |
|
|
| 85 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 86 |
|
|
| 87 |
static void stopSending() |
|
| 88 |
{
|
|
| 89 |
mPaused = true; |
|
| 90 |
} |
|
| 91 |
|
|
| 92 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 93 |
|
|
| 94 |
public void run() |
|
| 95 |
{
|
|
| 96 |
WorkLoad load; |
|
| 97 |
|
|
| 98 |
while(true) |
|
| 99 |
{
|
|
| 100 |
if( mBuffers.size()>0 ) |
|
| 101 |
{
|
|
| 102 |
load = mBuffers.remove(0); |
|
| 103 |
process(load); |
|
| 104 |
} |
|
| 105 |
|
|
| 106 |
if( mPaused ) |
|
| 107 |
{
|
|
| 108 |
synchronized(mThis) |
|
| 109 |
{
|
|
| 110 |
try { mThis.wait(); }
|
|
| 111 |
catch(InterruptedException ex) { }
|
|
| 112 |
} |
|
| 113 |
} |
|
| 114 |
} |
|
| 115 |
} |
|
| 116 |
|
|
| 117 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 118 |
|
|
| 119 |
static void newBuffer(ByteBuffer buffer, int width, int height, String filename) |
|
| 120 |
{
|
|
| 121 |
WorkLoad load = new WorkLoad(buffer,width,height,filename); |
|
| 122 |
mBuffers.add(load); |
|
| 123 |
} |
|
| 124 |
|
|
| 125 |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 126 |
|
|
| 127 |
private void process(WorkLoad load) |
|
| 128 |
{
|
|
| 129 |
ByteBuffer buf = load.buffer; |
|
| 130 |
int width = load.width; |
|
| 131 |
int height = load.height; |
|
| 132 |
final String filename = load.filename; |
|
| 133 |
|
|
| 134 |
byte[] tmp1 = new byte[width*4]; |
|
| 135 |
byte[] tmp2 = new byte[width*4]; |
|
| 136 |
|
|
| 137 |
for(int i=0; i<height/2; i++) |
|
| 138 |
{
|
|
| 139 |
buf.position(( i)*width*4); // GL uses a coordinate system from mathematics; i.e. |
|
| 140 |
buf.get(tmp1); // (0,0) is in the lower-left corner. 2D stuff has |
|
| 141 |
buf.position((height-1-i)*width*4); // the origin on the upper-left corner; we have to flip |
|
| 142 |
buf.get(tmp2); // out bitmap upside down! |
|
| 143 |
|
|
| 144 |
buf.position(( i)*width*4); |
|
| 145 |
buf.put(tmp2); |
|
| 146 |
buf.position((height-1-i)*width*4); |
|
| 147 |
buf.put(tmp1); |
|
| 148 |
} |
|
| 149 |
|
|
| 150 |
buf.rewind(); |
|
| 151 |
BufferedOutputStream bos =null; |
|
| 152 |
final Activity act = mWeakAct.get(); |
|
| 153 |
|
|
| 154 |
try |
|
| 155 |
{
|
|
| 156 |
bos = new BufferedOutputStream(new FileOutputStream(filename)); |
|
| 157 |
Bitmap bmp = Bitmap.createBitmap( width, height, Bitmap.Config.ARGB_8888); |
|
| 158 |
bmp.copyPixelsFromBuffer(buf); |
|
| 159 |
bmp.compress(Bitmap.CompressFormat.PNG, 90, bos); |
|
| 160 |
bmp.recycle(); |
|
| 161 |
|
|
| 162 |
act.runOnUiThread(new Runnable() |
|
| 163 |
{
|
|
| 164 |
public void run() |
|
| 165 |
{
|
|
| 166 |
Toast.makeText(act, |
|
| 167 |
"Saving to \n\n"+filename+"\n\nsuccessful" , |
|
| 168 |
Toast.LENGTH_LONG).show(); |
|
| 169 |
} |
|
| 170 |
}); |
|
| 171 |
} |
|
| 172 |
catch(final Exception ex) |
|
| 173 |
{
|
|
| 174 |
act.runOnUiThread(new Runnable() |
|
| 175 |
{
|
|
| 176 |
public void run() |
|
| 177 |
{
|
|
| 178 |
Toast.makeText(act, |
|
| 179 |
"Saving to \n\n"+filename+"\n\n failed: "+"\n\n"+ex.getMessage() , |
|
| 180 |
Toast.LENGTH_LONG).show(); |
|
| 181 |
} |
|
| 182 |
}); |
|
| 183 |
} |
|
| 184 |
finally |
|
| 185 |
{
|
|
| 186 |
if(bos!=null) |
|
| 187 |
{
|
|
| 188 |
try { bos.close(); }
|
|
| 189 |
catch(IOException io) {}
|
|
| 190 |
} |
|
| 191 |
} |
|
| 192 |
} |
|
| 193 |
} |
|
Also available in: Unified diff
Move the 'Save' effect to the Application and remove the 'OTHER' effect queue