Project

General

Profile

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

magiccube / src / main / java / org / distorted / bandaged / BandagedCreatorWorkerThread.java @ f404152d

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2022 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Distorted.                                                               //
5
//                                                                                               //
6
// Magic Cube is proprietary software licensed under an EULA which you should have received      //
7
// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
8
///////////////////////////////////////////////////////////////////////////////////////////////////
9

    
10
package org.distorted.bandaged;
11

    
12
import android.app.Activity;
13
import android.content.Intent;
14
import android.graphics.Bitmap;
15
import android.net.Uri;
16
import android.widget.Toast;
17

    
18
import java.io.BufferedOutputStream;
19
import java.io.File;
20
import java.io.FileOutputStream;
21
import java.io.IOException;
22
import java.lang.ref.WeakReference;
23
import java.nio.ByteBuffer;
24
import java.nio.ByteOrder;
25
import java.util.Vector;
26

    
27
///////////////////////////////////////////////////////////////////////////////////////////////////
28

    
29
class BandagedCreatorWorkerThread extends Thread
30
  {
31
  private static Vector<WorkLoad> mBuffers;
32
  private static BandagedCreatorWorkerThread mThis=null;
33
  private static WeakReference<Activity> mWeakAct;
34

    
35
  private static class WorkLoad
36
    {
37
    ByteBuffer buffer;
38
    int width;
39
    int height;
40
    int numColors;
41
    String filename;
42

    
43
    WorkLoad(ByteBuffer buf, int w, int h, int n, String name)
44
      {
45
      buffer   = buf;
46
      width    = w;
47
      height   = h;
48
      numColors= n;
49
      filename = name;
50
      }
51
    }
52

    
53
  private int mCreatedBufSize;
54

    
55
///////////////////////////////////////////////////////////////////////////////////////////////////
56

    
57
  private BandagedCreatorWorkerThread()
58
    {
59
    }
60

    
61
///////////////////////////////////////////////////////////////////////////////////////////////////
62

    
63
  static void create(Activity act)
64
    {
65
    mWeakAct = new WeakReference<>(act);
66

    
67
    if( mThis==null )
68
      {
69
      mBuffers = new Vector<>();
70
      mThis = new BandagedCreatorWorkerThread();
71
      mThis.start();
72
      }
73
    }
74

    
75
///////////////////////////////////////////////////////////////////////////////////////////////////
76

    
77
  public void run()
78
    {
79
    WorkLoad load;
80

    
81
    while(true)
82
      {
83
      synchronized(mThis)
84
        {
85
        while( mBuffers.size()>0 )
86
          {
87
          load = mBuffers.remove(0);
88
          process(load);
89
          }
90

    
91
        try  { mThis.wait(); }
92
        catch(InterruptedException ignored) { }
93
        }
94
      }
95
    }
96

    
97
///////////////////////////////////////////////////////////////////////////////////////////////////
98

    
99
  static void newBuffer(ByteBuffer buffer, int width, int height, int numColors, String filename)
100
    {
101
    synchronized(mThis)
102
      {
103
      WorkLoad load = new WorkLoad(buffer,width,height,numColors,filename);
104
      mBuffers.add(load);
105
      mThis.notify();
106
      }
107
    }
108

    
109
///////////////////////////////////////////////////////////////////////////////////////////////////
110

    
111
  private int firstBlackPixel(byte[] tmp, int width)
112
    {
113
    for(int i=0; i<width; i++)
114
      {
115
      if( tmp[4*i]==0 && tmp[4*i+3]==-1 ) return i;
116
      }
117

    
118
    return -1;
119
    }
120

    
121
///////////////////////////////////////////////////////////////////////////////////////////////////
122

    
123
  private int lastBlackPixel(byte[] tmp, int width)
124
    {
125
    for(int i=width-1; i>=0; i--)
126
      {
127
      if( tmp[4*i]==0 && tmp[4*i+3]==-1 ) return i;
128
      }
129

    
130
    return -1;
131
    }
132

    
133
///////////////////////////////////////////////////////////////////////////////////////////////////
134

    
135
  private int computeFirstRow(ByteBuffer buf, byte[] tmp, int width, int height)
136
    {
137
    int wBytes = 4*width;
138

    
139
    for(int i=0; i<height; i++)
140
      {
141
      buf.position(i*wBytes);
142
      buf.get(tmp);
143

    
144
      if( firstBlackPixel(tmp,width)>=0 ) return i;
145
      }
146

    
147
    return 0;
148
    }
149

    
150
///////////////////////////////////////////////////////////////////////////////////////////////////
151

    
152
  private int computeLastRow(ByteBuffer buf, byte[] tmp, int width, int height)
153
    {
154
    int wBytes = 4*width;
155

    
156
    for(int i=height-1; i>=0; i--)
157
      {
158
      buf.position(i*wBytes);
159
      buf.get(tmp);
160

    
161
      if( firstBlackPixel(tmp,width)>=0 ) return i;
162
      }
163

    
164
    return width-1;
165
    }
166

    
167
///////////////////////////////////////////////////////////////////////////////////////////////////
168

    
169
  private int computeLeftColumn(ByteBuffer buf, byte[] tmp, int width, int firstrow, int lastrow)
170
    {
171
    int wBytes = 4*width;
172
    int currentBest = width;
173

    
174
    for(int i=firstrow; i<lastrow; i++)
175
      {
176
      buf.position(i*wBytes);
177
      buf.get(tmp);
178

    
179
      int pixel = firstBlackPixel(tmp,width);
180
      if( pixel>=0 && pixel<currentBest ) currentBest = pixel;
181
      }
182

    
183
    return currentBest;
184
    }
185

    
186
///////////////////////////////////////////////////////////////////////////////////////////////////
187

    
188
  private int computeRightColumn(ByteBuffer buf, byte[] tmp, int width, int firstrow, int lastrow)
189
    {
190
    int wBytes = 4*width;
191
    int currentBest = 0;
192

    
193
    for(int i=firstrow; i<lastrow; i++)
194
      {
195
      buf.position(i*wBytes);
196
      buf.get(tmp);
197

    
198
      int pixel = lastBlackPixel(tmp,width);
199
      if( pixel>=0 && pixel>currentBest ) currentBest = pixel;
200
      }
201

    
202
    return currentBest;
203
    }
204

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

    
212
  private ByteBuffer adjustBuffer(ByteBuffer input, byte[] tmp, int width, int height)
213
    {
214
    int firstRow   = computeFirstRow(input,tmp,width,height);
215
    int lastRow    = computeLastRow(input,tmp,width,height);
216
    int leftColumn = computeLeftColumn(input,tmp,width,firstRow,lastRow);
217
    int rightColumn= computeRightColumn(input,tmp,width,firstRow,lastRow);
218

    
219
    int rowHeight = lastRow-firstRow;
220
    int colWidth  = rightColumn-leftColumn;
221
    int size      = Math.max(rowHeight,colWidth);
222
    size = (int)(1.1f*size);
223
    int half = size/2;
224
    int centerX = (leftColumn+rightColumn)/2;
225
    int centerY = (firstRow+lastRow)/2;
226
    int sL=size, sR=size, sT=size, sB=size;
227

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

    
264
    for(int i=0; i<mCreatedBufSize; i++)
265
      {
266
      input.position((startRow-i)*4*width + 4*distR );
267
      input.get(tmp,0,wBytes);
268
      output.position(i*wBytes);
269
      output.put(tmp,0,wBytes);
270
      }
271

    
272
    output.rewind();
273
    return output;
274
    }
275

    
276
///////////////////////////////////////////////////////////////////////////////////////////////////
277

    
278
  private void process(WorkLoad load)
279
    {
280
    int width  = load.width;
281
    int height = load.height;
282

    
283
    byte[] tmp = new byte[4*width];
284
    ByteBuffer bufSquare = adjustBuffer(load.buffer,tmp,width,height);
285
    final String filename = load.filename;
286
    BufferedOutputStream bos =null;
287
    final Activity act = mWeakAct.get();
288

    
289
    try
290
      {
291
      bos = new BufferedOutputStream(new FileOutputStream(filename));
292
      Bitmap bmp = Bitmap.createBitmap( mCreatedBufSize, mCreatedBufSize, Bitmap.Config.ARGB_8888);
293
      bmp.copyPixelsFromBuffer(bufSquare);
294
      bmp.compress(Bitmap.CompressFormat.PNG, 90, bos);
295

    
296
      BandagedCreatorActivity cact = (BandagedCreatorActivity)act;
297
      cact.iconCreationDone(bmp);
298
      }
299
    catch(final Exception ex)
300
      {
301
      act.runOnUiThread(new Runnable()
302
        {
303
        public void run()
304
          {
305
          Toast.makeText(act,
306
              "Saving to \n\n"+filename+"\n\n failed: "+"\n\n"+ex.getMessage() ,
307
              Toast.LENGTH_LONG).show();
308
          }
309
        });
310
      }
311
    finally
312
      {
313
      if(bos!=null)
314
        {
315
        try { bos.close(); }
316
        catch(IOException ignored) {}
317
        }
318
      }
319

    
320
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
321
    File f = new File(filename);
322
    Uri contentUri = Uri.fromFile(f);
323
    mediaScanIntent.setData(contentUri);
324
    act.sendBroadcast(mediaScanIntent);
325
    }
326
  }
(7-7/14)