App
BitmapDisplayer.java
Go to the documentation of this file.
1 package mhr.appandroid.displayer;
2 
3 import mhr.appandroid.adapters.APDBitmap;
4 import mhr.appcore.bitmap.BitmapInfo;
5 import mhr.appcore.interfaces.PDBitmap;
6 import mhr.appcore.interfaces.PDDisplayer;
7 import android.graphics.Bitmap;
8 import android.graphics.Canvas;
9 import android.graphics.Paint;
10 import android.graphics.Rect;
11 import android.view.SurfaceHolder;
12 import android.view.SurfaceView;
13 import android.view.View;
14 
20 public class BitmapDisplayer implements Runnable, SurfaceHolder.Callback, PDDisplayer {
21 
22  //===== INTERFACES, CLASSES, ENUMS ==========================================================================================================//
23  //----- NON-PUBLIC --------------------------------------------------------------------------------------------------------------------------//
33  private class Lock {
34  public volatile boolean REDRAW_FLAG = false;
35  public volatile boolean EXIT_FLAG = false;
36  public volatile boolean CHANGE_INPUT_FLAG = false;
37  public volatile boolean OUTPUT_READY_FLAG = false;
38  public volatile boolean INPUT_READY_FLAG = false;
39  public volatile boolean POSITIONING_CHANGED = false;
40  public volatile boolean BUSY_FLAG = true;
41  }
42  //----- PUBLIC ------------------------------------------------------------------------------------------------------------------------------//
43 
44  //===== FIELDS ==============================================================================================================================//
45  //----- NON-PUBLIC --------------------------------------------------------------------------------------------------------------------------//
46  private Lock lock = null;
47  private Thread thread = null;
48  private SurfaceView sv = null;
49  private int outputWidth = -1;
50  private int outputHeight = -1;
51  private Bitmap data = null;
52  private int dataWidth = -1;
53  private int dataHeight = -1;
54  private Bitmap newData = null;
55 
56  private double zoom = 1;
57  private float leftUserOffset;
58  private float topUserOffset;
59  //----- PUBLIC ------------------------------------------------------------------------------------------------------------------------------//
60 
61  //===== CONSTRUCTORS, DESTRUCTORS, RELATED METHODS ==========================================================================================//
62  //----- NON-PUBLIC --------------------------------------------------------------------------------------------------------------------------//
63  //----- PUBLIC ------------------------------------------------------------------------------------------------------------------------------//
68  public BitmapDisplayer(SurfaceView sv) {
69  this.sv = sv;
70  sv.getHolder().addCallback(this);
71  this.lock = new Lock();
72  }
73 
74 
75  //===== METHODS =============================================================================================================================//
76  //----- NON-PUBLIC --------------------------------------------------------------------------------------------------------------------------//
77  //----- PUBLIC ------------------------------------------------------------------------------------------------------------------------------//
78 
79 
80 
81 
87  public int translateCoordX(float x) {
88  synchronized (this.lock) {
89  return (int)((x - leftUserOffset) / zoom);
90  }
91  }
92 
98  public int translateCoordY(float y) {
99  synchronized (this.lock) {
100  return (int)((y - topUserOffset) / zoom);
101  }
102  }
103 
107  public SurfaceView getSurfaceView() {
108  return sv;
109  }
110 
121  public void setInput(Bitmap bitmap) {
122  if (bitmap == null) { return; }
123  synchronized (this.lock) {
124  this.newData = bitmap;
125  this.lock.CHANGE_INPUT_FLAG = true;
126  this.lock.REDRAW_FLAG = true;
127  this.lock.notifyAll();
128  }
129  }
130 
131  @Override
132  public void setInput(PDBitmap bitmap) {
133  if (!(bitmap instanceof APDBitmap)) {
134  throw new IllegalArgumentException("Unsupported bitmap type");
135  }
136  setInput(((APDBitmap)bitmap).getBitmap());
137  }
138 
139  @Override
140  public void redraw() {
141  synchronized (this.lock) {
142  this.lock.REDRAW_FLAG = true;
143  this.lock.notifyAll();
144  }
145  }
146 
154  public void setPosition(float leftOffset, float topOffset) {
155  synchronized (this.lock) {
156  this.leftUserOffset = (int) (leftOffset);
157  this.topUserOffset = (int) (topOffset);
158  lock.POSITIONING_CHANGED = true;
159  lock.REDRAW_FLAG = true;
160  lock.notifyAll();
161  }
162  }
163 
171  public void movePositionBy(float dx, float dy) {
172  synchronized (this.lock) {
173  this.leftUserOffset += (int) (dx);
174  this.topUserOffset += (int) (dy);
175  lock.POSITIONING_CHANGED = true;
176  lock.REDRAW_FLAG = true;
177  lock.notifyAll();
178  }
179  }
180 
185  public void setZoom(double zoom) {
186  if (zoom <= 0) { return; }
187  synchronized (this.lock) {
188  this.zoom = zoom;
189  lock.POSITIONING_CHANGED = true;
190  lock.REDRAW_FLAG = true;
191  lock.notifyAll();
192  }
193  }
194 
203  public void adjustZoom(double factor, float focusX, float focusY) {
204  if (factor <= 0) { return; }
205  synchronized (this.lock) {
206  this.zoom *= factor;
207  this.leftUserOffset -= (factor - 1) * (focusX - leftUserOffset);
208  this.topUserOffset -= (factor - 1) * (focusY - topUserOffset);
209  lock.POSITIONING_CHANGED = true;
210  lock.REDRAW_FLAG = true;
211  lock.notifyAll();
212  }
213  }
214 
221  public void adjustZoom(double factor) {
222  adjustZoom(factor, outputWidth / 2.0f, outputHeight / 2.0f);
223  }
224 
228  public void centerAndResetZoom() {
229  synchronized (this.lock) {
230  this.zoom = 1;
231  this.leftUserOffset = (outputWidth - dataWidth) / 2;
232  this.topUserOffset = (outputHeight - dataHeight) / 2;
233  lock.POSITIONING_CHANGED = true;
234  lock.REDRAW_FLAG = true;
235  lock.notifyAll();
236  }
237  }
238 
242  public void centerAndFit() {
243  synchronized (this.lock) {
244  double zx = outputWidth / (double)dataWidth;
245  double zy = outputHeight / (double)dataHeight;
246  if (zx > zy) {
247  zoom = zy;
248  leftUserOffset = (int) ((outputWidth - zoom * dataWidth) / 2);
249  topUserOffset = 0;
250  } else {
251  zoom = zx;
252  leftUserOffset = 0;
253  topUserOffset = (int) ((outputHeight - zoom * dataHeight) / 2);
254  }
255  lock.POSITIONING_CHANGED = true;
256  lock.REDRAW_FLAG = true;
257  lock.notifyAll();
258  }
259  }
260 
261 //===================================================================================================================================================//
262 // SurfaceHolder.Callback
263 //===================================================================================================================================================//
264 
266  public void surfaceCreated(SurfaceHolder holder) {
267  // Voláno v okamžiku, kdy je Surface vytvořen
268  }
269 
271  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
272  // Voláno v okamžiku, kdy Surface dostane rozměry, tedy až po vytvoření
273  this.outputWidth = width;
274  this.outputHeight = height;
275  synchronized (this.lock) {
276  this.lock.OUTPUT_READY_FLAG = true;
277  this.thread = new Thread(this);
278  this.lock.EXIT_FLAG = false;
279  this.thread.start();
280  }
281  }
282 
284  public void surfaceDestroyed(SurfaceHolder holder) {
285  // Voláno těsně před tím, než je Surface zničen
286  synchronized (this.lock) {
287  this.lock.OUTPUT_READY_FLAG = false;
288  this.lock.EXIT_FLAG = true;
289  this.lock.notifyAll();
290  }
291  try {
292  this.thread.join();
293  } catch (InterruptedException e) {
294  // Čekání na ukončení přerušeno, pravděpodobně konec programu
295  }
296  this.thread = null;
297  }
298 
299 //===================================================================================================================================================//
300 // Runnable
301 //===================================================================================================================================================//
302  @Override
303  public void run() {
304  SurfaceHolder sh = this.sv.getHolder();
305  Canvas c = null;
306  Paint p = new Paint();
307  p.setFlags(Paint.DITHER_FLAG);
308  p.setFilterBitmap(true);
309  Rect src = null;
310  Rect dst = null;
311 
312  while (true) { // vykreslujeme stále
313  synchronized (this.lock) {
314  while (true) {
315  // if-else určuje prioritu vykonávání
316  if (this.lock.EXIT_FLAG) {
317  return; // ukončení běhu kvůli nastavené EXIT_FLAG
318  }
319  if (this.lock.CHANGE_INPUT_FLAG) {
320  this.data = this.newData;
321  this.dataWidth = this.data.getWidth();
322  this.dataHeight = this.data.getHeight();
323  this.lock.CHANGE_INPUT_FLAG = false;
324  this.lock.INPUT_READY_FLAG = true;
325  this.lock.POSITIONING_CHANGED = true;
326  }
327  if (this.lock.POSITIONING_CHANGED && this.lock.INPUT_READY_FLAG) {
328  double st, sl, sb, sr;
329  double dt, dl, db, dr;
330  if (leftUserOffset < 0) {
331  sl = (-leftUserOffset) / zoom;
332  sr = Math.min(((outputWidth - leftUserOffset) / zoom), dataWidth);
333  dl = 0;
334  dr = Math.min(outputWidth, ((dataWidth * zoom)) + leftUserOffset);
335  } else {
336  sl = 0;
337  sr = Math.min( ((outputWidth - leftUserOffset) / zoom), dataWidth);
338  dl = leftUserOffset;
339  dr = Math.min(outputWidth, (dataWidth * zoom) + leftUserOffset);
340  }
341  if (topUserOffset < 0) {
342  st = (-topUserOffset / zoom);
343  sb = Math.min(((outputHeight - topUserOffset)/zoom), dataHeight);
344  dt = 0;
345  db = Math.min(outputHeight, (dataHeight * zoom) + topUserOffset);
346  } else {
347  st = 0;
348  sb = Math.min(((outputHeight - topUserOffset)/zoom), dataHeight);
349  dt = topUserOffset;
350  db = Math.min(outputHeight, (dataHeight * zoom) + topUserOffset);
351  }
352 
353  dst = new Rect((int)dl, (int)dt, (int)dr, (int)db);
354  src = new Rect((int)sl, (int)st, (int)sr, (int)sb);
355  lock.POSITIONING_CHANGED = false;
356  }
357  if (this.lock.REDRAW_FLAG && this.lock.OUTPUT_READY_FLAG && this.lock.INPUT_READY_FLAG) {
358  break; // vyskočíme a kreslíme
359  }
360  try {
361  this.lock.BUSY_FLAG = false;
362  this.lock.wait(); // žádné požadavky na toto vlákno
363  } catch (InterruptedException e) {
364  return;
365  }
366  this.lock.BUSY_FLAG = true;
367  }
368  this.lock.REDRAW_FLAG = false;
369  }
370  while (true) { // Následující kód je okopírovaný z literatury a celkem možné, že není korektní vzhledem k nekonečný smyčce
371  if (!sh.getSurface().isValid()) {
372  continue;
373  }
374  c = sh.lockCanvas();
375  c.drawColor(0xFF000000); // přemazání, jinak zůstávají na Canvasu předchozí obrazy (a to i 5 překreslení staré), toto by mohlo být nastavitelné..
376  c.drawBitmap(data, src, dst, p);
377  sh.unlockCanvasAndPost(c);
378  break;
379  }
380  }
381  }
382 }