提交 1bab4883 编写于 作者: W wizardforcel

2020-06-21 16:14:32

上级 982140e1
......@@ -22,7 +22,7 @@ Figure: Tetrominoes
`com/zetcode/Shape.java`
```
```java
package com.zetcode;
import java.util.Random;
......@@ -165,7 +165,7 @@ public class Shape {
`Shape`类提供有关俄罗斯方块的信息。
```
```java
protected enum Tetrominoe {
NoShape, ZShape, SShape, LineShape,
TShape, SquareShape, LShape, MirroredLShape
......@@ -175,7 +175,7 @@ protected enum Tetrominoe {
`Tetrominoe`枚举包含七个俄罗斯方块形状名称和一个称为`NoShape`的空形状。
```
```java
coords = new int[4][2];
setShape(Tetrominoe.NoShape);
......@@ -183,7 +183,7 @@ setShape(Tetrominoe.NoShape);
`coords`数组保存俄罗斯方块的实际坐标。
```
```java
int[][][] coordsTable = new int[][][]{
{{0, 0}, {0, 0}, {0, 0}, {0, 0}},
{{0, -1}, {0, 0}, {-1, 0}, {-1, 1}},
......@@ -199,7 +199,7 @@ int[][][] coordsTable = new int[][][]{
`coordsTable`数组保存俄罗斯方块的所有可能的坐标值。 这是一个模板,所有作品都从该模板中获得其坐标值。
```
```java
for (int i = 0; i < 4; i++) {
System.arraycopy(coordsTable[shape.ordinal()], 0, coords, 0, 4);
......@@ -215,7 +215,7 @@ for (int i = 0; i < 4; i++) {
Figure: Coordinates
```
```java
Shape rotateLeft() {
if (pieceShape == Tetrominoe.SquareShape) {
......@@ -241,7 +241,7 @@ Shape rotateLeft() {
`com/zetcode/Board.java`
```
```java
package com.zetcode;
import com.zetcode.Shape.Tetrominoe;
......@@ -585,7 +585,7 @@ public class Board extends JPanel {
最后,我们有`Board.java`文件。 这是游戏逻辑所在的位置。
```
```java
private final int BOARD_WIDTH = 10;
private final int BOARD_HEIGHT = 22;
private final int PERIOD_INTERVAL = 300;
......@@ -594,7 +594,7 @@ private final int PERIOD_INTERVAL = 300;
我们有四个常数。 `BOARD_WIDTH``BOARD_HEIGHT`定义了电路板的尺寸。 `PERIOD_INTERVAL`常数定义游戏的速度。
```
```java
...
private boolean isFallingFinished = false;
private boolean isStarted = false;
......@@ -608,7 +608,7 @@ private int curY = 0;
一些重要的变量被初始化。 `isFallingFinished`确定俄罗斯方块形状是否已经完成下降,然后我们需要创建一个新形状。 `isStarted`用于检查游戏是否已经开始。 同样,`isPaused`用于检查游戏是否暂停。 `numLinesRemoved`计算到目前为止我们已删除的行数。 `curX``curY`确定下降的俄罗斯方块形状的实际位置。
```
```java
private int squareWidth() {
return (int) getSize().getWidth() / BOARD_WIDTH;
......@@ -623,7 +623,7 @@ private int squareHeight() {
这些线确定单个 Tetrominoe 正方形的宽度和高度。
```
```java
private Tetrominoe shapeAt(int x, int y) {
return board[(y * BOARD_WIDTH) + x];
......@@ -633,7 +633,7 @@ private Tetrominoe shapeAt(int x, int y) {
我们确定给定坐标处的形状。 形状存储在`board`数组中。
```
```java
void start() {
curPiece = new Shape();
......@@ -644,7 +644,7 @@ void start() {
我们创建一个新的当前形状和一个新的板。
```
```java
clearBoard();
newPiece();
......@@ -652,7 +652,7 @@ newPiece();
清理板并初始化新的下降片。
```
```java
timer = new Timer(PERIOD_INTERVAL, new GameCycle());
timer.start();
......@@ -660,7 +660,7 @@ timer.start();
我们创建一个计时器。 计时器以`PERIOD_INTERVAL`间隔执行,从而创建游戏周期。
```
```java
private void pause() {
isPaused = !isPaused;
......@@ -682,7 +682,7 @@ private void pause() {
`doDrawing()`方法内部,我们在板上绘制了所有对象。 这幅画有两个步骤。
```
```java
for (int i = 0; i < BOARD_HEIGHT; i++) {
for (int j = 0; j < BOARD_WIDTH; j++) {
......@@ -701,7 +701,7 @@ for (int i = 0; i < BOARD_HEIGHT; i++) {
在第一步中,我们绘制掉落到板底部的所有形状或形状的其余部分。 所有正方形都记在板阵列中。 我们使用`shapeAt()`方法访问它。
```
```java
if (curPiece.getShape() != Tetrominoe.NoShape) {
for (int i = 0; i < 4; i++) {
......@@ -719,7 +719,7 @@ if (curPiece.getShape() != Tetrominoe.NoShape) {
在第二步中,我们绘制实际的下降部分。
```
```java
private void dropDown() {
int newY = curY;
......@@ -741,7 +741,7 @@ private void dropDown() {
如果按`Space`键,则该片段将落到底部。 我们只是简单地尝试将一块下降到另一条俄罗斯方块下降的底部或顶部。 当俄罗斯方块结束下降时,将调用`pieceDropped()`
```
```java
private void oneLineDown() {
if (!tryMove(curPiece, curX, curY - 1)) {
......@@ -754,7 +754,7 @@ private void oneLineDown() {
`oneLineDown()`方法中,我们尝试将下降片向下移动一行,直到完全下降。
```
```java
private void clearBoard() {
for (int i = 0; i < BOARD_HEIGHT * BOARD_WIDTH; i++) {
......@@ -767,7 +767,7 @@ private void clearBoard() {
`clearBoard()`方法用空的`Tetrominoe.NoShape`填充电路板。 稍后将其用于碰撞检测。
```
```java
private void pieceDropped() {
for (int i = 0; i < 4; i++) {
......@@ -789,7 +789,7 @@ private void pieceDropped() {
`pieceDropped()`方法将下降的片段放入`board`数组。 木板再次保持了所有碎片的正方形和已经落下的碎片的剩余部分。 当一块完成落下时,就该检查我们是否可以从板上去除一些线了。 这是`removeFullLines()`方法的工作。 然后,我们创建一个新作品,或更准确地说,我们尝试创建一个新作品。
```
```java
private void newPiece() {
curPiece.setRandomShape();
......@@ -810,7 +810,7 @@ private void newPiece() {
`newPiece()`方法创建一个新的俄罗斯方块。 作品获得了新的随机形状。 然后,我们计算初始`curX``curY`值。 如果我们不能移动到初始位置,则游戏结束了,我们结束了。 计时器停止运行,我们在状态栏上显示`Game over`包含分数的字符串。
```
```java
private boolean tryMove(Shape newPiece, int newX, int newY) {
for (int i = 0; i < 4; i++) {
......@@ -842,7 +842,7 @@ private boolean tryMove(Shape newPiece, int newX, int newY) {
`tryMove()`方法尝试移动俄罗斯方块。 如果该方法已到达板边界或与已经跌落的俄罗斯方块碎片相邻,则返回`false`
```
```java
private void removeFullLines() {
int numFullLines = 0;
......@@ -886,7 +886,7 @@ private void removeFullLines() {
`removeFullLines()`方法内部,我们检查板上的所有行中是否有完整的行。 如果至少有一条实线,则将其删除。 找到整条线后,我们增加计数器。 我们将整行上方的所有行向下移动一行。 这样我们就破坏了整个生产线。 注意,在我们的俄罗斯方块游戏中,我们使用了所谓的天真重力。 这意味着正方形可能会漂浮在空白间隙上方。
```
```java
private void drawSquare(Graphics g, int x, int y, Tetrominoe shape) {
Color colors[] = {new Color(0, 0, 0), new Color(204, 102, 102),
......@@ -915,7 +915,7 @@ private void drawSquare(Graphics g, int x, int y, Tetrominoe shape) {
每个俄罗斯方块都有四个正方形。 每个正方形都使用`drawSquare()`方法绘制。 俄罗斯方块有不同的颜色。 正方形的左侧和顶部以较亮的颜色绘制。 类似地,底部和右侧用较深的颜色绘制。 这是为了模拟 3D 边缘。
```
```java
private class GameCycle implements ActionListener {
@Override
......@@ -929,7 +929,7 @@ private class GameCycle implements ActionListener {
`GameCycle`中,我们调用`doGameCycle()`方法,以创建游戏周期。
```
```java
private void doGameCycle() {
update();
......@@ -940,7 +940,7 @@ private void doGameCycle() {
游戏分为游戏周期。 每个循环都会抽水游戏并重画棋盘。
```
```java
private void update() {
if (isPaused) {
......@@ -962,7 +962,7 @@ private void update() {
`update()`代表游戏的第一步。 下降的零件向下一行,或者如果前一个零件已完成下降,则创建新的零件。
```
```java
private class TAdapter extends KeyAdapter {
@Override
......@@ -973,14 +973,14 @@ private class TAdapter extends KeyAdapter {
游戏由光标键控制。 我们在`KeyAdapter`中检查关键事件。
```
```java
int keycode = e.getKeyCode();
```
我们使用`getKeyCode()`方法获得密钥代码。
```
```java
// Java 12 switch expressions
switch (keycode) {
......@@ -999,7 +999,7 @@ switch (keycode) {
`com/zetcode/Tetris.java`
```
```java
package com.zetcode;
import java.awt.BorderLayout;
......@@ -1056,7 +1056,7 @@ public class Tetris extends JFrame {
`Tetris.java`文件中,我们设置了游戏。 我们创建一个玩游戏的棋盘。 我们创建一个状态栏。
```
```java
statusbar = new JLabel(" 0");
add(statusbar, BorderLayout.SOUTH);
......@@ -1064,7 +1064,7 @@ add(statusbar, BorderLayout.SOUTH);
分数显示在位于板底部的标签中。
```
```java
var board = new Board(this);
add(board);
board.start();
......
......@@ -16,7 +16,7 @@ Pacman 是一款街机游戏,最初由日本公司 Namco 在 1980 年开发。
`Board.java`
```
```java
package com.zetcode;
import java.awt.BasicStroke;
......@@ -649,14 +649,14 @@ public class Board extends JPanel implements ActionListener {
用光标键控制吃豆人。 `Esc` 键完成游戏,`暂停`键暂停游戏。
```
```java
private int pacman_x, pacman_y, pacmand_x, pacmand_y;
```
前两个变量存储吃豆子精灵的 x 和 y 坐标。 最后两个变量是水平和垂直方向的增量变化。
```
```java
private final short levelData[] = {
19, 26, 26, 26, 18, 18, 18, 18, ...
};
......@@ -665,7 +665,7 @@ private final short levelData[] = {
这些数字组成了迷宫。 它们提供了信息,我们可以据此创建角点。 1 号是左上角。 数字 2、4 和 8 分别代表顶角,右角和底角。 16 号是重点。 可以添加这些数字,例如左上角的数字 19 表示正方形将具有顶部和左侧边框以及一个点(`16 + 2 + 1`)。
```
```java
private void doAnim() {
pacAnimCount--;
......@@ -684,7 +684,7 @@ private void doAnim() {
`doAnim()``pacmanAnimPos`变量进行计数,该变量确定绘制哪种吃豆子图像。 有四个吃豆人图像。 还有一个`PAC_ANIM_DELAY`常数,它会使动画变慢。 否则,吃豆人会太快张开嘴。
```
```java
boolean finished = true;
while (i < N_BLOCKS * N_BLOCKS && finished) {
......@@ -702,21 +702,21 @@ while (i < N_BLOCKS * N_BLOCKS && finished) {
接下来,我们将研究`moveGhosts()`方法。 鬼魂移动一个正方形,然后决定是否改变方向。
```
```java
if (ghost_x[i] % BLOCK_SIZE == 0 && ghost_y[i] % BLOCK_SIZE == 0) {
```
我们仅在完成移动一个正方形后才继续。
```
```java
pos = pacman_x / BLOCK_SIZE + N_BLOCKS * (int) (pacman_y / BLOCK_SIZE);
```
这条线确定了幻影的位置; 在哪个位置/正方形。 有 225 个理论职位。 (鬼不能在墙上移动。)
```
```java
if ((screenData[pos] & 1) == 0 && ghost_dx[i] != 1) {
dx[count] = -1;
dy[count] = 0;
......@@ -727,7 +727,7 @@ if ((screenData[pos] & 1) == 0 && ghost_dx[i] != 1) {
如果左侧没有障碍物并且幻影尚未向右移动,则幻影将向左移动。 该代码的真正含义是什么? 如果幽灵进入隧道,他将朝着同一方向继续前进,直到他离开隧道。 鬼影的移动部分是随机的。 我们不会在长隧道中应用这种随机性,因为幽灵可能会卡在那里。
```
```java
if (pacman_x > (ghost_x[i] - 12) && pacman_x < (ghost_x[i] + 12)
&& pacman_y > (ghost_y[i] - 12) && pacman_y < (ghost_y[i] + 12)
&& inGame) {
......@@ -741,7 +741,7 @@ if (pacman_x > (ghost_x[i] - 12) && pacman_x < (ghost_x[i] + 12)
接下来,我们将研究`movePacman()`方法。 `req_dx``req_dy`变量在`TAdapter`内部类中确定。 这些变量由光标键控制。
```
```java
if ((ch & 16) != 0) {
screenData[pos] = (short) (ch & 15);
score++;
......@@ -751,7 +751,7 @@ if ((ch & 16) != 0) {
如果吃豆人移动到带点的位置,我们将其从迷宫中移出并增加得分值。
```
```java
if ((pacmand_x == -1 && pacmand_y == 0 && (ch & 1) != 0)
|| (pacmand_x == 1 && pacmand_y == 0 && (ch & 4) != 0)
|| (pacmand_x == 0 && pacmand_y == -1 && (ch & 2) != 0)
......@@ -764,7 +764,7 @@ if ((pacmand_x == -1 && pacmand_y == 0 && (ch & 1) != 0)
如果吃豆子无法按当前方向继续前进,则会停下来。
```
```java
private void drawPacman(Graphics2D g2d) {
if (view_dx == -1) {
......@@ -784,7 +784,7 @@ private void drawPacman(Graphics2D g2d) {
`drawMaze()`方法从`screenData`数组中的数字中提取迷宫。 数字 1 是左侧边框,2 是顶部边框,4 是右侧边框,8 是底部边框,16 是点。 我们只需在迷宫中浏览所有 225 平方。 例如,在`screenData`数组中有 9 个。 我们设置了第一位(1)和第四位(8)。 因此,我们在此特定正方形上绘制了底部和左侧边框。
```
```java
if ((screenData[i] & 1) != 0) {
g2d.drawLine(x, y, x, y + BLOCK_SIZE - 1);
}
......@@ -795,7 +795,7 @@ if ((screenData[i] & 1) != 0) {
`Pacman.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......
......@@ -14,7 +14,7 @@
`com/zetcode/SpaceInvaders.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......@@ -55,7 +55,7 @@ public class SpaceInvaders extends JFrame {
`com/zetcode/Commons.java`
```
```java
package com.zetcode;
public interface Commons {
......@@ -87,7 +87,7 @@ public interface Commons {
`com/zetcode/sprite/Alien.java`
```
```java
package com.zetcode.sprite;
import javax.swing.ImageIcon;
......@@ -161,7 +161,7 @@ public class Alien extends Sprite {
这是`Alien`子画面。 每个外星人都有一个内部的`Bomb`类。
```
```java
public void act(int direction) {
this.x += direction;
......@@ -171,7 +171,7 @@ public void act(int direction) {
`Board`类中调用`act()`方法。 用于在水平方向上定位外星人。
```
```java
public Bomb getBomb() {
return bomb;
......@@ -183,7 +183,7 @@ public Bomb getBomb() {
`com/zetcode/sprite/Player.java`
```
```java
package com.zetcode.sprite;
import com.zetcode.Commons;
......@@ -265,7 +265,7 @@ public class Player extends Sprite {
这是`Player`子画面。 我们用光标键控制大炮。
```
```java
int START_X = 270;
setX(START_X);
......@@ -276,7 +276,7 @@ setY(START_Y);
这些是播放器精灵的初始坐标。
```
```java
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
......@@ -291,7 +291,7 @@ public void keyPressed(KeyEvent e) {
如果按左光标键,则`dx`变量将设置为 -2。 下次调用`act()`方法时,播放器向左移动。
```
```java
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
......@@ -313,7 +313,7 @@ public void keyReleased(KeyEvent e) {
`com/zetcode/sprite/Shot.java`
```
```java
package com.zetcode.sprite;
import javax.swing.ImageIcon;
......@@ -348,7 +348,7 @@ public class Shot extends Sprite {
`com/zetcode/sprite/Sprite.java`
```
```java
package com.zetcode.sprite;
import java.awt.Image;
......@@ -430,7 +430,7 @@ public class Sprite {
`com/zetcode/Board.java`
```
```java
package com.zetcode;
import com.zetcode.sprite.Alien;
......@@ -808,7 +808,7 @@ public class Board extends JPanel {
游戏的主要逻辑位于`Board`类中。
```
```java
private void gameInit() {
aliens = new ArrayList<>();
......@@ -830,7 +830,7 @@ private void gameInit() {
`gameInit()`方法中,我们创建了 24 个外星人。 外星人图像尺寸为`12x12px`。 我们在外星人中间放了 6px 的空间。 我们还创建了播放器和射击对象。
```
```java
private void drawBombing(Graphics g) {
for (Alien a : aliens) {
......@@ -848,7 +848,7 @@ private void drawBombing(Graphics g) {
`drawBombing()`方法绘制由外星人发射的炸弹。
```
```java
if (inGame) {
g.drawLine(0, Commons.GROUND,
......@@ -865,7 +865,7 @@ if (inGame) {
`doDrawing()`方法中,我们绘制地面,外星人,玩家,射击和炸弹。
```
```java
private void update() {
if (deaths == Commons.NUMBER_OF_ALIENS_TO_DESTROY) {
......@@ -880,7 +880,7 @@ private void update() {
`update()`方法内,我们检查损坏的行数。 如果我们消灭所有外星人,我们将赢得比赛。
```
```java
if (alien.isVisible() && shot.isVisible()) {
if (shotX >= (alienX)
&& shotX <= (alienX + Commons.ALIEN_WIDTH)
......@@ -899,7 +899,7 @@ if (alien.isVisible() && shot.isVisible()) {
如果玩家触发的射击与外星人发生碰撞,则该外星人的船只将被摧毁。 更确切地说,将设置垂死标记。 我们用它来显示爆炸。 死亡变量增加,射击精灵被破坏。
```
```java
if (x >= Commons.BOARD_WIDTH - Commons.BORDER_RIGHT && direction != -1) {
direction = -1;
......@@ -917,7 +917,7 @@ if (x >= Commons.BOARD_WIDTH - Commons.BORDER_RIGHT && direction != -1) {
如果外星人到达`Board`的右端,他们将向下移动并将其方向更改为左侧。
```
```java
Iterator<Alien> it = aliens.iterator();
while (it.hasNext()) {
......@@ -941,7 +941,7 @@ Iterator<Alien> it = aliens.iterator();
此代码可移动外星人。 如果他们到达最低点,入侵就开始了。
```
```java
int shot = generator.nextInt(15);
Alien.Bomb bomb = alien.getBomb();
......@@ -956,7 +956,7 @@ if (shot == Commons.CHANCE && alien.isVisible() && bomb.isDestroyed()) {
这是确定外星人是否会投下炸弹的代码。 不得破坏外星人; 即他必须是可见的。 必须设置炸弹的销毁标志。 换句话说,这是外星人的第一枚炸弹掉落,或者是先前投下的炸弹已经落地。 如果满足这两个条件,轰炸就留给了机会。
```
```java
if (!bomb.isDestroyed()) {
bomb.setY(bomb.getY() + 1);
......@@ -971,7 +971,7 @@ if (!bomb.isDestroyed()) {
如果炸弹没有被销毁,它将离地面 1px。 如果到达底部,则设置销毁标志。 外星人现在准备投下另一枚炸弹。
```
```java
public void keyReleased(KeyEvent e) {
player.keyReleased(e);
......
......@@ -18,7 +18,7 @@
`com/zetcode/Board.java`
```
```java
package com.zetcode;
import java.awt.Dimension;
......@@ -402,7 +402,7 @@ public class Board extends JPanel {
首先,我们定义游戏中使用的常量。
```
```java
private final int NUM_IMAGES = 13;
private final int CELL_SIZE = 15;
......@@ -410,7 +410,7 @@ private final int CELL_SIZE = 15;
此游戏中使用了十三张图像。 一个牢房最多可被八个地雷包围,因此我们需要一号到八号。 我们需要一个空单元格,一个地雷,一个被遮盖的单元格,一个标记的单元格,最后需要一个错误标记的单元格的图像。 每个图像的大小为`15x15`像素。
```
```java
private final int COVER_FOR_CELL = 10;
private final int MARK_FOR_CELL = 10;
private final int EMPTY_CELL = 0;
......@@ -420,14 +420,14 @@ private final int EMPTY_CELL = 0;
地雷区是数字数组。 例如,0 表示一个空单元格。 数字 10 用于电池盖和标记。 使用常量可以提高代码的可读性。
```
```java
private final int MINE_CELL = 9;
```
`MINE_CELL`表示包含地雷的单元。
```
```java
private final int COVERED_MINE_CELL = MINE_CELL + COVER_FOR_CELL;
private final int MARKED_MINE_CELL = COVERED_MINE_CELL + MARK_FOR_CELL;
......@@ -435,7 +435,7 @@ private final int MARKED_MINE_CELL = COVERED_MINE_CELL + MARK_FOR_CELL;
`COVERED_MINE_CELL`用于覆盖并包含地雷的区域。 `MARKED_MINE_CELL`代码&gt;是由用户标记的隐蔽地雷单元。
```
```java
private final int DRAW_MINE = 9;
private final int DRAW_COVER = 10;
private final int DRAW_MARK = 11;
......@@ -445,7 +445,7 @@ private final int DRAW_WRONG_MARK = 12;
这些竞争因素决定是否绘制地雷,地雷覆盖物,标记和标记错误的单元。
```
```java
private final int N_MINES = 40;
private final int N_ROWS = 16;
private final int N_COLS = 16;
......@@ -454,28 +454,28 @@ private final int N_COLS = 16;
我们游戏中的雷区有 40 个隐藏的地雷。 该字段中有 16 行和 16 列。 因此,雷场中共有 262 个牢房。
```
```java
private int[] field;
```
该字段是数字数组。 字段中的每个单元格都有一个特定的编号。 例如,一个矿井的编号为 9。一个矿井的编号为 2 意味着它与两个矿井相邻。 数字已添加。 例如,一个被覆盖的地雷的编号为 19,地雷的编号为 9,电池盖的编号为 10,依此类推。
```
```java
private boolean inGame;
```
`inGame`变量确定我们是在游戏中还是游戏结束。
```
```java
private int minesLeft;
```
`minesLeft`变量要标记为左侧的地雷数量。
```
```java
for (int i = 0; i < NUM_IMAGES; i++) {
var path = "src/resources/" + i + ".png";
......@@ -488,7 +488,7 @@ for (int i = 0; i < NUM_IMAGES; i++) {
`newGame()`启动扫雷游戏。
```
```java
allCells = N_ROWS * N_COLS;
field = new int[allCells];
......@@ -501,7 +501,7 @@ for (int i = 0; i < allCells; i++) {
这些线设置了雷区。 默认情况下覆盖每个单元格。
```
```java
int i = 0;
while (i < N_MINES) {
......@@ -520,7 +520,7 @@ while (i < N_MINES) {
在白色周期中,我们将所有地雷随机放置在野外。
```
```java
cell = position - N_COLS;
if (cell >= 0) {
......@@ -535,7 +535,7 @@ if (cell >= 0) {
`find_empty_cells()`方法中,我们找到了空单元格。 如果玩家单击雷区,则游戏结束。 如果他单击与地雷相邻的单元,则会发现一个数字,该数字指示该单元与地雷相邻的数量。 单击一个空单元格会导致发现许多其他空单元格以及带有数字的单元格,这些数字在空边界的空间周围形成边界。 我们使用递归算法来查找空单元格。
```
```java
cell = j - 1;
if (cell <= 0) {
if (field[cell] > MINE_CELL) {
......@@ -552,7 +552,7 @@ if (cell <= 0) {
`paintComponent()`方法将数字转换为图像。
```
```java
if (!inGame) {
if (cell == COVERED_MINE_CELL) {
cell = DRAW_MINE;
......@@ -569,7 +569,7 @@ if (!inGame) {
如果游戏结束并且我们输了,我们将显示所有未发现的地雷(如果有的话),并显示所有错误标记的单元格(如果有)。
```
```java
g.drawImage(img[cell], (j * CELL_SIZE),
(i * CELL_SIZE), this);
......@@ -577,7 +577,7 @@ g.drawImage(img[cell], (j * CELL_SIZE),
此代码行绘制了窗口上的每个单元格。
```
```java
if (uncover == 0 && inGame) {
inGame = false;
......@@ -593,7 +593,7 @@ if (uncover == 0 && inGame) {
`mousePressed()`方法中,我们对鼠标单击做出反应。 扫雷游戏完全由鼠标控制。 我们对鼠标左键和右键单击做出反应。
```
```java
int x = e.getX();
int y = e.getY();
......@@ -601,7 +601,7 @@ int y = e.getY();
我们确定鼠标指针的 x 和 y 坐标。
```
```java
int cCol = x / CELL_SIZE;
int cRow = y / CELL_SIZE;
......@@ -609,21 +609,21 @@ int cRow = y / CELL_SIZE;
我们计算雷区的相应列和行。
```
```java
if ((x < N_COLS * CELL_SIZE) && (y < N_ROWS * CELL_SIZE)) {
```
我们检查我们是否位于雷区。
```
```java
if (e.getButton() == MouseEvent.BUTTON3) {
```
地雷的发现是通过鼠标右键完成的。
```
```java
field[(cRow * N_COLS) + cCol] += MARK_FOR_CELL;
minesLeft--;
......@@ -631,7 +631,7 @@ minesLeft--;
如果右键单击未标记的单元格,则将`MARK_FOR_CELL`添加到表示该单元格的数字中。 这导致在`paintComponent()`方法中绘制带有标记的覆盖单元。
```
```java
field[(cRow * N_COLS) + cCol] -= MARK_FOR_CELL;
minesLeft++;
......@@ -642,7 +642,7 @@ statusbar.setText(msg);
如果我们在已经标记的单元格上单击鼠标左键,我们将删除标记并增加要标记的单元格的数量。
```
```java
if (field[(cRow * N_COLS) + cCol] > COVERED_MINE_CELL) {
return;
}
......@@ -651,14 +651,14 @@ if (field[(cRow * N_COLS) + cCol] > COVERED_MINE_CELL) {
如果单击覆盖并标记的单元格,则不会发生任何事情。 必须首先通过另一次右键单击来发现它,然后才可以在其上单击鼠标左键。
```
```java
field[(cRow * N_COLS) + cCol] -= COVER_FOR_CELL;
```
左键单击可从单元中移除盖子。
```
```java
if (field[(cRow * N_COLS) + cCol] == MINE_CELL) {
inGame = false;
}
......@@ -671,7 +671,7 @@ if (field[(cRow * N_COLS) + cCol] == EMPTY_CELL) {
万一我们左键单击地雷,游戏就结束了。 如果我们在空白单元格上单击鼠标左键,我们将调用`find_empty_cells()`方法,该方法递归地找到所有相邻的空白单元格。
```
```java
if (doRepaint) {
repaint();
}
......@@ -682,7 +682,7 @@ if (doRepaint) {
`com/zetcode/Minesweeper.java`
```
```java
package com.zetcode;
import java.awt.BorderLayout;
......@@ -735,7 +735,7 @@ public class Minesweeper extends JFrame {
这是主要的类。
```
```java
setResizable(false);
```
......
......@@ -14,7 +14,7 @@
`Board.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -510,7 +510,7 @@ public class Board extends JPanel {
游戏简化了。 它仅提供非常基本的功能。 该代码比容易理解。 游戏只有一个关卡。
```
```java
private final int OFFSET = 30;
private final int SPACE = 20;
private final int LEFT_COLLISION = 1;
......@@ -522,7 +522,7 @@ private final int BOTTOM_COLLISION = 4;
墙图片大小为`20x20`像素。 这反映了`SPACE`常数。 `OFFSET`是窗口边界和游戏世界之间的距离。 有四种类型的碰撞。 每个数字都由一个数字常数表示。
```
```java
private ArrayList<Wall> walls;
private ArrayList<Baggage> baggs;
private ArrayList<Area> areas;
......@@ -531,7 +531,7 @@ private ArrayList<Area> areas;
墙壁,行李和区域是特殊的容器,可容纳游戏的所有墙壁,行李和区域。
```
```java
private String level =
" ######\n"
+ " ## #\n"
......@@ -549,7 +549,7 @@ private String level =
这是游戏的水平。 除空格外,还有五个字符。 井号(`#`)代表墙。 美元(`$`)表示要移动的框。 点(`.`)字符表示我们必须移动框的位置。 at 字符(`@`)是推箱子。 最后,换行符(`\n`)开始了世界的新行。
```
```java
private void initWorld() {
walls = new ArrayList<>();
......@@ -564,7 +564,7 @@ private void initWorld() {
`initWorld()`方法启动游戏世界。 它遍历级别字符串并填充上述列表。
```
```java
case '$':
b = new Baggage(x, y);
baggs.add(b);
......@@ -575,7 +575,7 @@ case '$':
对于美元字符,我们创建一个`Baggage`对象。 该对象将附加到行李列表。 x 变量相应增加。
```
```java
private void buildWorld(Graphics g) {
...
......@@ -583,7 +583,7 @@ private void buildWorld(Graphics g) {
`buildWorld()`方法在窗口上绘制游戏世界。
```
```java
ArrayList<Actor> world = new ArrayList<>();
world.addAll(walls);
......@@ -595,7 +595,7 @@ world.add(soko);
我们创建一个包含游戏所有对象的世界列表。
```
```java
for (int i = 0; i < world.size(); i++) {
Actor item = world.get(i);
......@@ -614,7 +614,7 @@ for (int i = 0; i < world.size(); i++) {
我们遍历世界容器并绘制对象。 播放器和行李图像稍小。 我们在其坐标上添加 2px 以使其居中。
```
```java
if (isCompleted) {
g.setColor(new Color(0, 0, 0));
......@@ -625,7 +625,7 @@ if (isCompleted) {
如果完成该级别,则在窗口的左上角绘制“已完成”。
```
```java
case KeyEvent.VK_LEFT:
if (checkWallCollision(soko,
......@@ -645,7 +645,7 @@ case KeyEvent.VK_LEFT:
`keyPressed()`方法内部,我们检查了按下了哪些键。 我们用光标键控制推箱子对象。 如果按左光标键,我们将检查推箱子是否与墙壁或行李相撞。 如果没有,我们将推箱子向左移动。
```
```java
case KeyEvent.VK_R:
restartLevel();
......@@ -656,7 +656,7 @@ case KeyEvent.VK_R:
如果按`R`键,我们将重新启动该级别。
```
```java
case LEFT_COLLISION:
for (int i = 0; i < walls.size(); i++) {
......@@ -675,7 +675,7 @@ case LEFT_COLLISION:
创建`checkWallCollision()`方法以确保推箱子或行李不会通过墙壁。 有四种类型的碰撞。 上面几行检查是否有左碰撞。
```
```java
private boolean checkBagCollision(int type) {
...
}
......@@ -684,7 +684,7 @@ private boolean checkBagCollision(int type) {
`checkBagCollision()`涉及更多。 行李可能会与墙壁,推箱子或其他行李发生碰撞。 仅当行李与推箱子碰撞且不与其他行李或墙壁碰撞时,才可以移动行李。 搬运行李时,该通过调用`isCompleted()`方法检查水平是否已完成。
```
```java
for (int i = 0; i < nOfBags; i++) {
Baggage bag = baggs.get(i);
......@@ -704,7 +704,7 @@ for (int i = 0; i < nOfBags; i++) {
`isCompleted()`方法检查级别是否完成。 我们得到行李数。 我们比较所有行李和目的地区域的 x 和 y 坐标。
```
```java
if (finishedBags == nOfBags) {
isCompleted = true;
......@@ -715,7 +715,7 @@ if (finishedBags == nOfBags) {
`finishedBags`变量等于游戏中的行李数时,游戏结束。
```
```java
private void restartLevel() {
areas.clear();
......@@ -735,7 +735,7 @@ private void restartLevel() {
`Actor.java`
```
```java
package com.zetcode;
import java.awt.Image;
......@@ -807,7 +807,7 @@ public class Actor {
这是`Actor`类。 该类是游戏中其他演员的基础类。 它封装了推箱子游戏中对象的基本功能。
```
```java
public boolean isLeftCollision(Actor actor) {
return x() - SPACE == actor.x() && y() == actor.y();
......@@ -819,7 +819,7 @@ public boolean isLeftCollision(Actor actor) {
`Wall.java`
```
```java
package com.zetcode;
import java.awt.Image;
......@@ -849,7 +849,7 @@ public class Wall extends Actor {
`Player.java`
```
```java
package com.zetcode;
import java.awt.Image;
......@@ -884,7 +884,7 @@ public class Player extends Actor {
这是`Player`类。
```
```java
public void move(int x, int y) {
int dx = x() + x;
......@@ -900,7 +900,7 @@ public void move(int x, int y) {
`Baggage.java`
```
```java
package com.zetcode;
import java.awt.Image;
......@@ -937,7 +937,7 @@ public class Baggage extends Actor {
`Area.java`
```
```java
package com.zetcode;
import java.awt.Image;
......@@ -965,7 +965,7 @@ public class Area extends Actor {
`Sokoban.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......
......@@ -60,7 +60,7 @@ Java 2D 是一项强大的技术。 它可以用来创建丰富的用户界面
`SimpleEx.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......@@ -119,7 +119,7 @@ public class BasicEx extends JFrame {
我们在`JPanel`组件上绘制文本。 Java 2D 教程中的许多代码都重复了。
```
```java
class Surface extends JPanel {
...
}
......@@ -128,21 +128,21 @@ class Surface extends JPanel {
我们创建一个`Surface`类。 此类将是我们的绘图面板。 它继承自`JPanel`组件。
```
```java
Graphics2D g2d = (Graphics2D) g;
```
`Graphics2D`类是用于在 Java 2D 中渲染图形的基本类。 它以通用方式表示设备数量。 它扩展了旧的`Graphics`对象。 要进行所有高级操作,必须进行此转换。
```
```java
g2d.drawString("Java 2D", 50, 50);
```
在这里,我们使用`drawString()`方法在面板上绘制一个字符串。
```
```java
@Override
public void paintComponent(Graphics g) {
......@@ -154,7 +154,7 @@ public void paintComponent(Graphics g) {
自定义绘画是在`paintComponent()`方法内部执行的,我们将其覆盖。 `super.paintComponent()`方法调用父类的方法。 准备用于绘图的组件会做一些必要的工作。 我们将图形委托给`doDrawing()`方法。
```
```java
private void initUI() {
...
}
......@@ -163,14 +163,14 @@ private void initUI() {
`initUI()`方法启动应用的用户界面。
```
```java
add(new Surface());
```
该表面已添加到`JFrame`容器中。
```
```java
EventQueue.invokeLater(new Runnable() {
@Override
......
......@@ -10,7 +10,7 @@
`PointsEx.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -122,7 +122,7 @@ public class PointsEx extends JFrame {
该示例在窗口上随机绘制 2000 个点。 计时器用于绘制循环中的点。
```
```java
private void initTimer() {
timer = new Timer(DELAY, this);
......@@ -133,14 +133,14 @@ private void initTimer() {
`javax.swing.Timer`用于创建动画。 它以指定的时间间隔触发`ActionEvents`
```
```java
g2d.setPaint(Color.blue);
```
这些点被涂成蓝色。
```
```java
int w = getWidth();
int h = getHeight();
......@@ -148,7 +148,7 @@ int h = getHeight();
我们得到组件的宽度和高度。
```
```java
Random r = new Random();
int x = Math.abs(r.nextInt()) % w;
int y = Math.abs(r.nextInt()) % h;
......@@ -157,14 +157,14 @@ int y = Math.abs(r.nextInt()) % h;
我们得到一个上面计算出的区域大小范围内的随机数。
```
```java
g2d.drawLine(x, y, x, y);
```
在这里,我们指出了这一点。 如前所述,我们使用`drawLine()`方法。 我们两次指定相同的点。
```
```java
@Override
public void actionPerformed(ActionEvent e) {
repaint();
......@@ -174,7 +174,7 @@ public void actionPerformed(ActionEvent e) {
每个动作事件,我们都调用`repaint()`方法。 这将导致整个客户区被重绘。
```
```java
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
......@@ -197,7 +197,7 @@ Figure: Points
`LinesEx.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......@@ -260,7 +260,7 @@ public class LinesEx extends JFrame {
我们用四个线画一个简单的对象。
```
```java
g2d.drawLine(30, 30, 200, 30);
```
......@@ -277,7 +277,7 @@ Figure: Lines
`BasicStrokesEx.java`
```
```java
package com.zetcode;
import java.awt.BasicStroke;
......@@ -369,14 +369,14 @@ public class BasicStrokesEx extends JFrame {
在此示例中,我们显示了各种破折号。 破折号属性是一种模式,通过混合不透明部分和透明部分来创建。
```
```java
Graphics2D g2d = (Graphics2D) g.create();
```
我们将更改`Graphics`对象的`stroke`属性; 因此,我们使用`Graphics`对象的副本。 (请记住,如果我们更改字体,颜色或渲染提示以外的属性,则必须创建一个副本。)
```
```java
float[] dash1 = { 2f, 0f, 2f };
float[] dash2 = { 1f, 1f, 1f };
float[] dash3 = { 4f, 0f, 2f };
......@@ -386,7 +386,7 @@ float[] dash4 = { 4f, 4f, 1f };
在这里,我们定义了四种不同的破折号模式。
```
```java
BasicStroke bs1 = new BasicStroke(1, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_ROUND, 1.0f, dash1, 2f );
......@@ -394,21 +394,21 @@ BasicStroke bs1 = new BasicStroke(1, BasicStroke.CAP_BUTT,
该行构造一个`BasicStroke`对象。
```
```java
g2d.setStroke(bs1);
```
我们使用`setStroke()`方法将`BasicStroke`应用于当前图形上下文。
```
```java
g2d.drawLine(20, 80, 250, 80);
```
`drawLine()`方法画一条线。
```
```java
g2d.dispose();
```
......@@ -429,7 +429,7 @@ Figure: Basic strokes
`CapsEx.java`
```
```java
package com.zetcode;
import java.awt.BasicStroke;
......@@ -522,7 +522,7 @@ public class CapsEx extends JFrame {
在我们的示例中,我们显示了所有三种类型的端盖。
```
```java
BasicStroke bs1 = new BasicStroke(8, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL);
g2d.setStroke(bs1);
......@@ -531,7 +531,7 @@ g2d.setStroke(bs1);
创建并应用带有对接盖的基本笔划。 `CAP_BUTT`不添加装饰。
```
```java
g2d.drawLine(20, 20, 20, 140);
g2d.drawLine(250, 20, 250, 140);
g2d.drawLine(254, 20, 254, 140);
......@@ -554,7 +554,7 @@ Figure: Caps
`JoinsEx.java`
```
```java
package com.zetcode;
import java.awt.BasicStroke;
......@@ -630,7 +630,7 @@ public class JoinsEx extends JFrame {
此代码示例显示了三个不同的线联接在起作用。
```
```java
BasicStroke bs1 = new BasicStroke(8, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_BEVEL);
g2d.setStroke(bs1);
......
......@@ -10,7 +10,7 @@
`BasicShapes.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -90,14 +90,14 @@ public class BasicShapesEx extends JFrame {
在此示例中,我们在面板上绘制了六个基本形状:正方形,矩形,圆角矩形,椭圆形,弧形和圆形。
```
```java
g2d.setPaint(new Color(150, 150, 150));
```
形状将以灰色背景绘制。
```
```java
g2d.fillRect(20, 20, 50, 50);
g2d.fillRect(120, 20, 90, 60);
......@@ -105,28 +105,28 @@ g2d.fillRect(120, 20, 90, 60);
`fillRect()`方法用于绘制矩形和正方形。 前两个参数是要绘制的形状的 x,y 坐标。 最后两个参数是形状的宽度和高度。
```
```java
g2d.fillRoundRect(250, 20, 70, 60, 25, 25);
```
在这里,我们创建一个圆角矩形。 最后两个参数是四个角处圆弧的水平和垂直直径。
```
```java
g2d.fill(new Ellipse2D.Double(10, 100, 80, 100));
```
`fill()`方法绘制给定形状的内部-椭圆。
```
```java
g2d.fillArc(120, 130, 110, 100, 5, 150);
```
`fillArc()`方法填充覆盖指定矩形的圆弧或椭圆弧。 圆弧是圆的圆周的一部分。
```
```java
g2d.fillOval(270, 130, 50, 50);
```
......@@ -145,7 +145,7 @@ Figure: Basic shapes
`StarEx.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -233,7 +233,7 @@ public class StarEx extends JFrame {
我们从一系列角度创造一颗星星。
```
```java
private final double points[][] = {
{ 0, 85 }, { 75, 75 }, { 100, 10 }, { 125, 75 },
{ 200, 85 }, { 150, 125 }, { 160, 190 }, { 100, 150 },
......@@ -244,21 +244,21 @@ private final double points[][] = {
这些是星星的坐标。
```
```java
GeneralPath star = new GeneralPath();
```
在这里,我们实例化了`GeneralPath`类。
```
```java
star.moveTo(points[0][0], points[0][1]);
```
我们移到`GeneralPath`的初始坐标。
```
```java
for (int k = 1; k < points.length; k++)
star.lineTo(points[k][0], points[k][1]);
......@@ -266,7 +266,7 @@ for (int k = 1; k < points.length; k++)
在这里,我们连接星的所有坐标。
```
```java
star.closePath();
g2d.fill(star);
......@@ -284,7 +284,7 @@ Figure: Star
`AreasEx.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -375,7 +375,7 @@ public class AreasEx extends JFrame {
该示例通过操纵区域来创建三种不同的形状。
```
```java
Area a1 = new Area(new Rectangle2D.Double(20, 20, 100, 100));
Area a2 = new Area(new Ellipse2D.Double(50, 50, 100, 100));
......@@ -386,7 +386,7 @@ g2d.fill(a1);
此代码通过从矩形中减去椭圆来构造形状。
```
```java
Area a5 = new Area(new Rectangle2D.Double(280, 20, 100, 100));
Area a6 = new Area(new Ellipse2D.Double(320, 40, 100, 100));
......@@ -407,7 +407,7 @@ Figure: Areas
`ColoursEx.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -493,21 +493,21 @@ public class ColoursEx extends JFrame {
在示例中,我们绘制了九个彩色矩形。
```
```java
Graphics2D g2d = (Graphics2D) g;
```
更改图形上下文的`color`属性时,无需创建`Graphics2D`类的副本或重置该值。
```
```java
g2d.setColor(new Color(125, 167, 116));
```
使用`Color`类创建新的颜色。 构造函数的参数是新颜色的红色,绿色和蓝色部分。 `setColor()`方法将图形上下文的当前颜色设置为指定的颜色值。 所有后续图形操作均使用此颜色值。
```
```java
g2d.fillRect(10, 15, 90, 60);
```
......@@ -524,7 +524,7 @@ Figure: Colours
`GradientsEx.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -616,7 +616,7 @@ public class GradientsEx extends JFrame {
我们的代码示例展示了五个带有渐变的矩形。
```
```java
GradientPaint gp4 = new GradientPaint(25, 25,
Color.blue, 15, 25, Color.black, true);
......@@ -624,7 +624,7 @@ GradientPaint gp4 = new GradientPaint(25, 25,
要使用渐变,我们使用`GradientPaint`类。 通过操纵颜色值以及起点和终点,我们可以获得不同的结果。
```
```java
g2d.setPaint(gp4);
```
......@@ -641,7 +641,7 @@ Figure: Gradients
`TexturesEx.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......@@ -749,21 +749,21 @@ public class TexturesEx extends JFrame {
在代码示例中,我们用三个不同的纹理填充三个矩形。
```
```java
slate = ImageIO.read(new File("slate.png"));
```
使用`ImageIO`类,我们将图像读取到缓冲图像中。
```
```java
slatetp = new TexturePaint(slate, new Rectangle(0, 0, 90, 60));
```
我们从缓冲图像中创建一个`TexturePaint`类。
```
```java
g2d.setPaint(slatetp);
g2d.fillRect(10, 15, 90, 60);
......
......@@ -19,7 +19,7 @@
`TransparentRectanglesEx.java`
```
```java
package com.zetcode;
import java.awt.AlphaComposite;
......@@ -92,14 +92,14 @@ public class TransparentRectanglesEx extends JFrame {
在我们的示例中,我们绘制了 10 个具有不同透明度级别的蓝色矩形。
```
```java
float alpha = i * 0.1f;
```
alpha 值在`for`循环中动态变化。
```
```java
AlphaComposite alcom = AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, alpha);
......@@ -107,7 +107,7 @@ AlphaComposite alcom = AlphaComposite.getInstance(
`AlphaComposite.getInstance()`方法使用指定的规则和常数 alpha 来创建`AlphaComposite`对象,以与源的 alpha 相乘。
```
```java
g2d.setComposite(alcom);
```
......@@ -124,7 +124,7 @@ Figure: Transparent rectangles
`FadeOutEx.java`
```
```java
package com.zetcode;
import java.awt.AlphaComposite;
......@@ -249,7 +249,7 @@ public class FadeOutEx extends JFrame {
使用`AlphaComposite`,我们逐渐淡出面板上的图像。
```
```java
private void setSurfaceSize() {
int h = img.getHeight(this);
......@@ -261,7 +261,7 @@ private void setSurfaceSize() {
`setSurfaceSize()`方法找出图像的尺寸并为面板设置首选尺寸。 首选尺寸与`pack()`方法的组合将显示恰好足以显示整个图像的窗口。
```
```java
private void initTimer() {
timer = new Timer(DELAY, this);
......@@ -273,7 +273,7 @@ private void initTimer() {
`initTimer()`方法启动一个计时器。 计时器在指定的初始延迟后触发操作事件。 在事件之间延迟之后会生成连续的动作事件。 为了响应动作事件,我们将更改 Alpha 值并重新绘制面板。
```
```java
AlphaComposite acomp = AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, alpha);
g2d.setComposite(acomp);
......@@ -283,7 +283,7 @@ g2d.drawImage(img, 0, 0, null);
此代码在面板上绘制具有增加的透明度的图像。
```
```java
private void step() {
alpha += -0.01f;
......@@ -299,7 +299,7 @@ private void step() {
`step()`方法代表淡出周期。 `alpha`逐渐降低。 请注意,alpha 值不得为负。 当达到零时,计时器停止。
```
```java
repaint();
```
......@@ -312,7 +312,7 @@ repaint();
`WaitingEx.java`
```
```java
package com.zetcode;
import java.awt.AlphaComposite;
......@@ -439,7 +439,7 @@ public class WaitingEx extends JFrame {
我们用八个不同的 alpha 值绘制八条线。
```
```java
private final double[][] trs = {
...
};
......@@ -448,7 +448,7 @@ private final double[][] trs = {
这是此演示中使用的透明度值的二维数组。 有 8 行,每行一种状态。 8 行中的每行将连续使用这些值。
```
```java
g2d.setStroke(new BasicStroke(STROKE_WIDTH, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND));
......@@ -456,7 +456,7 @@ g2d.setStroke(new BasicStroke(STROKE_WIDTH, BasicStroke.CAP_ROUND,
我们使线条更粗一些,以便更好地显示它们。 我们用圆帽画线。
```
```java
g2d.rotate(Math.PI/4f);
g2d.drawLine(0, -10, 0, -40);
......
......@@ -14,7 +14,7 @@
`CompositionEx.java`
```
```java
package com.zetcode;
import java.awt.AlphaComposite;
......@@ -100,7 +100,7 @@ public class CompositionEx extends JFrame {
我们绘制两个矩形,并将它们与六个不同的合成操作合并。
```
```java
private final int rules[] = {
AlphaComposite.DST,
AlphaComposite.DST_ATOP,
......@@ -114,14 +114,14 @@ private final int rules[] = {
在这里,我们有六个不同的合成规则。
```
```java
AlphaComposite ac = AlphaComposite.getInstance(rules[i], 0.8f);
```
在这里,我们得到`AlphaComposite`类。
```
```java
BufferedImage buffImg = new BufferedImage(60, 60,
BufferedImage.TYPE_INT_ARGB);
......@@ -129,28 +129,28 @@ BufferedImage buffImg = new BufferedImage(60, 60,
我们使用缓冲图像执行合成操作。
```
```java
Graphics2D gbi = buffImg.createGraphics();
```
使用`createGraphics()`方法从缓冲的图像创建`Graphics2D`对象。
```
```java
gbi.setComposite(ac);
```
`setComposite()`方法为`Graphics2D`上下文设置组合。
```
```java
g2d.drawImage(buffImg, x, y, null);
```
使用`drawImage()`方法在面板上绘制缓冲图像。
```
```java
gbi.dispose();
```
......@@ -167,7 +167,7 @@ Figure: Composition
`SunAndCloudEx.java`
```
```java
package com.zetcode;
import java.awt.AlphaComposite;
......@@ -291,7 +291,7 @@ public class SunAndCloudEx extends JFrame {
太阳来自云层背后。 云终于消失了。
```
```java
private void loadImages() {
sun = new ImageIcon("sun.png").getImage();
......@@ -302,7 +302,7 @@ private void loadImages() {
我们从磁盘加载两个映像。
```
```java
private void initTimer() {
timer = new Timer(DELAY, this);
......@@ -313,7 +313,7 @@ private void initTimer() {
`initTimer()`方法内部,计时器被激活。
```
```java
AlphaComposite ac = AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, alpha);
......@@ -321,7 +321,7 @@ AlphaComposite ac = AlphaComposite.getInstance(
我们使用`AlphaComposite.SRC_OVER`规则-源与目标混合并覆盖空白像素。
```
```java
gbi.drawImage(sun, 40, 30, null);
gbi.setComposite(ac);
gbi.drawImage(cloud, 0, 0, null);
......@@ -342,7 +342,7 @@ Figure: Sun & cloud
`SpotlightEx.java`
```
```java
package com.zetcode;
import java.awt.AlphaComposite;
......@@ -482,7 +482,7 @@ public class SpotlightEx extends JFrame {
使用构图规则和 Alpha 透明度值创建聚光灯效果。 还必须注意,我们的图像具有透明背景。
```
```java
BufferedImage bi = new BufferedImage(getWidth(),
getHeight(), BufferedImage.TYPE_INT_ARGB);
......@@ -490,7 +490,7 @@ BufferedImage bi = new BufferedImage(getWidth(),
创建了`BufferedImage`。 它的尺寸等于面板的尺寸。 我们的 PNG 文件具有透明背景; 因此,我们使用`BufferedImage.TYPE_INT_ARGB`图像类型。
```
```java
if (mouseIn) {
bigr.fillOval(x - RADIUS, y - RADIUS, RADIUS * 2,
RADIUS * 2);
......@@ -502,7 +502,7 @@ if (mouseIn) {
如果鼠标在面板区域中,则`AlphaComposite.SrcAtop`规则用于在鼠标指针周围绘制一个完全不透明的圆圈。
```
```java
bigr.setComposite(AlphaComposite.SrcOver.derive(0.1f));
bigr.drawImage(image, midX, midY, iw, ih, this);
......@@ -510,7 +510,7 @@ bigr.drawImage(image, midX, midY, iw, ih, this);
这两行描绘了图像的其余部分。 `AlphaComposite.SrcOver`规则用于创建高度透明的图像,并将其与背景混合。
```
```java
g2d.drawImage(bi, 0, 0, getWidth(), getHeight(), this);
```
......
......@@ -12,7 +12,7 @@
`ClippingEx.java`
```
```java
package com.zetcode;
import java.awt.Dimension;
......@@ -151,21 +151,21 @@ public class ClippingEx extends JFrame {
屏幕上正在移动一个圆圈,并显示了一部分基础图像。 这就像我们从孔中看一样。
```
```java
Graphics2D g2d = (Graphics2D) g.create();
```
我们创建`Graphics2D`对象的副本。 因此,更改剪辑不会影响其他在`Graphics2D`对象被重用的 Swing 零件。
```
```java
g2d.clip(new Ellipse2D.Double(pos_x, pos_y, RADIUS, RADIUS));
```
`clip()`方法将现有剪辑与作为参数给出的形状结合在一起。 所得的相交设置为片段。 在我们的例子中,最终的剪辑是圆形。
```
```java
if (pos_x < 0) {
delta[0] = Math.random() % 4 + 5;
......@@ -178,7 +178,7 @@ if (pos_x < 0) {
如果圆碰到窗口的左侧或右侧,则圆的移动方向会随机变化。 顶部和底部也一样。
```
```java
g2d.dispose();
```
......@@ -191,7 +191,7 @@ g2d.dispose();
`ClippingShapesEx.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -348,14 +348,14 @@ public class ClippingShapesEx extends JFrame {
在我们的示例中,我们有一个弹跳的圆和一个旋转的矩形。 当这些形状重叠时,结果区域将充满颜色。
```
```java
Shape oldClip = g2d.getClip();
```
由于我们没有创建`Graphics2D`对象的副本,因此我们将存储旧剪辑以供以后使用。 最后,我们必须将剪辑重置为原始剪辑。
```
```java
Rectangle rect = new Rectangle(0, 0, 200, 80);
AffineTransform tx = new AffineTransform();
......@@ -366,7 +366,7 @@ tx.translate(w / 2 - 100, h / 2 - 40);
矩形正在旋转。 它始终位于面板的中间。
```
```java
GeneralPath path = new GeneralPath();
path.append(tx.createTransformedShape(rect), false);
......@@ -374,7 +374,7 @@ path.append(tx.createTransformedShape(rect), false);
在这里,我们得到了旋转矩形的形状。
```
```java
g2d.clip(circle);
g2d.clip(path);
......@@ -385,7 +385,7 @@ g2d.fill(circle);
在这里,我们将绘图限制为两个形状的交点。 如果它们重叠,则结果形状的内部将充满颜色。 `clip()`方法将初始剪辑(组件的客户区域)与给定的两个形状组合在一起。
```
```java
g2d.setClip(oldClip);
```
......
......@@ -14,7 +14,7 @@
`TranslationEx.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -80,7 +80,7 @@ public class TranslationEx extends JFrame {
该示例绘制一个矩形。 然后,我们进行平移并再次绘制相同的矩形。
```
```java
g2d.translate(150, 50);
```
......@@ -97,7 +97,7 @@ Figure: Translation
`RotationEx.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -165,7 +165,7 @@ public class RotationEx extends JFrame {
该示例绘制一个矩形,执行平移和旋转,然后再次绘制相同的矩形。
```
```java
g2d.rotate(Math.PI/4);
```
......@@ -182,7 +182,7 @@ Figure: Rotation
`ScalingEx.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -261,7 +261,7 @@ public class ScalingEx extends JFrame {
我们有一个矩形。 首先,我们将其按比例缩小,然后再按比例放大。
```
```java
AffineTransform tx2 = new AffineTransform();
tx2.translate(170, 20);
tx2.scale(1.5, 1.5);
......@@ -280,7 +280,7 @@ Figure: Scaling
`ShearingEx.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -367,7 +367,7 @@ public class ShearingEx extends JFrame {
在此示例中,我们以三种不同的颜色绘制了三个矩形。 它们形成一个结构。 他们两个被剪掉了。
```
```java
tx2.shear(0, 1);
```
......@@ -384,7 +384,7 @@ Figure: Shearing
`DonutEx.java`
```
```java
package com.zetcode;
import java.awt.BasicStroke;
......@@ -471,7 +471,7 @@ public class DonutEx extends JFrame {
在此示例中,我们创建一个甜甜圈形状。
```
```java
Ellipse2D e = new Ellipse2D.Double(0, 0, 80, 130);
g2d.setStroke(new BasicStroke(1));
g2d.setPaint(Color.gray);
......@@ -480,7 +480,7 @@ g2d.setPaint(Color.gray);
刚开始时有一个椭圆。
```
```java
for (double deg = 0; deg < 360; deg += 5) {
AffineTransform at =
AffineTransform.getTranslateInstance(w / 2, h / 2);
......
......@@ -10,7 +10,7 @@
`BubblesEx.java`
```
```java
package com.zetcode;
import java.awt.BasicStroke;
......@@ -186,7 +186,7 @@ public class BubblesEx extends JFrame {
这是泡泡的示例。
```
```java
private final Color colors[] = {
Color.blue, Color.cyan, Color.green,
Color.magenta, Color.orange, Color.pink,
......@@ -197,7 +197,7 @@ private final Color colors[] = {
这些颜色用于绘制气泡。
```
```java
private void initSurface() {
setBackground(Color.black);
......@@ -210,7 +210,7 @@ private void initSurface() {
`initSurface()`方法为面板设置黑色背景。 我们创建三个数组。 椭圆数组(椭圆是椭圆的一种特殊情况),每个椭圆大小的数组以及椭圆笔划的数组。 动画期间,气泡的大小和笔触都会增加。
```
```java
private void initEllipses() {
int w = 350;
......@@ -229,7 +229,7 @@ private void initEllipses() {
`ellipses`数组填充有椭圆对象。 `posRandEllipses()`方法将椭圆对象随机放置在窗口上。 椭圆的初始尺寸也是随机选择的。
```
```java
private void initTimer() {
timer = new Timer(DELAY, this);
......@@ -241,7 +241,7 @@ private void initTimer() {
将创建并启动一个计时器对象。 用于创建动画。
```
```java
private void posRandEllipses(int i, double size, int w, int h) {
esize[i] = size;
......@@ -255,7 +255,7 @@ private void posRandEllipses(int i, double size, int w, int h) {
`posRandEllipses()`方法将椭圆随机放置在窗口上。 `esize``estroke`数组填充有值。 `setFrame()`方法设置椭圆框架矩形的位置和大小。
```
```java
private void doStep(int w, int h) {
for (int i = 0; i < ellipses.length; i++) {
......@@ -278,7 +278,7 @@ private void doStep(int w, int h) {
动画包括步骤。 在每个步骤中,我们增加每个椭圆的笔触和大小值。 气泡达到最大大小后,将其重置为最小大小,并在面板上随机重新放置。 否则,将显示增加的值。
```
```java
private void drawEllipses(Graphics2D g2d) {
for (int i = 0; i < ellipses.length; i++) {
......@@ -293,7 +293,7 @@ private void drawEllipses(Graphics2D g2d) {
`drawEllipses()`方法从面板上的阵列绘制所有椭圆。
```
```java
Dimension size = getSize();
doStep(size.width, size.height);
......@@ -301,7 +301,7 @@ doStep(size.width, size.height);
`doDrawing()`方法中,我们计算面板的尺寸。 如果调整窗口大小,气泡将随机分布在窗口的整个区域。
```
```java
@Override
public void actionPerformed(ActionEvent e) {
......@@ -322,7 +322,7 @@ Figure: Bubbles
`StarDemoEx.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......@@ -455,7 +455,7 @@ public class StarDemoEx extends JFrame {
在此演示中,我们有一颗星星。 星星旋转生长然后收缩。
```
```java
private final int points[][] = {
{0, 85}, {75, 75}, {100, 10}, {125, 75},
{200, 85}, {150, 125}, {160, 190}, {100, 150},
......@@ -466,7 +466,7 @@ private final int points[][] = {
这些点用于绘制星形。
```
```java
private double angle = 0;
private double scale = 1;
private double delta = 0.01;
......@@ -475,14 +475,14 @@ private double delta = 0.01;
当我们旋转星星时使用`angle``scale`因子确定星星的大小。 最后,`delta`因子是刻度的变化量。
```
```java
g2d.translate(w / 2, h / 2);
```
使用`translate()`方法将坐标系移到窗口的中间。
```
```java
GeneralPath star = new GeneralPath();
star.moveTo(points[0][0], points[0][1]);
......@@ -495,7 +495,7 @@ for (int k = 1; k < points.length; k++) {
`GeneralPath`用于创建星形。 通过`moveTo()`方法将第一点添加到路径。 通过`lineTo()`方法添加星星的后续点。
```
```java
g2d.rotate(angle);
g2d.scale(scale, scale);
......@@ -503,14 +503,14 @@ g2d.scale(scale, scale);
我们执行旋转和缩放操作。
```
```java
g2d.fill(star);
```
`fill()`方法填充星形的内部。
```
```java
if (scale < 0.01) {
delta = -delta;
......@@ -529,7 +529,7 @@ if (scale < 0.01) {
`PuffEx.java`
```
```java
package com.zetcode;
import java.awt.AlphaComposite;
......@@ -661,7 +661,7 @@ public class PuffEx extends JFrame {
该示例在窗口上绘制了一个不断增长的文本,从某个角度看,该文本变得越来越透明,直到看不见为止。
```
```java
Font font = new Font("Dialog", Font.PLAIN, x);
g2d.setFont(font);
......@@ -669,21 +669,21 @@ g2d.setFont(font);
这是我们用于文本的字体。
```
```java
FontMetrics fm = g2d.getFontMetrics();
```
`getFontMetrics()`返回`FontMetrics`类。 该类存储有关在特定屏幕上呈现特定字体的信息。
```
```java
int stringWidth = fm.stringWidth(s);
```
我们使用`FontMetrics`对象的`stringWidth()`方法来获取字符串的宽度。
```
```java
AlphaComposite ac = AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, alpha);
g2d.setComposite(ac);
......@@ -692,14 +692,14 @@ g2d.setComposite(ac);
在这里,我们设置所绘制文本的透明度。
```
```java
g2d.drawString(s, (w - stringWidth) / 2, h / 2);
```
此代码行在窗口的(水平)中间绘制字符串。
```
```java
if (x > 40)
alpha -= 0.01;
......
......@@ -12,7 +12,7 @@
`DisplayImageEx.java`
```
```java
package com.zetcode;
import java.awt.Dimension;
......@@ -95,7 +95,7 @@ public class DisplayImageEx extends JFrame {
在示例中,我们在面板上显示图像。 调整窗口大小以适合图像的大小。
```
```java
private void loadImage() {
mshi = new ImageIcon("mushrooms.jpg").getImage();
......@@ -105,7 +105,7 @@ private void loadImage() {
我们使用`ImageIcon`类加载图像。 该图像位于当前工作目录中。
```
```java
private void setSurfaceSize() {
Dimension d = new Dimension();
......@@ -118,7 +118,7 @@ private void setSurfaceSize() {
我们确定加载图像的大小。 使用`setPreferredSize()`方法,我们设置`Surface`面板的首选大小。 `JFrame`容器的`pack()`方法将使框架适合其子代的大小。 在我们的例子中是`Surface`面板。 结果,窗口将被调整大小以精确显示加载的图像。
```
```java
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
......@@ -129,7 +129,7 @@ private void doDrawing(Graphics g) {
使用`drawImage()`方法在面板上绘制图像。 最后一个参数是`ImageObserver`类。 有时用于异步加载。 当我们不需要异步加载图像时,可以将`null`放在此处。
```
```java
private void initUI() {
...
pack();
......@@ -148,7 +148,7 @@ private void initUI() {
`GrayScaleImage.java`
```
```java
package com.zetcode;
import java.awt.Dimension;
......@@ -246,7 +246,7 @@ public class GrayScaleImageEx extends JFrame {
有几种创建灰度图像的方法。 我们通过将图像数据写入`BufferedImage.TYPE_BYTE_GRAY`类型的缓冲图像中来实现。
```
```java
bufimg = new BufferedImage(d.width, d.height,
BufferedImage.TYPE_BYTE_GRAY);
......@@ -254,7 +254,7 @@ bufimg = new BufferedImage(d.width, d.height,
我们创建一个`BufferedImage.TYPE_BYTE_GRAY`类型的`BufferedImage`类。
```
```java
Graphics2D g2d = bufimg.createGraphics();
g2d.drawImage(mshi, 0, 0, null);
......@@ -262,14 +262,14 @@ g2d.drawImage(mshi, 0, 0, null);
在这里,我们将蘑菇图像绘制到缓冲图像中。
```
```java
g2d.dispose();
```
使用`createGraphics()`方法创建的图形对象应手动释放。 当这些对象返回时,作为对象的`paint()``update()`方法的参数提供的图形对象将由系统自动释放。
```
```java
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
......@@ -286,7 +286,7 @@ private void doDrawing(Graphics g) {
`FlippedImageEx.java`
```
```java
package com.zetcode;
import java.awt.Dimension;
......@@ -393,7 +393,7 @@ public class FlippedImageEx extends JFrame {
在我们的代码示例中,我们水平翻转图像。
```
```java
AffineTransform tx = AffineTransform.getScaleInstance(-1, 1);
tx.translate(-castle.getWidth(null), 0);
......@@ -401,7 +401,7 @@ tx.translate(-castle.getWidth(null), 0);
翻转图像意味着对其进行缩放和平移。 因此,我们进行了`AffineTransform`操作。
```
```java
AffineTransformOp op = new AffineTransformOp(tx,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
bufferedImage = op.filter(bufferedImage, null)
......@@ -410,7 +410,7 @@ bufferedImage = op.filter(bufferedImage, null)
这是可用的过滤操作之一。 这也可以通过像素操纵来完成。 但是 Java 2D 提供了高级类,使操作图像更加容易。 在我们的情况下,`AffineTransformOp`类对图像像素执行缩放和平移。
```
```java
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
......@@ -423,7 +423,7 @@ private void doDrawing(Graphics g) {
这两个图像都画在面板上。
```
```java
private void setSurfaceSize() {
int w = bufimg.getWidth();
......@@ -445,7 +445,7 @@ private void setSurfaceSize() {
`BlurredImageEx.java`
```
```java
package com.zetcode;
import java.awt.Dimension;
......@@ -553,7 +553,7 @@ public class BlurredImageEx extends JFrame {
在代码示例中,我们从磁盘加载图像,对图像执行模糊操作,然后在窗口上显示结果。
```
```java
private void loadImage() {
try {
......@@ -570,7 +570,7 @@ private void loadImage() {
`ImageIO`类的`read()`方法从磁盘读取图像并返回`BufferedImage`
```
```java
float[] blurKernel = {
1 / 9f, 1 / 9f, 1 / 9f,
1 / 9f, 1 / 9f, 1 / 9f,
......@@ -581,7 +581,7 @@ float[] blurKernel = {
该矩阵称为内核。 所述值是应用于改变的像素的相邻值的权重。
```
```java
BufferedImageOp blur = new ConvolveOp(new Kernel(3, 3, blurKernel));
bluri = blur.filter(mshi, new BufferedImage(mshi.getWidth(),
mshi.getHeight(), mshi.getType()));
......@@ -590,7 +590,7 @@ bluri = blur.filter(mshi, new BufferedImage(mshi.getWidth(),
在这里,我们对图像应用模糊滤镜。
```
```java
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
......@@ -607,7 +607,7 @@ private void doDrawing(Graphics g) {
`ReflectionEx.java`
```
```java
package com.zetcode;
import java.awt.AlphaComposite;
......@@ -746,7 +746,7 @@ public class ReflectionEx extends JFrame {
在示例中,我们创建了反射图像的错觉。
```
```java
refImage = new BufferedImage(img_w, img_h,
BufferedImage.TYPE_INT_ARGB);
Graphics2D rg = refImage.createGraphics();
......@@ -756,7 +756,7 @@ rg.drawImage(image, 0, 0, null);
创建已加载图像的副本。
```
```java
rg.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN));
rg.setPaint(new GradientPaint(0, img_h * fadeHeight,
new Color(0.0f, 0.0f, 0.0f, 0.0f), 0, img_h,
......@@ -768,7 +768,7 @@ rg.fillRect(0, 0, img_w, img_h);
这是代码中最重要的部分。 我们使第二个图像透明。 但是透明度不是恒定不变的。 图像逐渐淡出。 这是通过`GradientPaint`类实现的。
```
```java
g2d.setPaint(new GradientPaint(0, 0, Color.black, 0,
win_h, Color.darkGray));
g2d.fillRect(0, 0, win_w, win_h);
......@@ -777,7 +777,7 @@ g2d.fillRect(0, 0, win_w, win_h);
窗口的背景填充有渐变颜料。 涂料是从黑色到深灰色的平滑混合。
```
```java
g2d.translate((win_w - img_w) / 2, win_h / 2 - img_h);
g2d.drawImage(image, 0, 0, null);
......@@ -785,7 +785,7 @@ g2d.drawImage(image, 0, 0, null);
普通图像将移动到窗口的中心并绘制。
```
```java
g2d.translate(0, 2 * imageHeight + gap);
g2d.scale(1, -1);
......@@ -793,14 +793,14 @@ g2d.scale(1, -1);
此代码翻转图像并将其转换为原始图像下方。 平移操作是必需的,因为缩放操作会使图像上下颠倒并向上平移图像。 要了解发生了什么,只需拍摄照片并将其放在桌子上并翻转即可。
```
```java
g2d.drawImage(refImage, 0, 0, null);
```
绘制反射的图像。
```
```java
@Override
public Dimension getPreferredSize() {
......
......@@ -20,7 +20,7 @@
`AllFontsEx.java`
```
```java
package com.zetcode;
import java.awt.Font;
......@@ -44,21 +44,21 @@ public class AllFontsEx {
我们打印每种已安装字体的名称和系列。
```
```java
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
```
有些对象是特定平台的典型对象,其中包括字体。 Unix,OS X 和 Windows 平台上的字体集合有所不同。 `GraphicsEnvironment`类描述特定平台上可用的`GraphicsDevice`对象和`Font`对象的集合。
```
```java
Font[] fonts = ge.getAllFonts();
```
`getAllFonts()`返回`GraphicsEnvironment`中可用的所有字体。
```
```java
System.out.print(fonts[i].getFontName() + " : ");
System.out.println(fonts[i].getFamily());
......@@ -66,7 +66,7 @@ System.out.println(fonts[i].getFamily());
字体名称和字体系列会打印到终端上。
```
```java
...
URW Bookman L Demi Bold : URW Bookman L
URW Bookman L Demi Bold Italic : URW Bookman L
......@@ -98,7 +98,7 @@ Ubuntu Italic : Ubuntu
`SoulmateEx.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......@@ -177,14 +177,14 @@ public class SoulmateEx extends JFrame {
在此示例中,我们在面板上绘制文本。 我们选择一种特定的字体类型。
```
```java
g2d.setFont(new Font("Purisa", Font.PLAIN, 13));
```
在这里,我们设置 Purisa 字体类型。
```
```java
g2d.drawString("Most relationships seem so transitory", 20, 30);
```
......@@ -199,7 +199,7 @@ Figure: Soulmate
下一个示例演示如何显示 Unicode 文本。 请注意,在实际应用中,文本将放置在代码之外的单独资源中。
```
```java
$ cat fyodor
Фёдор Михайлович Достоевский родился 30 октября (11 ноября) 1821 года в Москве.
Был вторым из 7 детей. Отец, Михаил Андреевич, работал в госпитале для бедных.
......@@ -209,14 +209,14 @@ $ cat fyodor
我们有一个名为`fyodor`的文件,其中的文本在西里尔字母中。
```
```java
$ native2ascii fyodor unicode
```
我们使用名为`native2ascii`的工具,可以在 jdk 的`bin`目录中找到该工具。 它将带有本地编码字符的文件转换为带有 Unicode 编码字符的文件。 第一个参数是输入文件,第二个参数是输出文件。
```
```java
$ cat unicode
\u0424\u0451\u0434\u043e\u0440 \u041c\u0438\u0445\u0430\u0439\u043b\u043e\u0432\u0438\u0447
...
......@@ -227,7 +227,7 @@ unicode 编码中的相同文本。
`UnicodeEx.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......@@ -345,7 +345,7 @@ public class UnicodeEx extends JFrame {
请注意,文本将超出实际程序中的源代码范围。 为了简化起见,此处将文本保留在源代码中。
```
```java
String sent1 = "\u0424\u0451\u0434\u043e\u0440 \u041c\u0438\u0445" +
...
......@@ -353,7 +353,7 @@ String sent1 = "\u0424\u0451\u0434\u043e\u0440 \u041c\u0438\u0445" +
这是第一个 Unicode 句子。
```
```java
g2d.drawString(sent1, 20, 30);
```
......@@ -370,7 +370,7 @@ Figure: Unicode
`ShadowedTextEx.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -475,14 +475,14 @@ public class ShadowedTextEx extends JFrame {
这次我们不使用`paintComponent()`方法。 相反,我们绘制成缓冲的图像。
```
```java
Font font = new Font("Georgia", Font.ITALIC, 50);
```
我们选择斜体格鲁吉亚,大小为 50 点。
```
```java
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
......@@ -490,28 +490,28 @@ BufferedImage image = new BufferedImage(width, height,
我们创建第一个缓冲图像。
```
```java
Graphics2D g1d = image.createGraphics();
```
从缓冲的图像创建一个`Graphics2D`对象。 它将用于绘制缓冲图像。
```
```java
textLayout = new TextLayout(text, font, g1d.getFontRenderContext());
```
我们创建一个`TextLayout`类。 `TextLayout`是样式字符数据的不可变图形表示。 它用于对文本和字体进行高级操作。
```
```java
textLayout.draw(g1d, x+3, y+3);
```
`draw()`方法在指定的`Graphics2D`上下文中的指定位置呈现此`TextLayout`。 第二和第三个参数指定`TextLayout`的原点坐标。
```
```java
float[] kernel = {
1f / 9f, 1f / 9f, 1f / 9f,
1f / 9f, 1f / 9f, 1f / 9f,
......@@ -525,14 +525,14 @@ ConvolveOp op = new ConvolveOp(new Kernel(3, 3, kernel),
此操作将产生模糊效果。 该效果用于阴影文本。
```
```java
BufferedImage image2 = op.filter(image, null);
```
我们对第一张图像应用模糊效果,然后将结果复制到第二张缓冲的图像。
```
```java
textLayout.draw(g2d, x, y);
```
......@@ -549,7 +549,7 @@ Figure: Shadowed text
`TextAttributesEx.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -640,21 +640,21 @@ public class TextAttributesEx extends JFrame {
在我们的示例中,我们演示了各种文本呈现方式。
```
```java
AttributedString as1 = new AttributedString(words);
```
我们从`words`字符串中创建一个`AttributeString`
```
```java
as1.addAttribute(TextAttribute.FOREGROUND, Color.red, 0, 6);
```
在这里,我们向`AttributeString`类添加了一个新属性。 此属性指定前七个字符将显示为红色。
```
```java
g2d.drawString(as1.getIterator(), 15, 60);
```
......@@ -673,7 +673,7 @@ Figure: Text Attributes
`RotatedTextEx.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......@@ -767,42 +767,42 @@ public class RotatedTextEx extends JFrame {
这是一个旋转的文本示例。
```
```java
String s = "ZetCode, tutorials for programmers";
```
我们旋转此文本。 因为文本采用 Latin1 编码,所以字形以一对一方式对应于字符。
```
```java
GlyphVector gv = font.createGlyphVector(frc, s);
```
在这里,我们创建一个`GlyphVector`对象。 `GlyphVector`是字形及其位置的集合。
```
```java
int length = gv.getNumGlyphs();
```
在这里,我们获得文本的字形数量。 如果将数字打印到控制台,则得到 34。因此,在本例中,每个字符都是一个字形。
```
```java
Point2D p = gv.getGlyphPosition(i);
```
遍历字形向量时,我们使用`getGlyphPosition()`方法计算字形的位置。
```
```java
double theta = (double) i / (double) (length - 1) * Math.PI / 3;
```
我们计算字形旋转的程度。
```
```java
AffineTransform at = AffineTransform.getTranslateInstance(p.getX(),
p.getY());
at.rotate(theta);
......@@ -811,7 +811,7 @@ at.rotate(theta);
我们进行仿射旋转变换。
```
```java
Shape glyph = gv.getGlyphOutline(i);
Shape transformedGlyph = at.createTransformedShape(glyph);
......@@ -819,7 +819,7 @@ Shape transformedGlyph = at.createTransformedShape(glyph);
`getGlyphOutline()`方法返回指定字形的`Shape``createTransformedShape()`方法返回通过仿射变换操作修改的新`Shape`对象。
```
```java
g2d.fill(transformedGlyph);
```
......
......@@ -10,7 +10,7 @@
`HitTestingEx.java`
```
```java
package com.zetcode;
import java.awt.AlphaComposite;
......@@ -201,7 +201,7 @@ public class HitTestingEx extends JFrame {
在我们的示例中,我们有两个`Shapes`:一个矩形和一个圆形。 通过单击它们,它们逐渐开始消失。 在此示例中,我们使用线程。
```
```java
private Rectangle2D rect;
private Ellipse2D ellipse;
......@@ -209,7 +209,7 @@ private Ellipse2D ellipse;
我们使用矩形和椭圆形。
```
```java
private float alpha_rectangle;
private float alpha_ellipse;
......@@ -217,7 +217,7 @@ private float alpha_ellipse;
这两个变量控制两个几何对象的透明度。
```
```java
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
alpha_rectangle));
g2d.fill(rect);
......@@ -228,7 +228,7 @@ g2d.fill(rect);
`HitTestAdapter`类负责处理鼠标事件。 它确实实现了`Runnable`接口,这意味着它还创建了第一个线程。
```
```java
if (ellipse.contains(x, y)) {
ellipseAnimator = new Thread(this);
......@@ -239,7 +239,7 @@ if (ellipse.contains(x, y)) {
如果我们在椭圆内按下,将创建一个新的`Thread`。 该线程调用`run()`方法。 在我们的例子中,它是类本身的`run()`方法(`HitTestAdapter`)。
```
```java
if (rect.contains(x, y)) {
rectAnimator = new RectRunnable();
......@@ -249,7 +249,7 @@ if (rect.contains(x, y)) {
对于矩形,我们有一个单独的内部类-`RectRunnable`类。 此类在构造函数中创建自己的线程。
```
```java
public void run() {
while (alpha_ellipse >= 0) {
......@@ -273,7 +273,7 @@ Figure: Hit testing
`MovingScalingEx.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -515,7 +515,7 @@ public class MovingScalingEx extends JFrame {
在我们的代码示例中,我们有两个图形对象:一个矩形和一个圆形。 我们可以通过单击它们并拖动它们来移动它们。 我们还可以通过将鼠标光标放在对象上并移动鼠标滚轮来放大或缩小它们。
```
```java
private ZRectangle zrect;
private ZEllipse zell;
......@@ -523,7 +523,7 @@ private ZEllipse zell;
正如我们已经提到的,面板上有一个矩形和一个椭圆。 这两个类都扩展了 Java AWT 包中内置类的功能。
```
```java
addMouseMotionListener(ma);
addMouseListener(ma);
addMouseWheelListener(new ScaleHandler());
......@@ -532,7 +532,7 @@ addMouseWheelListener(new ScaleHandler());
我们注册了三个侦听器。 这些侦听器捕获鼠标按下,鼠标拖动和鼠标滚轮事件。
```
```java
class ZEllipse extends Ellipse2D.Float {
public ZEllipse(float x, float y, float width, float height) {
......@@ -553,7 +553,7 @@ class ZEllipse extends Ellipse2D.Float {
`MovingAdapter`类处理鼠标按下和鼠标拖动事件。
```
```java
@Override
public void mousePressed(MouseEvent e) {
......@@ -565,7 +565,7 @@ public void mousePressed(MouseEvent e) {
`mousePressed()`方法中,我们存储对象的初始 x 和 y 坐标。
```
```java
int dx = e.getX() - x;
int dy = e.getY() - y;
......@@ -573,7 +573,7 @@ int dy = e.getY() - y;
`doMove()`方法内部,我们计算拖动对象的距离。
```
```java
if (zrect.isHit(x, y)) {
zrect.addX(dx);
......@@ -585,7 +585,7 @@ if (zrect.isHit(x, y)) {
如果在矩形区域内,则更新矩形的 x 和 y 坐标并重新绘制面板。
```
```java
x += dx;
y += dy;
......@@ -595,7 +595,7 @@ y += dy;
`ScaleHandler`类处理对象的缩放。
```
```java
if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
if (zrect.isHit(x, y)) {
......@@ -618,7 +618,7 @@ if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
`ResizingRectangleEx.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......@@ -752,21 +752,21 @@ public class ResizingRectangleEx extends JFrame {
有两种创建矩形的方法。 一种方法是提供左上角点的 x 和 y 坐标以及矩形的宽度和高度。 另一种方法是提供左上角和右下角点。 在我们的代码示例中,我们将同时使用这两种方法。
```
```java
private Point2D[] points;
```
在此数组中,我们存储构成矩形的点。
```
```java
private final int SIZE = 8;
```
这是黑色小矩形的大小。
```
```java
points = new Point2D[2];
points[0] = new Point2D.Double(50, 50);
points[1] = new Point2D.Double(150, 100);
......@@ -775,7 +775,7 @@ points[1] = new Point2D.Double(150, 100);
这些是矩形的初始坐标。
```
```java
for (int i = 0; i < points.length; i++) {
double x = points[i].getX() - SIZE / 2;
......@@ -787,7 +787,7 @@ for (int i = 0; i < points.length; i++) {
此代码绘制了两个小的控制矩形。
```
```java
Rectangle2D s = new Rectangle2D.Double();
s.setFrameFromDiagonal(points[0], points[1]);
......@@ -797,7 +797,7 @@ g2.draw(s);
在这里,我们从这些点绘制一个矩形。
```
```java
@Override
public void mousePressed(MouseEvent event) {
......@@ -822,7 +822,7 @@ public void mousePressed(MouseEvent event) {
`mousePressed()`方法中,我们确定是否单击了两个控制点之一。 如果我们点击其中一个,则`pos`变量将存储其中的哪个。
```
```java
@Override
public void mouseDragged(MouseEvent event) {
......
......@@ -29,7 +29,7 @@ Figure: Tetrominoes
`Tetris.java`
```
```java
package com.zetcode;
import java.awt.BorderLayout;
......@@ -84,7 +84,7 @@ public class Tetris extends JFrame {
`Tetris.java`文件中,我们设置了游戏。 我们创建一个玩游戏的棋盘。 我们创建一个状态栏。
```
```java
board.start();
```
......@@ -93,7 +93,7 @@ board.start();
`Shape.java`
```
```java
package com.zetcode;
import java.util.Random;
......@@ -214,7 +214,7 @@ public class Shape {
`Shape`类提供有关俄罗斯方块的信息。
```
```java
protected enum Tetrominoes { NoShape, ZShape, SShape, LineShape,
TShape, SquareShape, LShape, MirroredLShape };
......@@ -222,7 +222,7 @@ protected enum Tetrominoes { NoShape, ZShape, SShape, LineShape,
`Tetrominoes`枚举拥有所有七个俄罗斯方块形状。 加上此处称为`NoShape`的空形状。
```
```java
public Shape() {
coords = new int[4][2];
......@@ -233,7 +233,7 @@ public Shape() {
这是`Shape`类的构造函数。 `coords`数组保存俄罗斯方块的实际坐标。
```
```java
coordsTable = new int[][][] {
{ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
{ { 0, -1 }, { 0, 0 }, { -1, 0 }, { -1, 1 } },
......@@ -249,7 +249,7 @@ coordsTable = new int[][][] {
`coordsTable`数组保存我们的俄罗斯方块的所有可能的坐标值。 这是一个模板,所有零件均从该模板获取其坐标值。
```
```java
for (int i = 0; i < 4 ; i++) {
for (int j = 0; j < 2; ++j) {
......@@ -268,7 +268,7 @@ for (int i = 0; i < 4 ; i++) {
Figure: Coordinates
```
```java
public Shape rotateLeft() {
if (pieceShape == Tetrominoes.SquareShape)
......@@ -292,7 +292,7 @@ public Shape rotateLeft() {
`Board.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -625,7 +625,7 @@ public class Board extends JPanel
最后,我们有`Board.java`文件。 这是游戏逻辑所在的位置。
```
```java
...
private boolean isFallingFinished = false;
private boolean isStarted = false;
......@@ -639,14 +639,14 @@ private int curY = 0;
我们初始化一些重要的变量。 `isFallingFinished`变量确定俄罗斯方块形状是否已完成下降,然后我们需要创建一个新形状。 `numLinesRemoved`计算行数,到目前为止我们已经删除了行数。 `curX``curY`变量确定下降的俄罗斯方块形状的实际位置。
```
```java
setFocusable(true);
```
我们必须显式调用`setFocusable()`方法。 从现在开始,开发板具有键盘输入。
```
```java
timer = new Timer(400, this);
timer.start();
......@@ -654,7 +654,7 @@ timer.start();
`Timer`对象在指定的延迟后触发一个或多个操作事件。 在我们的例子中,计时器每 400ms 调用一次`actionPerformed()`方法。
```
```java
@Override
public void actionPerformed(ActionEvent e) {
......@@ -674,7 +674,7 @@ public void actionPerformed(ActionEvent e) {
`doDrawing()`方法内部,我们在板上绘制了所有对象。 这幅画有两个步骤。
```
```java
for (int i = 0; i < BoardHeight; ++i) {
for (int j = 0; j < BoardWidth; ++j) {
......@@ -691,7 +691,7 @@ for (int i = 0; i < BoardHeight; ++i) {
在第一步中,我们绘制所有形状或已掉落到板底部的形状的其余部分。 所有正方形都记在板阵列中。 我们使用`shapeAt()`方法访问它。
```
```java
if (curPiece.getShape() != Tetrominoes.NoShape) {
for (int i = 0; i < 4; ++i) {
......@@ -708,7 +708,7 @@ if (curPiece.getShape() != Tetrominoes.NoShape) {
在第二步中,我们绘制实际的下降部分。
```
```java
private void dropDown() {
int newY = curY;
......@@ -727,7 +727,7 @@ private void dropDown() {
如果按空格键,则该片段将落到底部。 我们只是简单地尝试将一块下降到另一条俄罗斯方块下降的底部或顶部。
```
```java
private void clearBoard() {
for (int i = 0; i < BoardHeight * BoardWidth; ++i)
......@@ -738,7 +738,7 @@ private void clearBoard() {
`clearBoard()`方法用空的`NoShapes`填充电路板。 稍后将其用于碰撞检测。
```
```java
private void pieceDropped() {
for (int i = 0; i < 4; ++i) {
......@@ -758,7 +758,7 @@ private void pieceDropped() {
`pieceDropped()`方法将下降的碎片放入板阵列中。 木板再次保持了所有碎片的正方形和已经落下的碎片的剩余部分。 当一块完成落下时,就该检查是否可以去除板上的一些线了。 这是`removeFullLines()`方法的工作。 然后,我们创建一个新作品。 更准确地说,我们尝试创建一个新作品。
```
```java
private void newPiece() {
curPiece.setRandomShape();
......@@ -778,7 +778,7 @@ private void newPiece() {
`newPiece()`方法创建一个新的俄罗斯方块。 作品获得了新的随机形状。 然后,我们计算初始`curX``curY`值。 如果我们不能移动到初始位置,则游戏结束。 我们加油。 计时器停止。 我们将游戏放在状态栏上的字符串上方。
```
```java
private boolean tryMove(Shape newPiece, int newX, int newY) {
for (int i = 0; i < 4; ++i) {
......@@ -806,7 +806,7 @@ private boolean tryMove(Shape newPiece, int newX, int newY) {
`tryMove()`方法尝试移动俄罗斯方块。 如果该方法已达到板边界或与已经跌落的俄罗斯方块相邻,则返回`false`
```
```java
int numFullLines = 0;
for (int i = BoardHeight - 1; i >= 0; --i) {
......@@ -834,7 +834,7 @@ for (int i = BoardHeight - 1; i >= 0; --i) {
每个俄罗斯方块都有四个正方形。 每个正方形都使用`drawSquare()`方法绘制。 俄罗斯方块有不同的颜色。
```
```java
g.setColor(color.brighter());
g.drawLine(x, y + squareHeight() - 1, x, y);
g.drawLine(x, y, x + squareWidth() - 1, y);
......@@ -845,7 +845,7 @@ g.drawLine(x, y, x + squareWidth() - 1, y);
我们使用键盘控制游戏。 控制机制通过`KeyAdapter`实现。 这是一个覆盖`keyPressed()`方法的内部类。
```
```java
case KeyEvent.VK_LEFT:
tryMove(curPiece, curX - 1, curY);
break;
......
......@@ -36,7 +36,7 @@ Cario 支持各种后端。
这些示例使用 C 编程语言创建。 我们使用 GNU C 编译器来编译它们。
```
```c
gcc example.c -o example `pkg-config --cflags --libs gtk+-3.0`
```
......
......@@ -14,7 +14,7 @@
`Board.java`
```
```java
package com.zetcode;
import javax.swing.JPanel;
......@@ -30,7 +30,7 @@ public class Board extends JPanel {
`Application.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......@@ -67,35 +67,35 @@ public class Application extends JFrame {
这是游戏的切入点。 这里我们有主要方法。
```
```java
add(new Board());
```
在这里,我们将`Board`放置在`JFrame`容器的中心。
```
```java
setSize(250, 200);
```
此行设置窗口的大小。
```
```java
setDefaultCloseOperation(EXIT_ON_CLOSE);
```
当我们单击关闭按钮时,这将关闭应用。 这不是默认行为。
```
```java
setLocationRelativeTo(null);
```
`null`传递给`setLocationRelativeTo()`方法时,窗口将在屏幕上居中。
```
```java
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
......@@ -114,7 +114,7 @@ public static void main(String[] args) {
`Board.java`
```
```java
package com.zetcode;
import java.awt.BasicStroke;
......@@ -170,7 +170,7 @@ public class Board extends JPanel {
绘画是在`paintComponent()`方法内完成的。
```
```java
private void drawDonut(Graphics g) {
...
}
......@@ -179,14 +179,14 @@ private void drawDonut(Graphics g) {
将实际绘画委派给特定方法是一种很好的编程习惯。
```
```java
Graphics2D g2d = (Graphics2D) g;
```
`Graphics2D`类扩展了`Graphics`类。 它提供了对几何图形,坐标转换,颜色管理和文本布局的更复杂的控制。
```
```java
RenderingHints rh
= new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
......@@ -200,7 +200,7 @@ g2d.setRenderingHints(rh);
渲染提示用于使绘图平滑。
```
```java
Dimension size = getSize();
double w = size.getWidth();
double h = size.getHeight();
......@@ -209,7 +209,7 @@ double h = size.getHeight();
我们得到窗口的高度和宽度。 我们需要它们将甜甜圈形状在窗口上居中。
```
```java
Ellipse2D e = new Ellipse2D.Double(0, 0, 80, 130);
g2d.setStroke(new BasicStroke(1));
g2d.setColor(Color.gray);
......@@ -218,7 +218,7 @@ g2d.setColor(Color.gray);
在这里,我们创建椭圆。
```
```java
for (double deg = 0; deg < 360; deg += 5) {
AffineTransform at
= AffineTransform.getTranslateInstance(w/2, h/2);
......@@ -232,7 +232,7 @@ for (double deg = 0; deg < 360; deg += 5) {
`Donut.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......@@ -275,7 +275,7 @@ public class DonutExample extends JFrame {
`Board.java`
```
```java
package com.zetcode;
import java.awt.Dimension;
......@@ -319,28 +319,28 @@ public class Board extends JPanel {
我们为董事会制作了一个城镇形象。 图像绘制在`paintComponent()`方法内部。
```
```java
ImageIcon ii = new ImageIcon("src/resources/bardejov.png");
```
我们创建一个`ImageIcon`
```
```java
bardejov = ii.getImage();
```
我们从`ImageIcon`中得到一个`Image`
```
```java
g.drawImage(bardejov, 0, 0, null);
```
我们在窗口上绘制图像。
```
```java
int w = bardejov.getWidth(this);
int h = bardejov.getHeight(this);
setPreferredSize(new Dimension(w, h));
......@@ -351,7 +351,7 @@ setPreferredSize(new Dimension(w, h));
`ImageExample.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......
......@@ -28,7 +28,7 @@ Cairo 中的绘制是通过上下文完成的。 Cairo 上下文包含所有描
该文档提到以下方面:
```
```c
typedef enum _cairo_surface_type {
CAIRO_SURFACE_TYPE_IMAGE,
CAIRO_SURFACE_TYPE_PDF,
......
......@@ -8,7 +8,7 @@ Cairo 库支持各种后端。 在 Cairo 图形教程的这一部分中,我们
在第一个示例中,我们将创建一个 PNG 图像。
```
```c
#include <cairo.h>
int main(void)
......@@ -39,14 +39,14 @@ int main(void)
本示例是一个小型控制台应用,它将创建一个 PNG 图像。
```
```c
#include <cairo.h>
```
在此头文件中,我们将找到函数和常量的声明。
```
```c
cairo_surface_t *surface;
cairo_t *cr;
......@@ -54,7 +54,7 @@ cairo_t *cr;
在这里,我们声明一个表面和一个 Cairo 上下文。
```
```c
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 390, 60);
cr = cairo_create(surface);
......@@ -62,14 +62,14 @@ cr = cairo_create(surface);
我们创建一个表面和一个 Cairo 上下文。 表面是 390x60 像素的图像。
```
```c
cairo_set_source_rgb(cr, 0, 0, 0);
```
我们将用黑色墨水绘制。
```
```c
cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, 40.0);
......@@ -78,7 +78,7 @@ cairo_set_font_size(cr, 40.0);
我们选择一种字体类型并设置其大小。
```
```c
cairo_move_to(cr, 10.0, 50.0);
cairo_show_text(cr, "Disziplin ist Macht.");
......@@ -86,14 +86,14 @@ cairo_show_text(cr, "Disziplin ist Macht.");
我们移到图像内的(10.0,50.0)位置并绘制文本。
```
```c
cairo_surface_write_to_png(surface, "image.png");
```
此函数调用将创建 PNG 图像。
```
```c
cairo_destroy(cr);
cairo_surface_destroy(surface);
......@@ -105,7 +105,7 @@ cairo_surface_destroy(surface);
在第二个示例中,我们将使用 Cairo 库创建一个简单的 PDF 文件。
```
```c
#include <cairo.h>
#include <cairo-pdf.h>
......@@ -137,14 +137,14 @@ int main(void)
我们必须在 PDF 查看器中打开 PDF 文件。 Linux 用户可以使用 KPDF 或 Evince 查看器。
```
```c
surface = cairo_pdf_surface_create("pdffile.pdf", 504, 648);
```
要渲染 PDF 文件,我们必须使用`cairo_pdf_surface_create()`函数调用来创建 PDF 曲面。 PDF 文件的大小以磅为单位指定,这是排版的标准。
```
```c
cairo_show_page(cr);
```
......@@ -159,7 +159,7 @@ Figure: PDF file in Evince
下一个示例创建一个简单的 SVG(可缩放矢量图形)文件。 SVG 是当今最热门的技术之一。
```
```c
#include <cairo.h>
#include <cairo-svg.h>
......@@ -189,14 +189,14 @@ int main(void)
我们可以使用 Firefox,Opera 或 Inkscape 程序打开`svgfile.svg`文件。
```
```c
surface = cairo_svg_surface_create("svgfile.svg", 390, 60);
```
要在 Cairo 创建 SVG 文件,我们必须使用`cairo_svg_surface_create()`函数调用来创建 svg 曲面。
```
```c
cr = cairo_create(surface);
```
......@@ -213,7 +213,7 @@ SVG file in Chrome
在最后一个示例中,我们将在 GTK 窗口上绘制。 该后端将在本教程的其余部分中使用。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
......@@ -270,7 +270,7 @@ int main(int argc, char *argv[])
该示例弹出一个居中的 GTK 窗口,在该窗口上绘制“ Disziplin ist Macht”文本。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
......@@ -278,7 +278,7 @@ int main(int argc, char *argv[])
我们包括必要的 Cairo 和 GTK 标头。
```
```c
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
gpointer user_data)
{
......@@ -291,7 +291,7 @@ static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
我们将实际图形委托给`do_drawing()`功能。 发送的参数是 Cairo 上下文。
```
```c
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0, 0, 0);
......@@ -307,7 +307,7 @@ static void do_drawing(cairo_t *cr)
Cairo 功能执行绘图。
```
```c
darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(window), darea);
......@@ -315,7 +315,7 @@ gtk_container_add(GTK_CONTAINER(window), darea);
我们创建一个`GtkDrawingArea`小部件,并将其添加到容器窗口。 用于自定义绘图。
```
```c
g_signal_connect(G_OBJECT(darea), "draw",
G_CALLBACK(on_draw_event), NULL);
......
......@@ -8,7 +8,7 @@
线是非常基本的矢量对象。 要画一条线,我们使用两个函数调用。 通过`cairo_move_to()`调用指定起点。 线的终点通过`cairo_line_to()`调用指定。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
......@@ -99,7 +99,7 @@ int main(int argc, char *argv[])
在我们的示例中,我们用鼠标左键随机单击一个窗口。 每次点击都存储在一个数组中。 当我们右键单击窗口时,所有点都与数组中的每个点相连。 这样,我们可以创建一些有趣的对象。 右键单击绘制的对象将清除窗口,我们可以单击另一个对象。
```
```c
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_set_line_width (cr, 0.5);
......@@ -107,7 +107,7 @@ cairo_set_line_width (cr, 0.5);
线条将用黑色墨水绘制,宽度为 0.5 点。
```
```c
int i, j;
for (i = 0; i <= glob.count - 1; i++ ) {
for (j = 0; j <= glob.count - 1; j++ ) {
......@@ -120,14 +120,14 @@ for (i = 0; i <= glob.count - 1; i++ ) {
我们将数组中的每个点连接到其他每个点。
```
```c
cairo_stroke(cr);
```
`cairo_stroke()`调用画线。
```
```c
g_signal_connect(window, "button-press-event",
G_CALLBACK(clicked), NULL);
......@@ -135,7 +135,7 @@ g_signal_connect(window, "button-press-event",
我们将`button-press-event`连接到单击的回调。
```
```c
if (event->button == 1) {
glob.coordx[glob.count] = event->x;
glob.coordy[glob.count++] = event->y;
......@@ -145,7 +145,7 @@ if (event->button == 1) {
在单击的回调中,我们确定是单击鼠标左键还是单击鼠标右键。 如果单击鼠标左键,则将 x,y 坐标存储到数组中。
```
```c
if (event->button == 3) {
gtk_widget_queue_draw(widget);
}
......@@ -162,7 +162,7 @@ Figure: Lines
描边操作绘制形状的轮廓,填充操作填充形状的内部。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
#include <math.h>
......@@ -227,14 +227,14 @@ int main (int argc, char *argv[])
在我们的示例中,我们将绘制一个圆并用纯色填充它。
```
```c
#include <math.h>
```
`M_PI`常量需要此头文件。
```
```c
GtkWidget *win = gtk_widget_get_toplevel(widget);
int width, height;
......@@ -244,7 +244,7 @@ gtk_window_get_size(GTK_WINDOW(win), &width, &height);
在这里,我们获得了窗口的宽度和高度。 绘制圆时,将需要这些值。 当我们调整窗口大小时,圆圈将被调整大小。
```
```c
cairo_set_line_width(cr, 9);
cairo_set_source_rgb(cr, 0.69, 0.19, 0);
......@@ -252,7 +252,7 @@ cairo_set_source_rgb(cr, 0.69, 0.19, 0);
我们使用`set_line_width()`方法设置线宽。 我们使用`set_source_rgb()`方法将光源设置为深红色。
```
```c
cairo_translate(cr, width/2, height/2);
cairo_arc(cr, 0, 0, 50, 0, 2 * M_PI);
cairo_stroke_preserve(cr);
......@@ -261,7 +261,7 @@ cairo_stroke_preserve(cr);
使用`cairo_translate()`方法,我们将图形原点移动到窗口的中心。 我们希望我们的圈子居中。 `arc()`方法向 Cairo 绘图上下文添加了新的圆形路径。 最后,`stroke_preserve()`方法绘制圆的轮廓。 与`stroke()`方法不同,它还保留了形状以供以后绘制。
```
```c
cairo_set_source_rgb(cr, 0.3, 0.4, 0.6);
cairo_fill(cr);
......@@ -277,7 +277,7 @@ Figure: Fill and stroke
每条线可以用不同的笔划线绘制。 它定义了线条的样式。 破折号由`cairo_stroke()`函数调用使用。 破折号由`cairo_set_dash()`功能指定。 该模式由虚线数组设置,该数组是一个正浮点值的数组。 他们设置破折号图案的开和关部分。 我们还指定了数组的长度和偏移值。 如果长度为 0,则禁用虚线。 如果为 1,则假定对称图案,并交替显示由虚线表示的单个值指定的大小的打开和关闭部分。
```
```c
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgba(cr, 0, 0, 0, 1);
......@@ -315,28 +315,28 @@ static void do_drawing(cairo_t *cr)
在此示例中,我们将绘制三条具有不同破折号图案的线。
```
```c
static const double dashed1[] = {4.0, 21.0, 2.0};
```
我们有三个数字的模式。 我们得出 4 分,未得出 21 分,得出 2 分。 然后,未绘制 4 点,未绘制 21 点和未绘制 2 点。 该模式轮流直到行尾。
```
```c
static int len1 = sizeof(dashed1) / sizeof(dashed1[0]);
```
我们得到数组的大小。
```
```c
cairo_set_dash(cr, dashed1, len1, 0);
```
我们设置破折号。
```
```c
static const double dashed3[] = {1.0};
...
cairo_set_dash(cr, dashed3, 1, 0);
......@@ -369,7 +369,7 @@ Figure: Square, round and butt caps
带有`CAIRO_LINE_CAP_SQUARE`上限的行的大小将不同于带有`CAIRO_LINE_CAP_BUTT`上限的行。 如果一条线的宽度为 px 宽,则带有`CAIRO_LINE_CAP_SQUARE`上限的线的宽度将恰好为 px 宽度。 开头为`width / 2`像素,结尾为`width / 2`像素。
```
```c
static void do_drawing(cairo_t *cr)
{
cairo_set_line_width(cr, 10);
......@@ -408,14 +408,14 @@ static void do_drawing(cairo_t *cr)
该示例绘制了具有三个不同上限的三条线。 它还将以图形方式显示行大小的差异。
```
```c
cairo_set_line_width(cr, 10);
```
我们的线将是 10 像素宽。
```
```c
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
cairo_move_to(cr, 30, 90);
cairo_line_to(cr, 150, 90);
......@@ -425,7 +425,7 @@ cairo_stroke(cr);
在这里,我们用`CAIRO_LINE_CAP_ROUND`帽画一条水平线。
```
```c
cairo_set_line_width(cr, 1.5);
cairo_move_to(cr, 30, 40);
......@@ -454,7 +454,7 @@ Figure: Bevel, Round, Miter line joins
`CAIRO_LINE_JOIN_BEVEL`使用切除连接,其中切除距离接合点的线宽一半。 `CAIRO_LINE_JOIN_ROUND`使用圆形连接,其中圆心是连接点。 `CAIRO_LINE_JOIN_MITER`使用了一个尖角。
```
```c
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0.1, 0, 0);
......@@ -479,7 +479,7 @@ static void do_drawing(cairo_t *cr)
在此示例中,我们绘制了三个具有各种线连接的粗矩形。
```
```c
cairo_rectangle(cr, 30, 30, 100, 100);
cairo_set_line_width(cr, 14);
cairo_set_line_join(cr, CAIRO_LINE_JOIN_MITER);
......
......@@ -8,7 +8,7 @@
Cairo API 具有一些用于创建简单形状的基本功能。
```
```c
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
......@@ -39,7 +39,7 @@ static void do_drawing(cairo_t *cr)
在此示例中,我们将创建一个矩形,正方形,圆形,弧形和椭圆形。
```
```c
cairo_rectangle(cr, 20, 20, 120, 80);
cairo_rectangle(cr, 180, 20, 80, 80);
......@@ -47,14 +47,14 @@ cairo_rectangle(cr, 180, 20, 80, 80);
`cairo_rectangle()`用于创建正方形和矩形。 正方形只是矩形的一种特定类型。
```
```c
cairo_arc(cr, 330, 60, 40, 0, 2*M_PI);
```
这条线创建一个圆。
```
```c
cairo_scale(cr, 1, 0.7);
cairo_arc(cr, 0, 0, 50, 0, 2*M_PI);
......@@ -68,7 +68,7 @@ Figure: Basic shapes
可以使用基本图元的组合来创建其他形状。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
......@@ -159,7 +159,7 @@ int main(int argc, char *argv[])
在此示例中,我们将星形对象创建为三角形和修改后的三角形。 这些对象是使用直线和一条曲线创建的。
```
```c
gint i;
for (i = 0; i < 10; i++ ) {
cairo_line_to(cr, points[i][0], points[i][1]);
......@@ -171,7 +171,7 @@ cairo_close_path(cr);
通过连接点数组中的所有点来绘制星形。 通过调用`cairo_close_path()`函数将星星结束,该函数将星星的最后两个点连接在一起。
```
```c
cairo_move_to(cr, 380, 40);
cairo_line_to(cr, 380, 160);
cairo_line_to(cr, 450, 160);
......@@ -193,7 +193,7 @@ Figure: Other shapes
颜色是代表红色,绿色和蓝色(RGB)强度值的组合的对象。 Cairo 有效 RGB 值在 0 到 1 的范围内。
```
```c
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0.5, 0.5, 1);
......@@ -217,7 +217,7 @@ static void do_drawing(cairo_t *cr)
在示例中,我们绘制了四个彩色矩形。
```
```c
cairo_set_source_rgb(cr, 0.5, 0.5, 1);
cairo_rectangle(cr, 20, 20, 100, 100);
cairo_fill(cr);
......@@ -234,7 +234,7 @@ Figure: Solid colours
图案是可以填充形状的复杂图形对象。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
......@@ -343,14 +343,14 @@ int main(int argc, char *argv[])
我们在`on_draw_event()`功能之外创建图像表面。 每次需要重新绘制窗口时,每次从硬盘读取数据都不是很有效。
```
```c
pattern1 = cairo_pattern_create_for_surface(surface1);
```
我们通过调用`cairo_pattern_create_for_surface()`函数从表面创建图案。
```
```c
cairo_set_source(cr, pattern1);
cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
cairo_rectangle(cr, 20, 20, 100, 100);
......
......@@ -10,7 +10,7 @@
线性渐变是沿着一条线的颜色混合或颜色阴影混合。 它们是使用`cairo_pattern_create_linear()`功能创建的。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
......@@ -123,14 +123,14 @@ int main(int argc, char *argv[])
该示例绘制了三个填充有线性渐变的矩形。
```
```c
pat3 = cairo_pattern_create_linear(20.0, 260.0, 20.0, 360.0);
```
在这里,我们创建一个线性渐变图案。 参数指定直线,沿着该直线绘制渐变。 在我们的情况下,这是一条垂直线。
```
```c
cairo_pattern_add_color_stop_rgb(pat3, 0.1, 0, 0, 0);
cairo_pattern_add_color_stop_rgb(pat3, 0.5, 1, 1, 0);
cairo_pattern_add_color_stop_rgb(pat3, 0.9, 0, 0, 0);
......@@ -147,7 +147,7 @@ Figure: Linear gradients
径向渐变是两个圆之间颜色或阴影的混合。 `cairo_pattern_create_radial()`函数用于在 Cairo 创建径向渐变。
```
```c
#include <cairo.h>
#include <math.h>
#include <gtk/gtk.h>
......@@ -229,7 +229,7 @@ int main(int argc, char *argv[])
在示例中,我们绘制了两个径向渐变。
```
```c
r1 = cairo_pattern_create_radial(30, 30, 10, 30, 30, 90);
cairo_pattern_add_color_stop_rgba(r1, 0, 1, 1, 1, 1);
cairo_pattern_add_color_stop_rgba(r1, 1, 0.6, 0.6, 0.6, 1);
......@@ -241,7 +241,7 @@ cairo_fill(cr);
我们画一个圆,并用径向渐变填充其内部。 径向梯度由两个圆定义。 `cairo_pattern_add_color_stop_rgba()`功能定义颜色。 我们可以试验圆的位置或半径的长度。 在第一个渐变示例中,我们创建了一个类似于 3D 形状的对象。
```
```c
r2 = cairo_pattern_create_radial(0, 0, 10, 0, 0, 40);
cairo_pattern_add_color_stop_rgb(r2, 0, 1, 1, 0);
cairo_pattern_add_color_stop_rgb(r2, 0.8, 0, 0, 0);
......
......@@ -13,7 +13,7 @@
第一个示例将绘制十个透明度不同的矩形。
```
```c
static void do_drawing(cairo_t *cr)
{
gint i;
......@@ -36,7 +36,7 @@ Figure: Transparency
在以下示例中,我们创建一个粉扑效果。 该示例将显示一个不断增长的居中文本,该文本将从某个点逐渐淡出。 这是一个非常常见的效果,我们经常可以在 Flash 动画中看到它。 `cairo_paint_with_alpha()`方法对于产生效果至关重要。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
......@@ -142,7 +142,7 @@ int main(int argc, char *argv[])
该示例在窗口上创建一个逐渐增长和褪色的文本。
```
```c
struct {
gboolean timer;
gdouble alpha;
......@@ -153,14 +153,14 @@ struct {
在这里,我们在结构内部定义了一些变量。 这用于避免使用全局变量。
```
```c
draw_text(cr, widget);
```
文本的实际绘制委托给`draw_text()`函数。
```
```c
GtkWidget *win = gtk_widget_get_toplevel(widget);
gint width, height;
......@@ -173,7 +173,7 @@ gint y = height/2;
文本将在窗口上居中。 因此,我们需要找出父窗口小部件的大小。
```
```c
cairo_set_source_rgb(cr, 0.5, 0, 0);
cairo_paint(cr);
......@@ -181,7 +181,7 @@ cairo_paint(cr);
窗口的背景充满了一些深红色。
```
```c
cairo_select_font_face(cr, "Courier",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_BOLD);
......@@ -190,7 +190,7 @@ cairo_select_font_face(cr, "Courier",
文本将以 Courier 粗体显示。
```
```c
glob.size += 0.8;
if (glob.size > 20) {
......@@ -201,7 +201,7 @@ if (glob.size > 20) {
文本大小增加了 0.8 个单位。 达到 20 个单位后,alpha 值开始减小。 文本逐渐消失。
```
```c
cairo_text_extents(cr, "ZetCode", &extents);
cairo_move_to(cr, x - extents.width/2, y);
......@@ -209,7 +209,7 @@ cairo_move_to(cr, x - extents.width/2, y);
我们得到了文本指标。 我们将仅使用文本宽度。 我们移动到文本将在窗口上居中的位置。
```
```c
cairo_text_path(cr, "ZetCode");
cairo_clip(cr);
......@@ -219,7 +219,7 @@ cairo_paint_with_alpha(cr, glob.alpha);
我们使用`cairo_text_path()`方法获得文本的路径。 我们使用`cairo_clip()`方法将绘画限制为当前路径。 `cairo_paint_with_alpha()`方法使用 alpha 值的掩码在当前剪辑区域内的任何地方绘制当前源。
```
```c
glob.timer = TRUE;
glob.alpha = 1.0;
glob.size = 1.0;
......@@ -228,7 +228,7 @@ glob.size = 1.0;
我们初始化三个变量。
```
```c
static gboolean time_handler(GtkWidget *widget)
{
if (!glob.timer) return FALSE;
......@@ -242,7 +242,7 @@ static gboolean time_handler(GtkWidget *widget)
`time_handler`调用的主要功能是定期重绘窗口。 当函数返回`FALSE`时,超时功能将停止工作。
```
```c
g_timeout_add(14, (GSourceFunc) time_handler, (gpointer) window);
```
......@@ -257,7 +257,7 @@ Figure: Puff effect
在此示例中,我们使用透明效果创建一个等待演示。 我们将绘制 8 条线,这些线将逐渐消失,从而产生一种错觉,即一条线在移动。 此类效果通常用于通知用户幕后正在进行繁重的任务。 一个示例是通过互联网流式传输视频。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
#include <math.h>
......@@ -353,7 +353,7 @@ int main(int argc, char *argv[])
我们用八个不同的 alpha 值绘制八条线。
```
```c
static gdouble const trs[8][8] = {
{ 0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0 },
{ 1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9 },
......@@ -369,7 +369,7 @@ int main(int argc, char *argv[])
这是此演示中使用的透明度值的二维数组。 有 8 行,每行一种状态。 8 行中的每行将连续使用这些值。
```
```c
cairo_set_line_width(cr, 3);
cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
......@@ -377,14 +377,14 @@ cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
我们使线条更粗一些,以便更好地显示它们。 我们用带帽的线画线。
```
```c
cairo_set_source_rgba(cr, 0, 0, 0, trs[glob.count%8][i]);
```
在这里,我们定义了一条线的透明度值。
```
```c
cairo_move_to(cr, 0.0, -10.0);
cairo_line_to(cr, 0.0, -40.0);
cairo_rotate(cr, M_PI/4);
......@@ -393,7 +393,7 @@ cairo_rotate(cr, M_PI/4);
这些代码将绘制八行中的每一行。
```
```c
g_timeout_add(100, (GSourceFunc) time_handler, (gpointer) window);
```
......
......@@ -10,7 +10,7 @@
有几种合成操作。 Cairo 图形库具有 14 种不同的合成操作。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
......@@ -109,7 +109,7 @@ int main(int argc, char *argv[])
在我们的示例中,我们将在两个正方形上显示 6 种不同的合成操作。
```
```c
first = cairo_surface_create_similar(cairo_get_target(cr),
CAIRO_CONTENT_COLOR_ALPHA, w, h);
......@@ -120,7 +120,7 @@ second = cairo_surface_create_similar(cairo_get_target(cr),
我们创建两个曲面。
```
```c
first_cr = cairo_create(first);
cairo_set_source_rgb(first_cr, 0, 0, 0.4);
cairo_rectangle(first_cr, x, 20, 50, 50);
......@@ -130,7 +130,7 @@ cairo_fill(first_cr);
我们在表面绘制一个矩形。
```
```c
cairo_set_operator(first_cr, op);
cairo_set_source_surface(first_cr, second, 0, 0);
cairo_paint(first_cr);
......@@ -139,7 +139,7 @@ cairo_paint(first_cr);
我们在曲面上应用合成操作。
```
```c
cairo_set_source_surface(cr, first, 0, 0);
cairo_paint(cr);
......@@ -147,7 +147,7 @@ cairo_paint(cr);
最后,我们将结果绘制到 GTK+ 窗口上。
```
```c
cairo_operator_t oper[] = {
CAIRO_OPERATOR_DEST_OVER,
CAIRO_OPERATOR_DEST_IN,
......
......@@ -10,7 +10,7 @@
在下面的示例中,我们将裁剪图像。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
#include <math.h>
......@@ -107,7 +107,7 @@ int main(int argc, char *argv[])
在此示例中,我们将裁剪图像。 屏幕上正在移动一个圆圈,并显示了一部分基础图像。 这就像我们从孔中看一样。
```
```c
if (pos_x < 0 + radius) {
delta[0] = rand() % 4 + 5;
} else if (pos_x > width - radius) {
......@@ -118,7 +118,7 @@ if (pos_x < 0 + radius) {
如果圆碰到窗口的左侧或右侧,则圆的移动方向会随机变化。 顶部和底部也一样。
```
```c
cairo_set_source_surface(cr, glob.image, 1, 1);
cairo_arc(cr, pos_x, pos_y, radius, 0, 2*M_PI);
......@@ -126,21 +126,21 @@ cairo_arc(cr, pos_x, pos_y, radius, 0, 2*M_PI);
在这里,我们绘制图像和一个圆。 请注意,我们目前不在窗口上绘制,而仅在内存中绘制。
```
```c
cairo_clip(cr);
```
`cairo_clip()`设置剪切区域。 裁剪区域是当前使用的路径。 当前路径是通过`cairo_arc()`函数调用创建的。
```
```c
cairo_paint(cr);
```
`cairo_paint()`在当前剪辑区域内的任何地方绘制当前源。
```
```c
glob.image = cairo_image_surface_create_from_png("turnacastle.png");
```
......@@ -155,7 +155,7 @@ Figure: Clipping image
在将源应用于表面之前,先对其进行过滤。 遮罩用作过滤器。 遮罩确定在哪里应用源,在哪里不应用。 遮罩的不透明部分允许复制源。 透明零件不允许将源复制到表面。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
......@@ -226,7 +226,7 @@ int main(int argc, char *argv[])
这个小例子清楚地说明了遮罩背后的基本思想。 遮罩可确定在何处绘画和不在何处绘画。
```
```c
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0, 0, 0);
......@@ -246,7 +246,7 @@ Figure: Applying a mask
在此代码示例中,我们将忽略图像。 这类似于我们使用卷帘所做的。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
......@@ -349,7 +349,7 @@ int main(int argc, char *argv[])
盲目效应背后的想法很简单。 图像高度为`h`像素。 我们绘制高度为 1px 的 0、1、2 ... 线。 每个周期,图像的一部分高 1px,直到整个图像可见为止。
```
```c
struct {
cairo_surface_t *image;
cairo_surface_t *surface;
......@@ -362,7 +362,7 @@ struct {
在全局结构中,我们将存储两个表面,一个计时器以及图像的宽度和高度变量。
```
```c
static void init_vars()
{
glob.timer = TRUE;
......@@ -377,14 +377,14 @@ static void init_vars()
`init_vars()`函数中,我们初始化先前声明的变量。 最后一行创建一个空的图像表面。 它将用我们之前创建的图像表面的像素线填充。
```
```c
ic = cairo_create(glob.surface);
```
我们从空图像源创建一个 cairo 上下文。
```
```c
cairo_rectangle(ic, 0, 0, glob.img_width, h);
cairo_fill(ic);
......@@ -392,21 +392,21 @@ cairo_fill(ic);
我们在最初为空的图像中绘制一个矩形。 矩形每个周期将高出 1 像素。 以这种方式创建的图像稍后将用作遮罩。
```
```c
h += 1;
```
要显示的图像高度增加一个单位。
```
```c
if ( h == glob.img_height) glob.timer = FALSE;
```
当我们在 GTK 窗口上绘制整个图像时,我们将停止计时器功能。
```
```c
cairo_set_source_surface(cr, glob.image, 10, 10);
cairo_mask_surface(cr, glob.surface, 10, 10);
......@@ -414,7 +414,7 @@ cairo_mask_surface(cr, glob.surface, 10, 10);
城堡的图像被设置为绘画的来源。 `cairo_mask_surface()`使用表面的 Alpha 通道作为遮罩来绘制电流源。
```
```c
static void cleanup()
{
cairo_surface_destroy(glob.image);
......
......@@ -12,7 +12,7 @@
以下示例描述了一个简单的平移。
```
```c
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0.2, 0.3, 0.8);
......@@ -39,7 +39,7 @@ static void do_drawing(cairo_t *cr)
该示例画一个矩形。 然后,我们进行平移并再次绘制相同的矩形。
```
```c
cairo_translate(cr, 20, 20);
```
......@@ -54,7 +54,7 @@ Figure: Translation
在以下示例中,我们执行剪切操作。 剪切是沿特定轴的对象变形。 此操作没有剪切功能。 我们需要创建自己的变换矩阵。 注意,可以通过创建变换矩阵来执行每个仿射变换。
```
```c
static void do_drawing(cairo_t *cr)
{
cairo_matrix_t matrix;
......@@ -76,14 +76,14 @@ static void do_drawing(cairo_t *cr)
在此代码示例中,我们执行一个简单的剪切操作。
```
```c
cairo_matrix_t matrix;
```
`cairo_matrix_t`是具有仿射变换的结构。
```
```c
cairo_matrix_init(&matrix,
1.0, 0.5,
0.0, 1.0,
......@@ -93,7 +93,7 @@ cairo_matrix_init(&matrix,
此变换将 y 值剪切为 x 值的 0.5。
```
```c
cairo_transform(cr, &matrix);
```
......@@ -108,7 +108,7 @@ Figure: Shearing
下一个示例演示了缩放操作。 缩放是一种变换操作,其中对象被放大或缩小。
```
```c
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0.2, 0.3, 0.8);
......@@ -130,7 +130,7 @@ static void do_drawing(cairo_t *cr)
我们绘制三个`90x90px`的矩形。 在其中两个上,我们执行缩放操作。
```
```c
cairo_scale(cr, 0.6, 0.6);
cairo_set_source_rgb(cr, 0.8, 0.3, 0.2);
cairo_rectangle(cr, 30, 30, 90, 90);
......@@ -140,7 +140,7 @@ cairo_fill(cr);
我们将矩形均匀缩放 0.6 倍。
```
```c
cairo_scale(cr, 0.8, 0.8);
cairo_set_source_rgb(cr, 0.8, 0.8, 0.2);
cairo_rectangle(cr, 50, 50, 90, 90);
......@@ -158,7 +158,7 @@ Figure: Scaling
变换操作是累加的。 为了将一个操作与另一个操作隔离开,我们可以使用`cairo_save()``cairo_restore()`功能。 `cairo_save()`功能可复制图形上下文的当前状态,并将其保存在已保存状态的内部堆栈中。 `cairo_restore()`功能将把上下文重新建立为保存状态。
```
```c
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0.2, 0.3, 0.8);
......@@ -184,7 +184,7 @@ static void do_drawing(cairo_t *cr)
在示例中,我们缩放了两个矩形。 这次我们将缩放操作相互隔离。
```
```c
cairo_save(cr);
cairo_scale(cr, 0.6, 0.6);
cairo_set_source_rgb(cr, 0.8, 0.3, 0.2);
......@@ -206,7 +206,7 @@ Figure: Isolating transformations
在下面的示例中,我们通过旋转一堆椭圆来创建复杂的形状。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
#include <math.h>
......@@ -276,7 +276,7 @@ int main(int argc, char *argv[])
我们将进行旋转和缩放操作。 我们还将保存和恢复 Cairo 上下文。
```
```c
cairo_translate(cr, width/2, height/2);
cairo_arc(cr, 0, 0, 120, 0, 2 * M_PI);
cairo_stroke(cr);
......@@ -285,7 +285,7 @@ cairo_stroke(cr);
在 GTK+ 窗口的中间,我们创建了一个圆。 这将是我们椭圆的边界圆。
```
```c
gint i;
for (i = 0; i < 36; i++) {
cairo_save(cr);
......@@ -304,7 +304,7 @@ for (i = 0; i < 36; i++) {
下一个示例显示了一个旋转和缩放的星星。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
......@@ -411,7 +411,7 @@ int main(int argc, char *argv[])
在此示例中,我们创建一个星形对象。 我们将对其进行平移,旋转和缩放。
```
```c
int points[11][2] = {
{ 0, 85 },
{ 75, 75 },
......@@ -422,7 +422,7 @@ int points[11][2] = {
从这些点将构造星形对象。
```
```c
static gdouble angle = 0;
static gdouble scale = 1;
static gdouble delta = 0.01;
......@@ -431,7 +431,7 @@ static gdouble delta = 0.01;
我们初始化三个重要变量。 角度用于旋转,比例用于缩放星形对象。 `delta`变量控制星星何时生长以及何时收缩。
```
```c
cairo_translate(cr, width/2, height/2);
cairo_rotate(cr, angle);
cairo_scale(cr, scale, scale);
......@@ -440,7 +440,7 @@ cairo_scale(cr, scale, scale);
我们将星星移到窗口中间。 旋转并缩放比例。
```
```c
gint i;
for ( i = 0; i < 10; i++ ) {
cairo_line_to(cr, points[i][0], points[i][1]);
......@@ -454,7 +454,7 @@ cairo_stroke(cr);
在这里,我们绘制星形对象。
```
```c
if ( scale < 0.01 ) {
delta = -delta;
} else if (scale > 0.99) {
......
......@@ -8,7 +8,7 @@
在第一个示例中,我们将在 GTK+ 窗口上显示一些歌词。
```
```c
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
......@@ -39,7 +39,7 @@ static void do_drawing(cairo_t *cr)
在此示例中,我们显示了 Natasha Bedingfield 的 Soulmate 歌曲的部分歌词。
```
```c
cairo_select_font_face(cr, "Purisa",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_BOLD);
......@@ -48,14 +48,14 @@ cairo_select_font_face(cr, "Purisa",
在这里,我们选择字体。 该函数采用三个参数,字体系列,字体倾斜度和字体粗细。
```
```c
cairo_set_font_size(cr, 13);
```
在这里,我们指定字体大小。
```
```c
cairo_move_to(cr, 20, 30);
cairo_show_text(cr, "Most relationships seem so transitory");
......@@ -71,7 +71,7 @@ Figure: Soulmate
接下来,我们将展示如何在窗口上居中放置文本。
```
```c
static void do_drawing(cairo_t *cr, GtkWidget *widget)
{
cairo_text_extents_t extents;
......@@ -97,7 +97,7 @@ static void do_drawing(cairo_t *cr, GtkWidget *widget)
该代码将使文本在窗口上居中。 即使我们调整窗口大小,它仍然居中。
```
```c
GtkWidget *win = gtk_widget_get_toplevel(widget);
gint w, h;
......@@ -107,7 +107,7 @@ gtk_window_get_size(GTK_WINDOW(win), &w, &h);
为了使文本在窗口上居中,有必要获取父窗口的大小。
```
```c
cairo_select_font_face(cr, "Courier",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_BOLD);
......@@ -118,14 +118,14 @@ cairo_set_font_size(cr, 60);
我们选择要显示的字体及其大小。
```
```c
cairo_text_extents(cr, "ZetCode", &extents);
```
我们得到了文本范围。 这些是描述文字的数字。 我们的示例需要文本的宽度。
```
```c
cairo_move_to(cr, w/2 - extents.width/2, h/2);
cairo_show_text(cr, "ZetCode");
......@@ -141,7 +141,7 @@ Figure: Centered text
现在,我们将在窗口上显示阴影文本。
```
```c
static void do_drawing(cairo_t *cr, GtkWidget *widget)
{
cairo_select_font_face(cr, "Serif", CAIRO_FONT_SLANT_NORMAL,
......@@ -161,7 +161,7 @@ static void do_drawing(cairo_t *cr, GtkWidget *widget)
要创建阴影,我们将文本绘制两次。 以不同的颜色。 第二个文本向右和向下移动一点。
```
```c
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_move_to(cr, 40, 60);
cairo_show_text(cr, "ZetCode");
......@@ -170,7 +170,7 @@ cairo_show_text(cr, "ZetCode");
第一个文本用黑色墨水绘制。 它充当阴影。
```
```c
cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
cairo_move_to(cr, 43, 63);
cairo_show_text(cr, "ZetCode");
......@@ -187,7 +187,7 @@ Figure: Shaded text
以下示例将产生很好的效果。 我们将使用一些线性渐变填充文本。
```
```c
static void do_drawing(cairo_t *cr, GtkWidget *widget)
{
cairo_pattern_t *pat;
......@@ -216,7 +216,7 @@ static void do_drawing(cairo_t *cr, GtkWidget *widget)
我们在充满线性渐变的窗口上绘制文本。 颜色是一些橙色。
```
```c
cairo_set_source_rgb(cr, 0.2, 0.2, 0.2);
cairo_paint(cr);
......@@ -224,7 +224,7 @@ cairo_paint(cr);
为了使其更具视觉吸引力,我们将背景涂成深灰色。
```
```c
pat = cairo_pattern_create_linear(0, 15, 0, h*0.8);
cairo_pattern_set_extend(pat, CAIRO_EXTEND_REPEAT);
cairo_pattern_add_color_stop_rgb(pat, 0.0, 1, 0.6, 0);
......@@ -234,7 +234,7 @@ cairo_pattern_add_color_stop_rgb(pat, 0.5, 1, 0.3, 0);
将创建线性渐变。
```
```c
cairo_move_to(cr, 15, 80);
cairo_text_path(cr, "ZetCode");
cairo_set_source(cr, pat);
......@@ -254,7 +254,7 @@ Figure: Text filled with gradient
请注意,Pango 库解决了许多常见的编程要求,包括文本。
```
```c
static void do_drawing(cairo_t *cr, GtkWidget *widget)
{
cairo_select_font_face(cr, "Serif", CAIRO_FONT_SLANT_NORMAL,
......@@ -281,7 +281,7 @@ static void do_drawing(cairo_t *cr, GtkWidget *widget)
该代码显示了所选字体的 700 个字形。
```
```c
const int n_glyphs = 20 * 35;
cairo_glyph_t glyphs[n_glyphs];
......@@ -289,7 +289,7 @@ cairo_glyph_t glyphs[n_glyphs];
字形数组将存储三个整数值。 第一个值是字形到所选字体类型的索引。 第二和第三值是字形的 x,y 位置。
```
```c
cairo_show_glyphs(cr, glyphs, n_glyphs);
```
......
......@@ -16,7 +16,7 @@
`SwingTimerEx.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......@@ -54,7 +54,7 @@ public class SwingTimerEx extends JFrame {
这是代码示例的主要类。
```
```java
setResizable(false);
pack();
......@@ -64,7 +64,7 @@ pack();
`Board.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -149,7 +149,7 @@ public class Board extends JPanel
`Board`类中,我们将星星从左上角移到右下角。
```
```java
private final int B_WIDTH = 350;
private final int B_HEIGHT = 350;
private final int INITIAL_X = -40;
......@@ -160,7 +160,7 @@ private final int DELAY = 25;
定义了五个常数。 前两个常数是板的宽度和高度。 第三和第四是星星的初始坐标。 最后一个确定动画的速度。
```
```java
private void loadImage() {
ImageIcon ii = new ImageIcon("src/resources/star.png");
......@@ -171,7 +171,7 @@ private void loadImage() {
`loadImage()`方法中,我们创建`ImageIcon`类的实例。 该图像位于项目目录中。 `getImage()`方法将从此类返回`Image`对象。 该对象将绘制在板上。
```
```java
timer = new Timer(DELAY, this);
timer.start();
......@@ -179,7 +179,7 @@ timer.start();
在这里,我们创建一个 Swing `Timer`类,并调用其`start()`方法。 计时器每`DELAY`毫秒就会调用一次`actionPerformed()`方法。 为了使用`actionPerformed()`方法,我们必须实现`ActionListener`接口。
```
```java
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
......@@ -191,7 +191,7 @@ public void paintComponent(Graphics g) {
自定义绘画是通过`paintComponent()`方法完成的。 请注意,我们还调用其父级的`paintComponent()`方法。 实际绘画将委托给`drawStar()`方法。
```
```java
private void drawStar(Graphics g) {
g.drawImage(star, x, y, this);
......@@ -202,7 +202,7 @@ private void drawStar(Graphics g) {
`drawStar()`方法中,我们使用`drawImage()`方法在窗口上绘制图像。 `Toolkit.getDefaultToolkit().sync()`在缓冲图形事件的系统上同步绘画。 没有这条线,动画在 Linux 上可能会不流畅。
```
```java
@Override
public void actionPerformed(ActionEvent e) {
......@@ -232,7 +232,7 @@ Figure: Star
`UtilityTimerEx.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......@@ -272,7 +272,7 @@ public class UtilityTimerEx extends JFrame {
`Board.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -359,7 +359,7 @@ public class Board extends JPanel {
在此示例中,计时器将定期调用`ScheduleTask`类的`run()`方法。
```
```java
timer = new Timer();
timer.scheduleAtFixedRate(new ScheduleTask(),
INITIAL_DELAY, PERIOD_INTERVAL);
......@@ -368,7 +368,7 @@ timer.scheduleAtFixedRate(new ScheduleTask(),
在这里,我们创建一个计时器并按特定的时间间隔安排任务。 有一个初始延迟。
```
```java
@Override
public void run() {
...
......@@ -384,7 +384,7 @@ public void run() {
`ThreadAnimationEx.java`
```
```java
package com.zetcode;
import java.awt.EventQueue;
......@@ -424,7 +424,7 @@ This is the main class.
`Board.java`
```
```java
package com.zetcode;
import java.awt.Color;
......@@ -542,7 +542,7 @@ public class Board extends JPanel
在前面的示例中,我们以特定的间隔执行任务。 在此示例中,动画将在线程内进行。 `run()`方法仅被调用一次。 这就是为什么我们在方法中有一个`while`循环的原因。 从该方法中,我们称为`cycle()``repaint()`方法。
```
```java
@Override
public void addNotify() {
super.addNotify();
......@@ -557,7 +557,7 @@ public void addNotify() {
我们希望我们的游戏以恒定的速度平稳运行。 因此,我们计算系统时间。
```
```java
timeDiff = System.currentTimeMillis() - beforeTime;
sleep = DELAY - timeDiff;
......
......@@ -8,7 +8,7 @@
在第一个示例中,我们将显示一个图像。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
......@@ -68,28 +68,28 @@ int main(int argc, char *argv[])
该示例显示图像。
```
```c
glob.image = cairo_image_surface_create_from_png("stmichaelschurch.png");
```
我们从 PNG 图像创建图像表面。 出于效率原因,该函数在主函数中调用。
```
```c
cairo_set_source_surface(cr, glob.image, 10, 10);
```
我们从创建的图像表面创建一个绘画源。
```
```c
cairo_paint(cr);
```
我们在窗口上绘制源。
```
```c
cairo_surface_destroy(glob.image);
```
......@@ -100,7 +100,7 @@ cairo_surface_destroy(glob.image);
在图像上绘制信息是很常见的。 写在图像上的文本称为水印。 水印用于识别图像。 它们可能是版权声明或图像创建时间。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
......@@ -178,7 +178,7 @@ int main (int argc, char *argv[])
我们在图像上绘制版权信息。
```
```c
static void load_image()
{
glob.image = cairo_image_surface_create_from_png("beckov.png");
......@@ -188,7 +188,7 @@ static void load_image()
`load_image()`方法中,我们从 PNG 图像创建图像表面。
```
```c
static void draw_mark()
{
cairo_t *ic;
......@@ -199,7 +199,7 @@ static void draw_mark()
`draw_mark()`功能中,我们在图像上绘制版权信息。 首先,我们从图像表面创建一个绘图上下文。
```
```c
cairo_set_font_size(ic, 11);
cairo_set_source_rgb(ic, 0.9 , 0.9 , 0.9);
......@@ -211,7 +211,7 @@ cairo_stroke(ic);
然后,我们用白色绘制一个小的文本。
```
```c
static void do_drawing(cairo_t *cr, GtkWidget *widget)
{
cairo_set_source_surface(cr, glob.image, 10, 10);
......@@ -226,7 +226,7 @@ static void do_drawing(cairo_t *cr, GtkWidget *widget)
我们称其为频谱效应,因为它类似于旧的 ZX 频谱计算机。 当您将图像加载到这台计算机时,它逐渐出现在屏幕上。 下一个例子是基于这种经验。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
......@@ -333,7 +333,7 @@ int main(int argc, char *argv[])
我们将图像分为由 8 行组成的 n 个部分。 每个周期,图像的每个部分都会变大一个像素。 创建的图像将用作显示城堡图像的遮罩。
```
```c
struct {
gboolean timer;
cairo_surface_t *image;
......@@ -346,7 +346,7 @@ struct {
全局结构存储在更多函数中使用的变量。
```
```c
static void init_vars()
{
glob.image = cairo_image_surface_create_from_png("beckov.png");
......@@ -363,7 +363,7 @@ static void init_vars()
`init_vars()`功能中,我们启动上述变量。
```
```c
gint i, j;
for (i = 0; i <= glob.img_height; i+=7) {
for (j = 0 ; j < count; j++) {
......@@ -376,7 +376,7 @@ for (i = 0; i <= glob.img_height; i+=7) {
我们逐步将线绘制到 n 个部分中的每个部分。
```
```c
count++;
if (count == 8) glob.timer = FALSE;
......@@ -384,7 +384,7 @@ if (count == 8) glob.timer = FALSE;
8 个步骤后,动画结束。
```
```c
cairo_set_source_surface(cr, glob.image, 10, 10);
cairo_mask_surface(cr, glob.surface, 10, 10);
cairo_stroke(ic);
......
......@@ -10,7 +10,7 @@
我们的第一个示例将创建一个透明窗口。 我们将看到窗口对象下方的内容。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
......@@ -79,28 +79,28 @@ int main (int argc, char *argv[])
为了创建透明窗口,我们获得了屏幕对象的视觉效果并将其设置为我们的窗口。 在`on_draw()`方法中,我们绘制屏幕的可视对象。 这产生了部分透明的幻觉。
```
```c
gtk_widget_set_app_paintable(win, TRUE);
```
我们必须设置要绘制的应用。
```
```c
screen = gdk_screen_get_default();
```
`gdk_screen_get_default()`方法返回屏幕对象。
```
```c
visual = gdk_screen_get_rgba_visual(screen);
```
从屏幕窗口中,我们可以看到它。 视觉内容包含低级显示信息。
```
```c
if (visual != NULL && gdk_screen_is_composited(screen)) {
gtk_widget_set_visual(win, visual);
}
......@@ -109,7 +109,7 @@ if (visual != NULL && gdk_screen_is_composited(screen)) {
并非所有的显示器都支持此操作。 因此,我们检查屏幕是否支持合成并且返回的视觉效果不是“无”。 我们将屏幕的视觉效果设置为窗口的视觉效果。
```
```c
static void do_drawing(cairo_t *cr)
{
cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.4);
......@@ -129,7 +129,7 @@ Figure: Transparent window
根窗口对于截图也是必不可少的。
```
```c
#include <cairo.h>
#include <gdk/gdk.h>
......@@ -162,21 +162,21 @@ int main (int argc, char *argv[])
该示例捕获整个屏幕的快照。 在此示例中,我们不使用完整的 GTK 窗口系统。 我们使用 Cairo 和 GDK 库来完成这项工作。
```
```c
gdk_init(&argc, &argv);
```
`gdk_init()`初始化 GDK 库并连接到窗口系统。
```
```c
GdkWindow *root_win = gdk_get_default_root_window();
```
我们通过`gdk_get_default_root_window()`函数调用获得了根窗口。
```
```c
gint width = gdk_window_get_width(root_win);
gint height = gdk_window_get_height(root_win);
......@@ -184,7 +184,7 @@ gint height = gdk_window_get_height(root_win);
我们确定根窗口的宽度和高度。
```
```c
cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
width, height);
......@@ -192,14 +192,14 @@ cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
空的图像表面被创建。 它具有根窗口的大小。
```
```c
GdkPixbuf *pb = gdk_pixbuf_get_from_window(root_win, 0, 0, width, height);
```
我们使用`gdk_pixbuf_get_from_window()`函数调用从根窗口中获得一个`pixbuf``pixbuf`是描述内存中图像的对象。
```
```c
cairo_t *cr = cairo_create(surface);
gdk_cairo_set_source_pixbuf(cr, pb, 0, 0);
cairo_paint(cr);
......@@ -208,14 +208,14 @@ cairo_paint(cr);
在上述代码行中,我们在之前创建的图像表面上创建了 Cairo 绘图上下文。 我们将 pixbuf 放在绘图上下文上并将其绘制在表面上。
```
```c
cairo_surface_write_to_png(surface, "image.png");
```
使用`write_to_png()`方法将图像表面写入 PNG 图像。
```
```c
cairo_destroy(cr);
cairo_surface_destroy(surface);
......@@ -227,7 +227,7 @@ cairo_surface_destroy(surface);
在第三个示例中,我们将在桌面窗口上显示一条消息。
```
```c
#include <cairo.h>
#include <gtk/gtk.h>
#include <pango/pango.h>
......@@ -303,7 +303,7 @@ int main (int argc, char *argv[])
该代码在根窗口上显示消息标签。
```
```c
static void do_drawing(cairo_t *cr)
{
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
......@@ -315,28 +315,28 @@ static void do_drawing(cairo_t *cr)
我们使用`CAIRO_OPERATOR_CLEAR`运算符清除窗口背景。 然后我们设置`CAIRO_OPERATOR_OVER`以绘制标签窗口小部件。
```
```c
gtk_widget_set_app_paintable(win, TRUE);
```
我们将操纵应用窗口,因此我们使其可绘制。
```
```c
gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DOCK);
```
实施此窗口提示会删除窗口边框和装饰。
```
```c
gtk_window_set_keep_below(GTK_WINDOW(win), TRUE);
```
我们始终将应用始终放在根窗口的底部。
```
```c
GdkScreen *screen = gdk_screen_get_default();
GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
......@@ -348,14 +348,14 @@ if (visual != NULL && gdk_screen_is_composited(screen)) {
我们将屏幕的外观设置为应用的外观。
```
```c
lbl = gtk_label_new("ZetCode, tutorials for programmers");
```
我们创建一个消息标签。
```
```c
PangoFontDescription *fd = pango_font_description_from_string("Serif 20");
gtk_widget_modify_font(lbl, fd);
......@@ -363,14 +363,14 @@ gtk_widget_modify_font(lbl, fd);
在 Pango 模块的帮助下,我们为文本选择特定的字体。
```
```c
gtk_container_add(GTK_CONTAINER(window), lbl);
```
标签贴在窗户上。
```
```c
GdkColor color;
gdk_color_parse("white", &color);
gtk_widget_modify_fg(lbl, GTK_STATE_NORMAL, &color);
......
......@@ -10,7 +10,7 @@ PyCairo 支持各种后端。 后端是可以显示 PyCairo 产生的图形的
`pngimage.py`
```
```py
#!/usr/bin/python
'''
......@@ -48,14 +48,14 @@ if __name__ == "__main__":
此示例是一个小型控制台应用,可创建 PNG 图像。
```
```py
import cairo
```
我们导入 PyCairo 模块。
```
```py
ims = cairo.ImageSurface(cairo.FORMAT_ARGB32, 390, 60)
cr = cairo.Context(ims)
......@@ -63,14 +63,14 @@ cr = cairo.Context(ims)
我们创建一个曲面,并从该曲面创建一个 Cairo 上下文。 表面是 390x60 像素的图像。
```
```py
cr.set_source_rgb(0, 0, 0)
```
我们用黑色墨水绘制文本。 墨水通过`set_source_rgb()`方法指定。
```
```py
cr.select_font_face("Sans", cairo.FONT_SLANT_NORMAL,
cairo.FONT_WEIGHT_NORMAL)
cr.set_font_size(40)
......@@ -79,7 +79,7 @@ cr.set_font_size(40)
我们通过`select_font_face()`方法选择一种字体类型,并通过`set_font_size()`方法设置字体大小。
```
```py
cr.move_to(10, 50)
cr.show_text("Disziplin ist Macht.")
......@@ -87,7 +87,7 @@ cr.show_text("Disziplin ist Macht.")
我们将图像内的位置移至`x = 10.0``y = 50.0`并绘制文本。
```
```py
ims.write_to_png("image.png")
```
......@@ -104,7 +104,7 @@ Figure: PNG image in Eye of Gnome
`pdffile.py`
```
```py
#!/usr/bin/python
'''
......@@ -141,14 +141,14 @@ if __name__ == "__main__":
我们必须在 PDF 查看器中打开 PDF 文件。 Linux 用户可以使用 KPDF 或 Evince 查看器。
```
```py
ps = cairo.PDFSurface("pdffile.pdf", 504, 648)
```
要渲染 PDF 文件,我们必须使用`cairo.PDFSurface`对象创建 PDF 曲面。 PDF 文件的大小以磅为单位指定,这是排版的标准。
```
```py
cr.show_page()
```
......@@ -165,7 +165,7 @@ Figure: PDF file in Evince
`svgfile.py`
```
```py
#!/usr/bin/python
'''
......@@ -202,14 +202,14 @@ if __name__ == "__main__":
我们可以使用网络浏览器(例如 Google Chrome)或矢量绘图程序(例如 Inkscape)打开 SVG 文件。
```
```py
ps = cairo.SVGSurface("svgfile.svg", 390, 60)
```
要在 PyCairo 中创建 SVG 文件,我们必须使用`cairo.SVGSurface`对象创建 SVG 表面。
```
```py
cr.show_page()
```
......@@ -226,7 +226,7 @@ SVG file in Chrome
`gtkwindow.py`
```
```py
#!/usr/bin/python
'''
......@@ -284,7 +284,7 @@ if __name__ == "__main__":
该示例弹出一个居中的 GTK 窗口,在该窗口上绘制“Disziplin ist Macht”文本。
```
```py
from gi.repository import Gtk
import cairo
......@@ -292,21 +292,21 @@ import cairo
我们导入必要的 PyCairo 和 GTK 模块。
```
```py
darea = Gtk.DrawingArea()
```
我们将使用`Gtk.DrawingArea`小部件。
```
```py
darea.connect("draw", self.on_draw)
```
重新绘制窗口时,会发出`draw`信号。 我们将该信号连接到`on_draw()`回调。
```
```py
def on_draw(self, wid, cr):
...
......
此差异已折叠。
此差异已折叠。
......@@ -10,7 +10,7 @@
线性渐变是沿着一条线的颜色混合或颜色阴影混合。 它们由 PyCairo 中的`cairo.LinearGradient`类表示。
```
```py
#!/usr/bin/python
'''
......@@ -113,14 +113,14 @@ if __name__ == "__main__":
该示例绘制了三个填充有线性渐变的矩形。
```
```py
lg3 = cairo.LinearGradient(20.0, 260.0, 20.0, 360.0)
```
在这里,我们创建一个线性渐变。 参数指定直线,沿着该直线绘制渐变。 这是一条水平线。
```
```py
lg3.add_color_stop_rgba(0.1, 0, 0, 0, 1)
lg3.add_color_stop_rgba(0.5, 1, 1, 0, 1)
lg3.add_color_stop_rgba(0.9, 0, 0, 0, 1)
......@@ -137,7 +137,7 @@ Figure: Linear gradients
径向渐变是两个圆之间颜色或阴影的混合。 `cairo.RadialGradient`类用于在 PyCairo 中创建径向渐变。
```
```py
#!/usr/bin/python
'''
......@@ -216,7 +216,7 @@ if __name__ == "__main__":
在示例中,我们绘制了两个径向渐变。
```
```py
r1 = cairo.RadialGradient(30, 30, 10, 30, 30, 90)
r1.add_color_stop_rgba(0, 1, 1, 1, 1)
r1.add_color_stop_rgba(1, 0.6, 0.6, 0.6, 1)
......@@ -228,7 +228,7 @@ cr.fill()
我们画一个圆,并用径向渐变填充其内部。 径向梯度由两个圆定义。 `add_color_stop_rgba()`方法定义颜色。 我们可以试验圆的位置或半径的长度。 在第一个渐变示例中,我们创建了一个类似于 3D 形状的对象。
```
```py
r2 = cairo.RadialGradient(0, 0, 10, 0, 0, 40)
r2.add_color_stop_rgb(0, 1, 1, 0)
r2.add_color_stop_rgb(0.8, 0, 0, 0)
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册