Java 图像编程:从基础到高级过滤
1. 图像加载与跟踪
在 Java 中处理图像时,首先要考虑的是如何加载和跟踪图像。可以在
init()
方法中创建一个新的
MediaTracker
,并使用
addImage()
方法将每个命名图像添加为跟踪图像。在
paint()
方法中,对正在跟踪的每个图像调用
checkID()
方法。如果所有图像都已加载,则显示它们;否则,显示一个简单的条形图,展示已加载图像的数量,并在条形图下方显示已完全加载的图像名称。
以下是一个简单示例:
// 示例代码:图像加载与跟踪
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
public class ImageLoader extends Applet {
MediaTracker tracker;
Image img;
public void init() {
tracker = new MediaTracker(this);
img = getImage(getDocumentBase(), "example.jpg");
tracker.addImage(img, 0);
}
public void paint(Graphics g) {
if (tracker.checkID(0)) {
g.drawImage(img, 0, 0, this);
} else {
// 显示加载进度
g.drawString("Loading...", 10, 30);
}
}
}
操作步骤:
-
创建
MediaTracker对象。 -
使用
getImage()方法获取图像。 -
使用
addImage()方法将图像添加到MediaTracker中。 -
在
paint()方法中,使用checkID()方法检查图像是否加载完成。
2. ImageProducer 接口
ImageProducer
是一个接口,用于为图像生成数据的对象。实现
ImageProducer
接口的对象将提供表示图像数据的整数或字节数组,并生成
Image
对象。
java.awt.image
包中包含两个图像生成器:
MemoryImageSource
和
FilteredImageSource
。
2.1 MemoryImageSource 类
MemoryImageSource
类从数据数组创建新的
Image
对象。它定义了几个构造函数,常用的构造函数如下:
MemoryImageSource(int width, int height, int pixel[], int offset, int scanLineWidth)
该构造函数使用
pixel
数组中的整数,在默认 RGB 颜色模型下为
Image
对象生成数据。在默认颜色模型中,像素是一个包含 Alpha、Red、Green 和 Blue(0xAARRGGBB)的整数。Alpha 值表示像素的透明度,完全透明为 0,完全不透明为 255。
以下是一个使用
MemoryImageSource
生成图像的示例:
/*
* <applet code="MemoryImageGenerator" width=256 height=256>
* </applet>
*/
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
public class MemoryImageGenerator extends Applet {
Image img;
public void init() {
Dimension d = getSize();
int w = d.width;
int h = d.height;
int pixels[] = new int[w * h];
int i = 0;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int r = (x ^ y) & 0xff;
int g = (x * 2 ^ y * 2) & 0xff;
int b = (x * 4 ^ y * 4) & 0xff;
pixels[i++] = (255 << 24) | (r << 16) | (g << 8) | b;
}
}
img = createImage(new MemoryImageSource(w, h, pixels, 0, w));
}
public void paint(Graphics g) {
g.drawImage(img, 0, 0, this);
}
}
操作步骤:
-
创建一个整数数组
pixels来存储像素值。 -
使用嵌套的
for循环生成像素数据。 -
创建
MemoryImageSource对象,传入图像的宽度、高度、像素数组、偏移量和扫描线宽度。 -
使用
createImage()方法创建Image对象。 -
在
paint()方法中绘制图像。
2.2 流程图:MemoryImageSource 图像生成流程
graph TD;
A[初始化 Applet] --> B[获取 Applet 大小];
B --> C[创建像素数组];
C --> D[生成像素数据];
D --> E[创建 MemoryImageSource 对象];
E --> F[创建 Image 对象];
F --> G[绘制图像];
3. ImageConsumer 接口
ImageConsumer
是一个接口,用于从图像中获取像素数据并将其作为另一种数据提供的对象。它与
ImageProducer
相反。实现
ImageConsumer
接口的对象将从
Image
对象创建表示像素的整数或字节数组。这里将介绍
PixelGrabber
类,它是
ImageConsumer
接口的一个简单实现。
3.1 PixelGrabber 类
PixelGrabber
类定义在
java.lang.image
包中,它是
MemoryImageSource
类的逆操作。它不是从像素值数组构造图像,而是从现有图像中获取像素数组。
PixelGrabber
的构造函数如下:
PixelGrabber(Image imgObj, int left, int top, int width, int height, int pixel[], int offset, int scanLineWidth)
操作步骤:
- 创建一个足够大的整数数组来存储像素数据。
-
创建
PixelGrabber实例,传入要抓取的矩形区域。 -
调用
grabPixels()方法获取像素数据。
以下是一个抓取图像像素并创建像素亮度直方图的示例:
/*
* <applet code=HistoGrab width=400 height=345>
* <param name=img value=Lilies.jpg>
* </applet>
*/
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
public class HistoGrab extends Applet {
Dimension d;
Image img;
int iw, ih;
int pixels[];
int w, h;
int hist[] = new int[256];
int max_hist = 0;
public void init() {
d = getSize();
w = d.width;
h = d.height;
try {
img = getImage(getDocumentBase(), getParameter("img"));
MediaTracker t = new MediaTracker(this);
t.addImage(img, 0);
t.waitForID(0);
iw = img.getWidth(null);
ih = img.getHeight(null);
pixels = new int[iw * ih];
PixelGrabber pg = new PixelGrabber(img, 0, 0, iw, ih, pixels, 0, iw);
pg.grabPixels();
} catch (InterruptedException e) {
System.out.println("Interrupted");
return;
}
for (int i = 0; i < iw * ih; i++) {
int p = pixels[i];
int r = 0xff & (p >> 16);
int g = 0xff & (p >> 8);
int b = 0xff & (p);
int y = (int) (.33 * r + .56 * g + .11 * b);
hist[y]++;
}
for (int i = 0; i < 256; i++) {
if (hist[i] > max_hist)
max_hist = hist[i];
}
}
public void update() {}
public void paint(Graphics g) {
g.drawImage(img, 0, 0, null);
int x = (w - 256) / 2;
int lasty = h - h * hist[0] / max_hist;
for (int i = 0; i < 256; i++, x++) {
int y = h - h * hist[i] / max_hist;
g.setColor(new Color(i, i, i));
g.fillRect(x, y, 1, h);
g.setColor(Color.red);
g.drawLine(x - 1, lasty, x, y);
lasty = y;
}
}
}
3.2 表格:PixelGrabber 方法说明
| 方法 | 描述 |
|---|---|
boolean grabPixels()
|
尝试抓取像素数据,成功返回
true
,失败返回
false
,可能抛出
InterruptedException
异常。
|
boolean grabPixels(long milliseconds)
|
在指定的毫秒数内尝试抓取像素数据,成功返回
true
,失败返回
false
,可能抛出
InterruptedException
异常。
|
4. ImageFilter 类
基于
ImageProducer
和
ImageConsumer
接口及其具体类
MemoryImageSource
和
PixelGrabber
,可以创建任意一组转换过滤器,对像素源进行修改并传递给任意消费者。
ImageFilter
类完善了图像的流模型。
java.awt.image
包中的一些
ImageFilter
子类包括
AreaAveragingScaleFilter
、
CropImageFilter
、
ReplicateScaleFilter
和
RGBImageFilter
。还有一个名为
FilteredImageSource
的
ImageProducer
实现,它将任意
ImageFilter
包装在
ImageProducer
周围,对其生成的像素进行过滤。
4.1 CropImageFilter 类
CropImageFilter
用于过滤图像源,提取矩形区域。当需要从单个较大的源图像中使用多个小图像时,这个过滤器非常有用。
以下是一个从单个图像中创建 16 个图像并打乱它们的示例:
/*
* <applet code=TileImage width=400 height=345>
* <param name=img value=Lilies.jpg>
* </applet>
*/
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
public class TileImage extends Applet {
Image img;
Image cell[] = new Image[4 * 4];
int iw, ih;
int tw, th;
public void init() {
try {
img = getImage(getDocumentBase(), getParameter("img"));
MediaTracker t = new MediaTracker(this);
t.addImage(img, 0);
t.waitForID(0);
iw = img.getWidth(null);
ih = img.getHeight(null);
tw = iw / 4;
th = ih / 4;
CropImageFilter f;
FilteredImageSource fis;
t = new MediaTracker(this);
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
f = new CropImageFilter(tw * x, th * y, tw, th);
fis = new FilteredImageSource(img.getSource(), f);
int i = y * 4 + x;
cell[i] = createImage(fis);
t.addImage(cell[i], i);
}
}
t.waitForAll();
for (int i = 0; i < 32; i++) {
int si = (int) (Math.random() * 16);
int di = (int) (Math.random() * 16);
Image tmp = cell[si];
cell[si] = cell[di];
cell[di] = tmp;
}
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
g.drawImage(cell[y * 4 + x], x * tw, y * th, null);
}
}
}
}
操作步骤:
- 获取源图像。
-
使用
CropImageFilter提取矩形区域。 -
使用
FilteredImageSource包装过滤器和图像源。 -
创建新的
Image对象。 - 打乱图像顺序。
4.2 RGBImageFilter 类
RGBImageFilter
用于逐像素地将一个图像转换为另一个图像,并在转换过程中改变颜色。它可以用于提亮图像、增加对比度或甚至将图像转换为灰度图。
为了演示
RGBImageFilter
,下面将介绍一个使用动态插件策略进行图像处理过滤的示例,包括
ImageFilterDemo
主 applet 类、
PlugInFilter
接口、
LoadedImage
实用类,以及
Grayscale
、
Invert
、
Contrast
、
Blur
和
Sharpen
等过滤器类。
4.2.1 ImageFilterDemo 类
ImageFilterDemo
类是示例图像过滤器的 applet 框架。它使用简单的
BorderLayout
,在南部位置放置一个面板来容纳表示每个过滤器的按钮,北部位置放置一个
Label
对象用于显示过滤器进度的信息,中心位置放置图像。
/*
* <applet code=ImageFilterDemo width=400 height=345>
* <param name=img value=Lilies.jpg>
* <param name=filters value="Grayscale+Invert+Contrast+Blur+Sharpen">
* </applet>
*/
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class ImageFilterDemo extends Applet implements ActionListener {
Image img;
PlugInFilter pif;
Image fimg;
Image curImg;
LoadedImage lim;
Label lab;
Button reset;
public void init() {
setLayout(new BorderLayout());
Panel p = new Panel();
add(p, BorderLayout.SOUTH);
reset = new Button("Reset");
reset.addActionListener(this);
p.add(reset);
StringTokenizer st = new StringTokenizer(getParameter("filters"), "+");
while (st.hasMoreTokens()) {
Button b = new Button(st.nextToken());
b.addActionListener(this);
p.add(b);
}
lab = new Label("");
add(lab, BorderLayout.NORTH);
img = getImage(getDocumentBase(), getParameter("img"));
lim = new LoadedImage(img);
add(lim, BorderLayout.CENTER);
}
public void actionPerformed(ActionEvent ae) {
String a = "";
try {
a = ae.getActionCommand();
if (a.equals("Reset")) {
lim.set(img);
lab.setText("Normal");
} else {
pif = (PlugInFilter) Class.forName(a).newInstance();
fimg = pif.filter(this, img);
lim.set(fimg);
lab.setText("Filtered: " + a);
}
repaint();
} catch (ClassNotFoundException e) {
lab.setText(a + " not found");
lim.set(img);
repaint();
} catch (InstantiationException e) {
lab.setText("couldn’t new " + a);
} catch (IllegalAccessException e) {
lab.setText("no access: " + a);
}
}
}
操作步骤:
- 初始化 applet,设置布局和按钮。
- 解析过滤器名称。
- 加载图像。
- 处理按钮点击事件,根据按钮标签加载相应的过滤器类。
- 应用过滤器并更新显示。
4.2.2 PlugInFilter 接口
PlugInFilter
是一个简单的接口,用于抽象图像过滤。它只有一个方法
filter()
,接受 applet 和源图像,并返回经过某种过滤的新图像。
interface PlugInFilter {
java.awt.Image filter(java.applet.Applet a, java.awt.Image in);
}
4.2.3 LoadedImage 类
LoadedImage
是
Canvas
的一个方便子类,它在构造时接受一个图像,并使用
MediaTracker
同步加载它。它还提供了
set()
方法,用于设置要在该
Canvas
中显示的新图像。
import java.awt.*;
public class LoadedImage extends Canvas {
Image img;
public LoadedImage(Image i) {
set(i);
}
void set(Image i) {
MediaTracker mt = new MediaTracker(this);
mt.addImage(i, 0);
try {
mt.waitForAll();
} catch (InterruptedException e) {
System.out.println("Interrupted");
return;
}
img = i;
repaint();
}
public void paint(Graphics g) {
if (img == null) {
g.drawString("no image", 10, 30);
} else {
g.drawImage(img, 0, 0, this);
}
}
public Dimension getPreferredSize() {
return new Dimension(img.getWidth(this), img.getHeight(this));
}
public Dimension getMinimumSize() {
return getPreferredSize();
}
}
操作步骤:
-
创建
LoadedImage对象,传入图像。 -
使用
set()方法设置新图像。 - 自动加载图像并重新绘制。
4.2.4 具体过滤器类
以下是几个具体的过滤器类示例:
Grayscale 类
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
class Grayscale extends RGBImageFilter implements PlugInFilter {
public Image filter(Applet a, Image in) {
return a.createImage(new FilteredImageSource(in.getSource(), this));
}
public int filterRGB(int x, int y, int rgb) {
int r = (rgb >> 16) & 0xff;
int g = (rgb >> 8) & 0xff;
int b = rgb & 0xff;
int k = (int) (.56 * g + .33 * r + .11 * b);
return (0xff000000 | k << 16 | k << 8 | k);
}
}
Invert 类
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
class Invert extends RGBImageFilter implements PlugInFilter {
public Image filter(Applet a, Image in) {
return a.createImage(new FilteredImageSource(in.getSource(), this));
}
public int filterRGB(int x, int y, int rgb) {
int r = 0xff - (rgb >> 16) & 0xff;
int g = 0xff - (rgb >> 8) & 0xff;
int b = 0xff - rgb & 0xff;
return (0xff000000 | r << 16 | g << 8 | b);
}
}
Contrast 类
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
public class Contrast extends RGBImageFilter implements PlugInFilter {
public Image filter(Applet a, Image in) {
return a.createImage(new FilteredImageSource(in.getSource(), this));
}
private int multclamp(int in, double factor) {
in = (int) (in * factor);
return in > 255 ? 255 : in;
}
double gain = 1.2;
private int cont(int in) {
return (in < 128) ? (int) (in / gain) : multclamp(in, gain);
}
public int filterRGB(int x, int y, int rgb) {
int r = cont((rgb >> 16) & 0xff);
int g = cont((rgb >> 8) & 0xff);
int b = cont(rgb & 0xff);
return (0xff000000 | r << 16 | g << 8 | b);
}
}
4.3 表格:过滤器类功能总结
| 过滤器类 | 功能描述 |
|---|---|
Grayscale
| 将图像转换为灰度图。 |
Invert
| 反转图像的颜色。 |
Contrast
| 增强图像的对比度。 |
Blur
| 对图像进行模糊处理。 |
Sharpen
| 对图像进行锐化处理。 |
4.4 流程图:图像过滤流程
graph TD;
A[加载图像] --> B[选择过滤器];
B --> C[应用过滤器];
C --> D[显示过滤后的图像];
通过以上介绍,我们了解了 Java 中图像编程的多个方面,包括图像加载、生成、像素抓取和过滤等。这些技术可以帮助我们实现各种图像处理任务,如创建自定义图像、提取图像区域、改变图像颜色和增强图像效果等。希望这些内容对你有所帮助,你可以根据自己的需求进一步探索和应用这些技术。
5. 卷积过滤器相关类
5.1 Convolver 抽象类
Convolver
是一个抽象类,它实现了
ImageConsumer
接口,用于处理卷积过滤器的基本操作。它将源像素移动到一个名为
imgpixels
的数组中,并创建另一个名为
newimgpixels
的数组来存储过滤后的数据。卷积过滤器会对图像中每个像素周围的一个小矩形区域(即卷积核)进行采样,以此决定如何改变该区域的中心像素。
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
abstract class Convolver implements ImageConsumer, PlugInFilter {
int width, height;
int imgpixels[], newimgpixels[];
boolean imageReady = false;
abstract void convolve(); // 具体的过滤操作
public Image filter(Applet a, Image in) {
imageReady = false;
in.getSource().startProduction(this);
waitForImage();
newimgpixels = new int[width*height];
try {
convolve();
} catch (Exception e) {
System.out.println("Convolver failed: " + e);
e.printStackTrace();
}
return a.createImage(
new MemoryImageSource(width, height, newimgpixels, 0, width));
}
synchronized void waitForImage() {
try {
while(!imageReady) wait();
} catch (Exception e) {
System.out.println("Interrupted");
}
}
public void setProperties(java.util.Hashtable<?,?> dummy) { }
public void setColorModel(ColorModel dummy) { }
public void setHints(int dummy) { }
public synchronized void imageComplete(int dummy) {
imageReady = true;
notifyAll();
}
public void setDimensions(int x, int y) {
width = x;
height = y;
imgpixels = new int[x*y];
}
public void setPixels(int x1, int y1, int w, int h,
ColorModel model, byte pixels[], int off, int scansize) {
int pix, x, y, x2, y2, sx, sy;
x2 = x1+w;
y2 = y1+h;
sy = off;
for(y=y1; y<y2; y++) {
sx = sy;
for(x=x1; x<x2; x++) {
pix = model.getRGB(pixels[sx++]);
if((pix & 0xff000000) == 0)
pix = 0x00ffffff;
imgpixels[y*width+x] = pix;
}
sy += scansize;
}
}
public void setPixels(int x1, int y1, int w, int h,
ColorModel model, int pixels[], int off, int scansize) {
int pix, x, y, x2, y2, sx, sy;
x2 = x1+w;
y2 = y1+h;
sy = off;
for(y=y1; y<y2; y++) {
sx = sy;
for(x=x1; x<x2; x++) {
pix = model.getRGB(pixels[sx++]);
if((pix & 0xff000000) == 0)
pix = 0x00ffffff;
imgpixels[y*width+x] = pix;
}
sy += scansize;
}
}
}
操作步骤:
-
实现
convolve()抽象方法,定义具体的卷积过滤操作。 -
在
filter()方法中,启动图像生产,等待图像准备好,创建存储过滤后数据的数组,调用convolve()方法进行过滤,最后创建并返回过滤后的图像。
5.2 Blur 类
Blur
类是
Convolver
的子类,它对源图像数组
imgpixels
中的每个像素进行遍历,计算其周围 3x3 区域的平均值,并将该平均值作为
newimgpixels
中对应输出像素的值。
public class Blur extends Convolver {
public void convolve() {
for(int y=1; y<height-1; y++) {
for(int x=1; x<width-1; x++) {
int rs = 0;
int gs = 0;
int bs = 0;
for(int k=-1; k<=1; k++) {
for(int j=-1; j<=1; j++) {
int rgb = imgpixels[(y+k)*width+x+j];
int r = (rgb >> 16) & 0xff;
int g = (rgb >> 8) & 0xff;
int b = rgb & 0xff;
rs += r;
gs += g;
bs += b;
}
}
rs /= 9;
gs /= 9;
bs /= 9;
newimgpixels[y*width+x] = (0xff000000 |
rs << 16 | gs << 8 | bs);
}
}
}
}
5.3 Sharpen 类
Sharpen
类也是
Convolver
的子类,它与
Blur
类大致相反。它遍历源图像数组
imgpixels
中的每个像素,计算其周围 3x3 区域(不包括中心像素)的平均值,然后将中心像素与周围平均值的差值加到中心像素上,作为
newimgpixels
中对应输出像素的值。这样可以突出图像的边缘,同时保持平滑区域不变。
public class Sharpen extends Convolver {
private final int clamp(int c) {
return (c > 255 ? 255 : (c < 0 ? 0 : c));
}
public void convolve() {
int r0=0, g0=0, b0=0;
for(int y=1; y<height-1; y++) {
for(int x=1; x<width-1; x++) {
int rs = 0;
int gs = 0;
int bs = 0;
for(int k=-1; k<=1; k++) {
for(int j=-1; j<=1; j++) {
int rgb = imgpixels[(y+k)*width+x+j];
int r = (rgb >> 16) & 0xff;
int g = (rgb >> 8) & 0xff;
int b = rgb & 0xff;
if (j == 0 && k == 0) {
r0 = r;
g0 = g;
b0 = b;
} else {
rs += r;
gs += g;
bs += b;
}
}
}
rs >>= 3;
gs >>= 3;
bs >>= 3;
newimgpixels[y*width+x] = (0xff000000 |
clamp(r0+r0-rs) << 16 |
clamp(g0+g0-gs) << 8 |
clamp(b0+b0-bs));
}
}
}
}
5.4 表格:卷积过滤器类对比
| 过滤器类 | 操作方式 | 效果 |
|---|---|---|
Blur
| 计算每个像素 3x3 区域的平均值 | 模糊图像 |
Sharpen
| 计算每个像素 3x3 区域(不包括中心)的平均值,用中心像素与平均值的差值调整中心像素 | 锐化图像,突出边缘 |
5.5 流程图:卷积过滤流程
graph TD;
A[加载源图像] --> B[初始化 Convolver];
B --> C[将源像素存入 imgpixels 数组];
C --> D[选择具体卷积子类(Blur 或 Sharpen)];
D --> E[执行 convolve() 方法];
E --> F[将过滤后数据存入 newimgpixels 数组];
F --> G[创建并返回过滤后的图像];
6. 总结
通过上述内容,我们全面了解了 Java 中图像编程的丰富知识和技术。从图像的加载与跟踪开始,我们学会了如何确保图像正确加载并进行显示。
ImageProducer
和
ImageConsumer
接口及其具体实现类
MemoryImageSource
和
PixelGrabber
,让我们能够生成自定义图像和抓取现有图像的像素数据。
ImageFilter
类及其子类为我们提供了强大的图像处理能力,包括提取图像区域、改变图像颜色和增强图像效果等。其中,
CropImageFilter
可以方便地从大图像中提取小图像,
RGBImageFilter
能够逐像素地对图像进行颜色转换。动态插件策略的引入,使得我们可以根据需要灵活加载不同的过滤器,实现多样化的图像处理需求。
卷积过滤器相关类
Convolver
及其子类
Blur
和
Sharpen
,则为我们提供了更高级的图像处理功能,能够对图像进行模糊和锐化处理,突出图像的特征。
在实际应用中,我们可以根据具体需求选择合适的技术和过滤器,实现各种复杂的图像处理任务。例如,在游戏开发中,可以使用图像过滤技术实现特效;在图像处理软件中,可以利用卷积过滤器增强图像的质量。希望这些知识能够帮助你在 Java 图像编程领域取得更好的成果。
6.1 操作步骤总结
-
图像加载与跟踪
:创建
MediaTracker,添加图像,检查加载状态。 -
图像生成
:使用
MemoryImageSource,创建像素数组,生成图像。 -
像素抓取
:使用
PixelGrabber,创建数组,抓取像素数据。 - 图像过滤 :选择合适的过滤器类,应用过滤器,显示过滤后的图像。
-
卷积过滤
:继承
Convolver类,实现convolve()方法,进行卷积操作。
6.2 技术点回顾表格
| 技术点 | 相关类/接口 | 作用 |
|---|---|---|
| 图像加载与跟踪 |
MediaTracker
| 确保图像正确加载 |
| 图像生成 |
ImageProducer
、
MemoryImageSource
| 生成自定义图像 |
| 像素抓取 |
ImageConsumer
、
PixelGrabber
| 抓取现有图像的像素数据 |
| 图像过滤 |
ImageFilter
及其子类
| 对图像进行各种处理 |
| 卷积过滤 |
Convolver
及其子类
| 实现图像的模糊和锐化 |
通过不断实践和探索,你可以进一步挖掘 Java 图像编程的潜力,创造出更加精彩的图像处理应用。
超级会员免费看
1278

被折叠的 条评论
为什么被折叠?



