App

- Main Page
- Related Pages
- Namespaces
- Classes
- Files

- File List
- File Members

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 }