Tetris Java Project


SUBMITTED BY: Guest

DATE: July 30, 2014, 7:41 p.m.

FORMAT: Text only

SIZE: 38.3 kB

HITS: 611

  1. package tetris;
  2. import java.awt.*;
  3. import java.applet.*;
  4. import java.awt.event.*;
  5. import java.util.Random;
  6. import java.net.URL;
  7. import java.net.MalformedURLException;
  8. public class tetris extends Applet {
  9. private final static int INITIAL_DELAY = 1000;
  10. private final static byte ROWS = 18;
  11. private final static byte COLUMNS = 10;
  12. private final static int EMPTY = -1;
  13. private final static int DELETED_ROWS_PER_LEVEL = 5;
  14. private final static Color PIECE_COLORS[] = {
  15. new Color(0xFF00FF), // fucia
  16. new Color(0xDC143C), // crimson
  17. new Color(0x00CED1), // dark turquoise
  18. new Color(0xFFD700), // gold
  19. new Color(0x32CD32), // lime green
  20. new Color(0x008080), // teal
  21. new Color(0xFFA500), // orange
  22. };
  23. private final static Color BACKGROUND_COLORS[] = {
  24. new Color(0xFFDAB9), // peachpuff
  25. new Color(0xFFC0CB), // pink
  26. new Color(0xFF99CC), // hot pink
  27. new Color(0x0099CC), // sky blue
  28. new Color(0x9966CC), // lavender
  29. };
  30. private final static Color BACKGROUND_COLOR = new Color(0x99FFCC);
  31. // * ** * * * *
  32. // * * * ** ** ** **
  33. // * * ** * * * **
  34. // *
  35. // 0 1 2 3 4 5 6
  36. private final static boolean PIECE_BITS[][][] = {
  37. {
  38. {false, true, false, false},
  39. {false, true, false, false},
  40. {false, true, false, false},
  41. {false, true, false, false},
  42. },
  43. {
  44. {false, false, false, false},
  45. {false, true, true, false},
  46. {false, true, false, false},
  47. {false, true, false, false},
  48. },
  49. {
  50. {false, false, false, false},
  51. {false, true, false, false},
  52. {false, true, false, false},
  53. {false, true, true, false},
  54. },
  55. {
  56. {false, false, false, false},
  57. {false, true, false, false},
  58. {false, true, true, false},
  59. {false, false, true, false},
  60. },
  61. {
  62. {false, false, false, false},
  63. {false, false, true, false},
  64. {false, true, true, false},
  65. {false, true, false, false},
  66. },
  67. {
  68. {false, false, false, false},
  69. {false, true, false, false},
  70. {false, true, true, false},
  71. {false, true, false, false},
  72. },
  73. {
  74. {false, false, false, false},
  75. {false, false, false, false},
  76. {false, true, true, false},
  77. {false, true, true, false},
  78. },
  79. };
  80. private static boolean tmp_grid[][] = new boolean[4][4]; // scratch space
  81. private static Random random = new Random();
  82. private static class TetrisLabel extends Label {
  83. private final static Font LABEL_FONT = new Font("Serif", Font.BOLD, 18);
  84. private TetrisLabel(String text) {
  85. super(text);
  86. setFont(LABEL_FONT);
  87. }
  88. private void addValue(int val) {
  89. setText(Integer.toString((Integer.parseInt(getText())) + val ));
  90. }
  91. }
  92. //
  93. // INSTANCE DATA
  94. //
  95. private int grid[][] = new int[ROWS][COLUMNS];
  96. private int next_piece_grid[][] = new int[4][4];
  97. private int num_rows_deleted = 0;
  98. private GridCanvas game_grid = new GridCanvas(grid, true);
  99. private GridCanvas next_piece_canvas = new GridCanvas(next_piece_grid, false);
  100. private Timer timer;
  101. private TetrisPiece cur_piece;
  102. private TetrisPiece next_piece = randomPiece();
  103. private TetrisSound sounds;// = new TetrisSound(this);
  104. private TetrisLabel rows_deleted_label = new TetrisLabel("0");
  105. private TetrisLabel level_label = new TetrisLabel("1");
  106. private TetrisLabel score_label = new TetrisLabel("0");
  107. private TetrisLabel high_score_label = new TetrisLabel("");
  108. final Button start_newgame_butt = new TetrisButton("Start");
  109. final Button pause_resume_butt = new TetrisButton("Pause");
  110. //
  111. // INNER CLASSES
  112. //
  113. private class TetrisButton extends Button {
  114. public TetrisButton(String label) {
  115. super(label);
  116. }
  117. public Dimension getPreferredSize() {
  118. return new Dimension(120, super.getPreferredSize().height);
  119. }
  120. }
  121. private class TetrisPiece {
  122. private boolean squares[][];
  123. private int type;
  124. private Point position = new Point(3, -4); // -4 to start above top row
  125. public int getX() { return position.x; }
  126. public int getY() { return position.y; }
  127. public void setX(int newx) { position.x = newx; }
  128. public void setY(int newy) { position.y = newy; }
  129. public void setPosition(int newx, int newy) { setX(newx); setY(newy); }
  130. public TetrisPiece(int type) {
  131. this.type = type;
  132. this.squares = new boolean[4][4];
  133. for(int i=0; i<4; i++)
  134. for(int j=0; j<4; j++)
  135. this.squares[i][j] = PIECE_BITS[type][i][j];
  136. }
  137. public boolean canStepDown() {
  138. synchronized(timer) {
  139. cut();
  140. position.y++;
  141. boolean OK = canPaste();
  142. position.y--;
  143. paste();
  144. return OK;
  145. }
  146. }
  147. public boolean canPaste() {
  148. for(int i=0; i<4; i++) {
  149. for(int j=0; j<4; j++) {
  150. int to_x = j + position.x;
  151. int to_y = i + position.y;
  152. if(squares[i][j]) { // piece contains this square?
  153. if(0 > to_x || to_x >= COLUMNS // square too far left or right?
  154. || to_y >= ROWS) // square off bottom?
  155. {
  156. return false;
  157. // note: it's always considered OK to paste a square
  158. // *above* the grid though attempting to do so does nothing.
  159. // This allows the user to move a piece before it drops
  160. // completely into view.
  161. }
  162. if(to_y >= 0 && grid[to_y][to_x] != EMPTY)
  163. return false;
  164. }
  165. }
  166. }
  167. return true;
  168. }
  169. public void stepDown() {
  170. position.y++;
  171. }
  172. public void cut() {
  173. for(int i=0; i<4; i++)
  174. for(int j=0; j<4; j++)
  175. if(squares[i][j] && position.y+i>=0)
  176. grid[position.y + i][position.x + j] = EMPTY;
  177. }
  178. /**
  179. * Paste the color info of this piece into the given grid
  180. */
  181. public void paste(int into[][]) {
  182. for(int i=0; i<4; i++)
  183. for(int j=0; j<4; j++)
  184. if(squares[i][j] && position.y+i>=0)
  185. into[position.y + i][position.x + j] = type;
  186. }
  187. /**
  188. * No argument version assumes pasting into main game grid
  189. */
  190. public void paste() {
  191. paste(grid);
  192. }
  193. public void rotate() {
  194. // copy the piece's data into a temp array
  195. for(int i=0; i<4; i++)
  196. for(int j=0; j<4; j++)
  197. tmp_grid[i][j] = squares[i][j];
  198. // copy back rotated 90 degrees
  199. for(int i=0; i<4; i++)
  200. for(int j=0; j<4; j++)
  201. squares[j][i] = tmp_grid[i][3-j];
  202. }
  203. public void rotateBack() {
  204. // copy originally saved version back
  205. // this of course assumes this call was preceeded by
  206. // a call to rotate() for the same piece
  207. for(int i=0; i<4; i++)
  208. for(int j=0; j<4; j++)
  209. squares[i][j] = tmp_grid[i][j];
  210. }
  211. // this method is a bit of a hack to check for the case
  212. // where a piece may be safely on the grid but have one or more
  213. // rows of empty squares that are above the grid and therefore OK
  214. public boolean isTotallyOnGrid() {
  215. for(int i=0; i<4; i++) {
  216. if(position.y + i >= 0)
  217. return true; //everything from here down is on grid
  218. // this row is above grid so look for non-empty squares
  219. for(int j=0; j<4; j++)
  220. if(squares[i][j])
  221. return false;
  222. }
  223. System.err.println("TetrisPiece.isTotallyOnGrid internal error");
  224. return false;
  225. }
  226. } // end class TetrisPiece
  227. private class Timer extends Thread {
  228. private long m_delay;
  229. private boolean m_paused = true;
  230. private boolean m_fast = false;
  231. private ActionListener m_cb;
  232. public Timer(long delay, ActionListener cb) {
  233. setDelay(delay);
  234. m_cb = cb;
  235. }
  236. public void setPaused(boolean pause) {
  237. m_paused = pause;
  238. if(m_paused) {
  239. sounds.stopSoundtrack();
  240. }
  241. else {
  242. sounds.playSoundtrack();
  243. synchronized(this) {
  244. this.notify();
  245. }
  246. }
  247. }
  248. public boolean isPaused() { return m_paused; }
  249. public void setDelay(long delay) { m_delay = delay; }
  250. public boolean isRunning() { return !m_paused; }
  251. public void setFast(boolean fast) {
  252. m_fast = fast;
  253. if(m_fast) {
  254. try {
  255. this.checkAccess();
  256. this.interrupt(); // no exception, so OK to interrupt
  257. } catch(SecurityException se) {}
  258. }
  259. }
  260. public boolean isFast() { return m_fast; }
  261. public void faster() {
  262. m_delay = (int)(m_delay * .9); //increase the speed exponentially in reverse
  263. }
  264. public void run() {
  265. while(true) {
  266. try {
  267. sleep(m_fast ? 30 : m_delay);
  268. } catch (Exception e) {}
  269. if(m_paused) {
  270. try {
  271. synchronized(this) {
  272. this.wait();
  273. }
  274. } catch(InterruptedException ie) {}
  275. }
  276. synchronized(this) {
  277. m_cb.actionPerformed(null);
  278. }
  279. }
  280. }
  281. } // end class Timer
  282. private class GridCanvas extends DoubleBufferedCanvas {
  283. private int grid[][];
  284. private boolean paint_background;
  285. public GridCanvas(int[][] grid, boolean do_background) {
  286. this.grid = grid;
  287. paint_background = do_background;
  288. clear();
  289. }
  290. private void clear() {
  291. for(int i=0; i<grid.length; i++)
  292. for(int j=0; j<grid[0].length; j++)
  293. grid[i][j] = EMPTY;
  294. }
  295. public Dimension getPreferredSize() {
  296. return new Dimension(grid[0].length * 30, grid.length * 30);
  297. }
  298. public void paint(Graphics g) {
  299. g = this.startPaint(g); // returned g paints into offscreen image
  300. int width = this.getSize().width;
  301. int height = this.getSize().height;
  302. g.clearRect(0, 0, width, height);
  303. int cell_size, xstart, ystart;
  304. double panel_aspect_ratio = (double)width/height;
  305. double grid_aspect_ratio = (double)grid[0].length/grid.length;
  306. if(panel_aspect_ratio > grid_aspect_ratio) {
  307. // extra space on sides
  308. cell_size = (int)((double)height/grid.length + 0.5);
  309. xstart = (int)(width/2 - (grid[0].length/2.0 * cell_size + 0.5));
  310. ystart = 0;
  311. }
  312. else {
  313. // extra vertical space
  314. cell_size = (int)((double)width/grid[0].length + 0.5);
  315. xstart = 0;
  316. ystart = (int)(height/2 - (grid.length/2.0 * cell_size + 0.5));
  317. }
  318. if(paint_background) {
  319. g.setColor(BACKGROUND_COLORS[(num_rows_deleted / DELETED_ROWS_PER_LEVEL) % BACKGROUND_COLORS.length]);
  320. g.fillRect(xstart, ystart, COLUMNS*cell_size, ROWS*cell_size);
  321. }
  322. for(int i=0; i<grid.length; i++) {
  323. for(int j=0; j<grid[0].length; j++) {
  324. if(grid[i][j] != EMPTY) {
  325. g.setColor(PIECE_COLORS[grid[i][j]]);
  326. int x = xstart + j*cell_size;
  327. int y = ystart + i*cell_size;
  328. g.fill3DRect(x, y, cell_size, cell_size, true);
  329. }
  330. }
  331. }
  332. this.endPaint(); // paints accumulated image in one shot
  333. }
  334. } // end class GridCanvas
  335. private class TetrisSound {
  336. private AudioClip soundTrack = null;
  337. private AudioClip destroyRowSounds[] = new AudioClip[4];
  338. private AudioClip gameOverSound = null;
  339. private AudioClip getClip(String name) throws MalformedURLException {
  340. URL soundFileUrl = new URL(getCodeBase(), name);
  341. try {
  342. AudioClip clip = getAudioClip(soundFileUrl);
  343. return clip;
  344. } catch(NullPointerException npe) {
  345. System.err.println("exception " + npe);
  346. return null;
  347. }
  348. }
  349. public TetrisSound() {
  350. //load sound files
  351. try {
  352. soundTrack = getClip("theme.au");
  353. destroyRowSounds[0] = getClip("quiteImpressive.au");
  354. destroyRowSounds[1] = getClip("smashing.au");
  355. destroyRowSounds[2] = getClip("yeahbaby.au");
  356. destroyRowSounds[3] = getClip("great.au");
  357. gameOverSound = getClip("groovy.au");
  358. }
  359. catch (Exception e) {
  360. System.err.println(e.getMessage());
  361. }
  362. }
  363. public void playSoundtrack() {
  364. if(soundTrack == null)
  365. return;
  366. soundTrack.loop();
  367. }
  368. public void playDestroyRows(int rows) {
  369. int soundid = rows - 1;
  370. if(0 > soundid || soundid >= destroyRowSounds.length || destroyRowSounds[soundid] == null)
  371. return;
  372. destroyRowSounds[soundid].play();
  373. }
  374. public void playGameOverSound() {
  375. if(gameOverSound == null)
  376. return;
  377. gameOverSound.play();
  378. }
  379. public void stopSoundtrack() {
  380. if(soundTrack == null)
  381. return;
  382. soundTrack.stop();
  383. }
  384. } // end class TetrisSound
  385. //
  386. // INSTANCE METHODS
  387. //
  388. private TetrisPiece randomPiece() {
  389. int rand = Math.abs(random.nextInt());
  390. return new TetrisPiece(rand % (PIECE_COLORS.length));
  391. }
  392. private void installNewPiece() {
  393. next_piece_canvas.clear();
  394. cur_piece = next_piece;
  395. cur_piece.setPosition(3, -4); //-4 to start above top of grid
  396. if(cur_piece.canPaste()) {
  397. next_piece = randomPiece();
  398. next_piece.setPosition(0, 0);
  399. next_piece.paste(next_piece_grid);
  400. next_piece_canvas.repaint();
  401. }
  402. else
  403. gameOver();
  404. }
  405. private void gameOver() {
  406. System.out.println("Game Over!");
  407. timer.setPaused(true);
  408. pause_resume_butt.setEnabled(false);
  409. int score = Integer.parseInt(score_label.getText());
  410. int high_score = high_score_label.getText().length() > 0 ?
  411. Integer.parseInt(high_score_label.getText()) : 0;
  412. if(score > high_score)
  413. high_score_label.setText("" + score);
  414. sounds.playGameOverSound();
  415. }
  416. private boolean rowIsFull(int row) {
  417. for(int i=0; i<COLUMNS; i++)
  418. if(grid[row][i] == EMPTY)
  419. return false;
  420. return true;
  421. }
  422. private int countFullRows() {
  423. int n_full_rows = 0;
  424. for(int i=0; i<ROWS; i++)
  425. if(rowIsFull(i))
  426. n_full_rows++;
  427. return n_full_rows;
  428. }
  429. private void removeRow(int row) {
  430. for(int j=0; j<COLUMNS; j++)
  431. grid[row][j] = EMPTY;
  432. for(int i=row; i>0; i--) {
  433. for(int j=0; j<COLUMNS; j++) {
  434. grid[i][j] = grid[i-1][j];
  435. }
  436. }
  437. }
  438. private void removeFullRows() {
  439. int n_full = countFullRows();
  440. score_label.addValue((int)(10 * Math.pow(2, n_full) - 10)); //give points exponentially
  441. if(n_full == 0)
  442. return;
  443. sounds.playDestroyRows(n_full);
  444. if(num_rows_deleted / DELETED_ROWS_PER_LEVEL != (num_rows_deleted+n_full) / DELETED_ROWS_PER_LEVEL) {
  445. timer.faster();
  446. level_label.addValue(n_full / DELETED_ROWS_PER_LEVEL + 1);
  447. level_label.repaint();
  448. }
  449. rows_deleted_label.addValue(n_full);
  450. num_rows_deleted += n_full;
  451. for(int i=ROWS-1; i>=0; i--)
  452. while(rowIsFull(i))
  453. removeRow(i);
  454. game_grid.repaint();
  455. }
  456. public void start() {
  457. timer = new Timer(INITIAL_DELAY, new ActionListener() {
  458. public void actionPerformed(ActionEvent ae) {
  459. synchronized(timer) {
  460. if(cur_piece.canStepDown()) {
  461. cur_piece.cut();
  462. cur_piece.stepDown();
  463. cur_piece.paste();
  464. if(timer.isFast())
  465. score_label.addValue(1); // a small reward for using fast mode
  466. }
  467. else { // it hit something
  468. timer.setFast(false);
  469. if( ! cur_piece.isTotallyOnGrid())
  470. gameOver();
  471. else {
  472. removeFullRows();
  473. installNewPiece();
  474. }
  475. }
  476. }
  477. game_grid.repaint();
  478. }
  479. });
  480. timer.start(); // pauses immediately
  481. }
  482. public void stop() {
  483. pauseGame();
  484. synchronized(timer){
  485. timer.stop();
  486. }
  487. timer = null;
  488. }
  489. private void startGame() {
  490. timer.setDelay(INITIAL_DELAY);
  491. timer.setPaused(false);
  492. start_newgame_butt.setLabel("Start New Game");
  493. pause_resume_butt.setEnabled(true); // stays enabled from here on
  494. pause_resume_butt.setLabel("Pause");
  495. pause_resume_butt.validate();
  496. sounds.playSoundtrack();
  497. }
  498. private void newGame() {
  499. game_grid.clear();
  500. installNewPiece();
  501. num_rows_deleted = 0;
  502. rows_deleted_label.setText("0");
  503. level_label.setText("1");
  504. score_label.setText("0");
  505. startGame();
  506. }
  507. private void pauseGame() {
  508. timer.setPaused(true);
  509. pause_resume_butt.setLabel("Resume");
  510. sounds.stopSoundtrack();
  511. }
  512. private void resumeGame() {
  513. timer.setPaused(false);
  514. pause_resume_butt.setLabel("Pause");
  515. sounds.playSoundtrack();
  516. }
  517. public void init() {
  518. sounds = new TetrisSound(); // NOTE: Must be initialized after Applet fully constructed!
  519. installNewPiece();
  520. pause_resume_butt.setEnabled(false);
  521. start_newgame_butt.addActionListener(new ActionListener() {
  522. public void actionPerformed(ActionEvent ae) {
  523. if(start_newgame_butt.getLabel().equals("Start"))
  524. startGame();
  525. else
  526. newGame();
  527. }
  528. });
  529. pause_resume_butt.addActionListener(new ActionListener() {
  530. public void actionPerformed(ActionEvent ae) {
  531. if(pause_resume_butt.getLabel().equals("Pause"))
  532. pauseGame();
  533. else
  534. resumeGame();
  535. }
  536. });
  537. //create key listener for rotating, moving left, moving right
  538. KeyListener key_listener = new KeyAdapter() {
  539. public void keyPressed(KeyEvent e) {
  540. if(timer.isPaused()) //don't do anything if game is paused
  541. return;
  542. if (e.getKeyCode() == 37 || e.getKeyCode() == 39) { //left or right arrow pressed
  543. int dir = e.getKeyCode() == 37 ? -1 : 1;
  544. synchronized(timer) {
  545. cur_piece.cut();
  546. cur_piece.setX(cur_piece.getX() + dir); // try to move
  547. if( ! cur_piece.canPaste())
  548. cur_piece.setX(cur_piece.getX() - dir); // undo move
  549. cur_piece.paste();
  550. }
  551. game_grid.repaint();
  552. }
  553. else if (e.getKeyCode() == 38) { //rotate
  554. synchronized(timer) {
  555. cur_piece.cut();
  556. cur_piece.rotate();
  557. if( ! cur_piece.canPaste())
  558. cur_piece.rotateBack();
  559. cur_piece.paste();
  560. }
  561. game_grid.repaint();
  562. }
  563. if (e.getKeyCode() == 40) { //down arrow pressed; drop piece
  564. timer.setFast(true);
  565. }
  566. }
  567. };
  568. // add the key listener to all components that might get focus
  569. // so that it'll work regardless of which has focus
  570. start_newgame_butt.addKeyListener(key_listener);
  571. pause_resume_butt.addKeyListener(key_listener);
  572. Panel right_panel = new Panel(new GridLayout(3, 1));
  573. right_panel.setBackground(BACKGROUND_COLOR);
  574. Panel control_panel = new Panel();
  575. control_panel.add(start_newgame_butt);
  576. control_panel.add(pause_resume_butt);
  577. control_panel.setBackground(BACKGROUND_COLOR);
  578. right_panel.add(control_panel);
  579. Panel tmp = new Panel(new BorderLayout());
  580. tmp.add("North", new TetrisLabel(" Next Piece:"));
  581. tmp.add("Center", next_piece_canvas);
  582. tmp.setBackground(BACKGROUND_COLOR);
  583. right_panel.add(tmp);
  584. Panel stats_panel = new Panel(new GridLayout(4, 2));
  585. stats_panel.add(new TetrisLabel(" Rows Deleted: "));
  586. stats_panel.add(rows_deleted_label);
  587. stats_panel.add(new TetrisLabel(" Level: "));
  588. stats_panel.add(level_label);
  589. stats_panel.add(new TetrisLabel(" Score: "));
  590. stats_panel.add(score_label);
  591. stats_panel.add(new TetrisLabel(" High Score: "));
  592. stats_panel.add(high_score_label);
  593. tmp = new Panel(new BorderLayout());
  594. tmp.setBackground(BACKGROUND_COLOR);
  595. tmp.add("Center", stats_panel);
  596. right_panel.add(tmp);
  597. // finaly, add all the main panels to the applet panel
  598. this.setLayout(new GridLayout(1, 2));
  599. this.add(game_grid);
  600. this.add(right_panel);
  601. this.setBackground(BACKGROUND_COLOR);
  602. this.validate();
  603. }
  604. public static void main(String[] args) {
  605. Frame frame = new Frame("Tetris");
  606. tetris tetris = new tetris();
  607. frame.add(tetris);
  608. tetris.init();
  609. tetris.start();
  610. frame.addWindowListener(new WindowAdapter() {
  611. public void windowClosing(WindowEvent e) {
  612. System.exit(0);
  613. }
  614. });
  615. frame.setSize(489, 441);
  616. frame.setResizable(false);
  617. frame.setVisible(true);
  618. }
  619. } // end class Tetris
  620. class DoubleBufferedCanvas extends Canvas {
  621. private Image mActiveOffscreenImage = null;
  622. private Dimension mOffscreenSize = new Dimension(-1,-1);
  623. private Graphics mActiveOffscreenGraphics = null;
  624. private Graphics mSystemGraphics = null;
  625. DoubleBufferedCanvas() {
  626. /*
  627. this.addComponentListener(new ComponentAdapter() {
  628. public void componentResized(ComponentEvent e) {
  629. repaint();
  630. }
  631. });*/
  632. }
  633. /**
  634. * NOTE: when extending applets:
  635. * this overrides update() to *not* erase the background before painting
  636. */
  637. public void update(Graphics g) {
  638. paint(g);
  639. }
  640. public Graphics startPaint (Graphics sysgraph) {
  641. mSystemGraphics = sysgraph;
  642. // Initialize if this is the first pass or the size has changed
  643. Dimension d = getSize();
  644. if ((mActiveOffscreenImage == null) ||
  645. (d.width != mOffscreenSize.width) ||
  646. (d.height != mOffscreenSize.height))
  647. {
  648. mActiveOffscreenImage = createImage(d.width, d.height);
  649. mActiveOffscreenGraphics = mActiveOffscreenImage.getGraphics();
  650. mOffscreenSize = d;
  651. mActiveOffscreenGraphics.setFont(getFont());
  652. }
  653. //mActiveOffscreenGraphics.clearRect(0, 0, mOffscreenSize.width, mOffscreenSize.height);
  654. return mActiveOffscreenGraphics;
  655. }
  656. public void endPaint () {
  657. // Start copying the offscreen image to this canvas
  658. // The application will begin drawing into the other one while this happens
  659. mSystemGraphics.drawImage(mActiveOffscreenImage, 0, 0, null);
  660. }
  661. }

comments powered by Disqus