Project

General

Profile

Download (10.5 KB) Statistics
| Branch: | Tag: | Revision:

magiccube / src / main / java / org / distorted / bandaged / BandagedCreatorWorkerThread.java @ 39176a1f

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2022 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.bandaged;
21

    
22
import android.app.Activity;
23
import android.content.Intent;
24
import android.graphics.Bitmap;
25
import android.net.Uri;
26
import android.widget.Toast;
27

    
28
import java.io.BufferedOutputStream;
29
import java.io.File;
30
import java.io.FileOutputStream;
31
import java.io.IOException;
32
import java.lang.ref.WeakReference;
33
import java.nio.ByteBuffer;
34
import java.nio.ByteOrder;
35
import java.util.Vector;
36

    
37
///////////////////////////////////////////////////////////////////////////////////////////////////
38

    
39
class BandagedCreatorWorkerThread extends Thread
40
  {
41
  private static Vector<WorkLoad> mBuffers;
42
  private static BandagedCreatorWorkerThread mThis=null;
43
  private static WeakReference<Activity> mWeakAct;
44

    
45
  private static class WorkLoad
46
    {
47
    ByteBuffer buffer;
48
    int width;
49
    int height;
50
    int numColors;
51
    String filename;
52

    
53
    WorkLoad(ByteBuffer buf, int w, int h, int n, String name)
54
      {
55
      buffer   = buf;
56
      width    = w;
57
      height   = h;
58
      numColors= n;
59
      filename = name;
60
      }
61
    }
62

    
63
  private int mCreatedBufSize;
64

    
65
///////////////////////////////////////////////////////////////////////////////////////////////////
66

    
67
  private BandagedCreatorWorkerThread()
68
    {
69
    }
70

    
71
///////////////////////////////////////////////////////////////////////////////////////////////////
72

    
73
  static void create(Activity act)
74
    {
75
    mWeakAct = new WeakReference<>(act);
76

    
77
    if( mThis==null )
78
      {
79
      mBuffers = new Vector<>();
80
      mThis = new BandagedCreatorWorkerThread();
81
      mThis.start();
82
      }
83
    }
84

    
85
///////////////////////////////////////////////////////////////////////////////////////////////////
86

    
87
  public void run()
88
    {
89
    WorkLoad load;
90

    
91
    while(true)
92
      {
93
      synchronized(mThis)
94
        {
95
        while( mBuffers.size()>0 )
96
          {
97
          load = mBuffers.remove(0);
98
          process(load);
99
          }
100

    
101
        try  { mThis.wait(); }
102
        catch(InterruptedException ex) { }
103
        }
104
      }
105
    }
106

    
107
///////////////////////////////////////////////////////////////////////////////////////////////////
108

    
109
  static void newBuffer(ByteBuffer buffer, int width, int height, int numColors, String filename)
110
    {
111
    synchronized(mThis)
112
      {
113
      WorkLoad load = new WorkLoad(buffer,width,height,numColors,filename);
114
      mBuffers.add(load);
115
      mThis.notify();
116
      }
117
    }
118

    
119
///////////////////////////////////////////////////////////////////////////////////////////////////
120

    
121
  private int firstBlackPixel(byte[] tmp, int width)
122
    {
123
    for(int i=0; i<width; i++)
124
      {
125
      if( tmp[4*i]==0 && tmp[4*i+3]==-1 ) return i;
126
      }
127

    
128
    return -1;
129
    }
130

    
131
///////////////////////////////////////////////////////////////////////////////////////////////////
132

    
133
  private int lastBlackPixel(byte[] tmp, int width)
134
    {
135
    for(int i=width-1; i>=0; i--)
136
      {
137
      if( tmp[4*i]==0 && tmp[4*i+3]==-1 ) return i;
138
      }
139

    
140
    return -1;
141
    }
142

    
143
///////////////////////////////////////////////////////////////////////////////////////////////////
144

    
145
  private int computeFirstRow(ByteBuffer buf, byte[] tmp, int width, int height)
146
    {
147
    int wBytes = 4*width;
148

    
149
    for(int i=0; i<height; i++)
150
      {
151
      buf.position(i*wBytes);
152
      buf.get(tmp);
153

    
154
      if( firstBlackPixel(tmp,width)>=0 ) return i;
155
      }
156

    
157
    return 0;
158
    }
159

    
160
///////////////////////////////////////////////////////////////////////////////////////////////////
161

    
162
  private int computeLastRow(ByteBuffer buf, byte[] tmp, int width, int height)
163
    {
164
    int wBytes = 4*width;
165

    
166
    for(int i=height-1; i>=0; i--)
167
      {
168
      buf.position(i*wBytes);
169
      buf.get(tmp);
170

    
171
      if( firstBlackPixel(tmp,width)>=0 ) return i;
172
      }
173

    
174
    return width-1;
175
    }
176

    
177
///////////////////////////////////////////////////////////////////////////////////////////////////
178

    
179
  private int computeLeftColumn(ByteBuffer buf, byte[] tmp, int width, int firstrow, int lastrow)
180
    {
181
    int wBytes = 4*width;
182
    int currentBest = width;
183

    
184
    for(int i=firstrow; i<lastrow; i++)
185
      {
186
      buf.position(i*wBytes);
187
      buf.get(tmp);
188

    
189
      int pixel = firstBlackPixel(tmp,width);
190
      if( pixel>=0 && pixel<currentBest ) currentBest = pixel;
191
      }
192

    
193
    return currentBest;
194
    }
195

    
196
///////////////////////////////////////////////////////////////////////////////////////////////////
197

    
198
  private int computeRightColumn(ByteBuffer buf, byte[] tmp, int width, int firstrow, int lastrow)
199
    {
200
    int wBytes = 4*width;
201
    int currentBest = 0;
202

    
203
    for(int i=firstrow; i<lastrow; i++)
204
      {
205
      buf.position(i*wBytes);
206
      buf.get(tmp);
207

    
208
      int pixel = lastBlackPixel(tmp,width);
209
      if( pixel>=0 && pixel>currentBest ) currentBest = pixel;
210
      }
211

    
212
    return currentBest;
213
    }
214

    
215
///////////////////////////////////////////////////////////////////////////////////////////////////
216
// GL uses a coordinate system from mathematics; i.e. (0,0) is in the lower-left corner. 2D stuff
217
// has the origin on the upper-left corner; we have to flip our bitmap upside down!
218
//
219
// We also need to figure out the topmost and bottommost rows where the object starts and cut out
220
// a square section from the 'input' so that the object is vertically centralized.
221

    
222
  private ByteBuffer adjustBuffer(ByteBuffer input, byte[] tmp, int width, int height)
223
    {
224
    int firstRow   = computeFirstRow(input,tmp,width,height);
225
    int lastRow    = computeLastRow(input,tmp,width,height);
226
    int leftColumn = computeLeftColumn(input,tmp,width,firstRow,lastRow);
227
    int rightColumn= computeRightColumn(input,tmp,width,firstRow,lastRow);
228

    
229
    int rowHeight = lastRow-firstRow;
230
    int colWidth  = rightColumn-leftColumn;
231
    int size      = Math.max(rowHeight,colWidth);
232
    size = (int)(1.1f*size);
233
    int half = size/2;
234
    int centerX = (leftColumn+rightColumn)/2;
235
    int centerY = (firstRow+lastRow)/2;
236
    int sL=size, sR=size, sT=size, sB=size;
237

    
238
    if( centerX-half<    0 )
239
      {
240
      android.util.Log.e("D", "buffer encroaches on the left!   centerX="+centerX+" centerY="+centerY+" size="+size);
241
      sL = 2*centerX;
242
      }
243
    if( centerX+half>width )
244
      {
245
      android.util.Log.e("D", "buffer encroaches on the right!  centerX="+centerX+" centerY="+centerY+" size="+size);
246
      sR = 2*(width-centerX);
247
      }
248
    if( centerY-half<    0 )
249
      {
250
      android.util.Log.e("D", "buffer encroaches on the top!    centerX="+centerX+" centerY="+centerY+" size="+size);
251
      sT = 2*centerY;
252
      }
253
    if( centerY+half>height)
254
      {
255
      android.util.Log.e("D", "buffer encroaches on the bottom! centerX="+centerX+" centerY="+centerY+" size="+size);
256
      sB = 2*(height-centerY);
257
      }
258
/*
259
    if( sL==size && sR==size && sT==size && sB==size )
260
      {
261
      android.util.Log.e("D", "EVERYTHING ALL RIGHT centerX="+centerX+" centerY="+centerY+" size="+size);
262
      }
263
*/
264
    int minH = Math.min(sL,sR);
265
    int minV = Math.min(sT,sB);
266
    mCreatedBufSize = Math.min(minH,minV);
267
    half = mCreatedBufSize/2;
268
    int wBytes = 4*mCreatedBufSize;
269
    ByteBuffer output = ByteBuffer.allocateDirect(wBytes*mCreatedBufSize);
270
    output.order(ByteOrder.LITTLE_ENDIAN);
271
    int startRow = centerY+half;
272
    int distR = width-centerX-half;
273

    
274
    for(int i=0; i<mCreatedBufSize; i++)
275
      {
276
      input.position((startRow-i)*4*width + 4*distR );
277
      input.get(tmp,0,wBytes);
278
      output.position(i*wBytes);
279
      output.put(tmp,0,wBytes);
280
      }
281

    
282
    output.rewind();
283
    return output;
284
    }
285

    
286
///////////////////////////////////////////////////////////////////////////////////////////////////
287

    
288
  private void process(WorkLoad load)
289
    {
290
    int width  = load.width;
291
    int height = load.height;
292

    
293
    byte[] tmp = new byte[4*width];
294
    ByteBuffer bufSquare = adjustBuffer(load.buffer,tmp,width,height);
295
    final String filename = load.filename;
296
    BufferedOutputStream bos =null;
297
    final Activity act = mWeakAct.get();
298

    
299
    try
300
      {
301
      bos = new BufferedOutputStream(new FileOutputStream(filename));
302
      Bitmap bmp = Bitmap.createBitmap( mCreatedBufSize, mCreatedBufSize, Bitmap.Config.ARGB_8888);
303
      bmp.copyPixelsFromBuffer(bufSquare);
304
      bmp.compress(Bitmap.CompressFormat.PNG, 90, bos);
305

    
306
      BandagedCreatorActivity cact = (BandagedCreatorActivity)act;
307
      cact.iconCreationDone(bmp);
308
      }
309
    catch(final Exception ex)
310
      {
311
      act.runOnUiThread(new Runnable()
312
        {
313
        public void run()
314
          {
315
          Toast.makeText(act,
316
              "Saving to \n\n"+filename+"\n\n failed: "+"\n\n"+ex.getMessage() ,
317
              Toast.LENGTH_LONG).show();
318
          }
319
        });
320
      }
321
    finally
322
      {
323
      if(bos!=null)
324
        {
325
        try { bos.close(); }
326
        catch(IOException io) {}
327
        }
328
      }
329

    
330
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
331
    File f = new File(filename);
332
    Uri contentUri = Uri.fromFile(f);
333
    mediaScanIntent.setData(contentUri);
334
    act.sendBroadcast(mediaScanIntent);
335
    }
336
  }
(7-7/13)