Java将图像像素保存到数组中绘制图像 [英] Java save image pixels into an array & draw image
问题描述
我正在开发一个仅下载jar的游戏,当您下载jar时,游戏将下载新的缓存.
同时我想显示一个漂亮的背景,而不是将其加载到链接之外,我想到了这个主意,但不确定是否可行.
每个图像都加载并逐像素绘制,是否有可能获取图像的所有像素颜色,宽度,高度,然后打印值&然后将它们放在一个数组中,例如:
public int [] imagePixels = new int [] {此处放置像素...};
然后简单地使用一种方法来绘制背景?那有可能吗?
是否有更好的解决方案,例如将图像包装到罐子中?
说明:
您有一张图片,我想加载该图片并加载每个像素,我们从第0行开始,按宽度&高度.
我想收集每个像素并将其保存到图像中,因此我可以在不使用任何文件的情况下加载图像,而只需从阵列上绘制像素即可.
好的,所以您将面临基本问题.大多数图像格式都对图像数据进行某种压缩,它们也可能将有关图像的重要数据附加到文件末尾,例如颜色模型信息,这使得在读取它们时呈现它们变得有些困难.>
您需要的是一种将图像的块"写入文件的方法,该文件可以轻松读取但不会显着增加文件大小.
我的测试图像开始于301.68 kb,而我的块"文件格式最终为1.42 mb,直到我测试了一个未压缩的文件,最终为5.63 mb时,我对此并不特别满意.为了钱.
该示例使用内置的 GZip
压缩,您可以使用 Apache-Commons-Compress
在纸面上,这基本上是做什么的...
- 读取一部分像素数据,并将其写入以逗号分隔的
String
,其中每个值都是图像中给定的像素值.该示例每个块读取文件的10%. - 然后使用
GZip
压缩 压缩每个块 - 然后使用
Base64
编码对得到的压缩字节进行编码.我个人更喜欢使用Apache-Commons-Encode
a>,因为它减少了对内部/私有类的依赖. - 然后将生成的编码后的
String
写入File
,并在该行的末尾放置新行.
图像反向加载...
- 从文件中读取一行(Base64编码的
String
) -
String
被解码(压缩为byte
数组) - 然后将
byte
数组解压缩为逗号分隔的String
- 然后将逗号分隔的
String
进行split
并将得到的像素数据绘制到后备缓冲区 - 生成的后备缓冲区已更新到屏幕...
理论是好的,实现是...有点混乱,对不起,可能有点儿整洁,但是你明白了.
此想法的目的是不立即读取整个 Image.dat
文件,而是将其保留在原处并一次读取一行...这允许延迟.
现在,在此示例中,我使用了 javax.swing.Timer
注入一点暂停,说实话,最好使用 SwingWorker
...但我确定您知道这个主意...
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;导入java.awt.BorderLayout;导入java.awt.Color;导入java.awt.Dimension;导入java.awt.EventQueue;导入java.awt.Graphics;导入java.awt.Graphics2D;导入java.awt.Point;导入java.awt.event.ActionEvent;导入java.awt.event.ActionListener;导入java.awt.image.BufferedImage;导入java.io.BufferedReader;导入java.io.BufferedWriter;导入java.io.ByteArrayInputStream;导入java.io.ByteArrayOutputStream;导入java.io.File;导入java.io.FileReader;导入java.io.FileWriter;导入java.io.IOException;导入java.util.zip.GZIPInputStream;导入java.util.zip.GZIPOutputStream;导入javax.imageio.ImageIO;导入javax.swing.JFrame;导入javax.swing.JPanel;导入javax.swing.Timer;导入javax.swing.UIManager;导入javax.swing.UnsupportedLookAndFeelException;公共类ConvertImage {公共静态void main(String [] args){尝试 {exportImage(新文件("/path/to/your/image.jpg"),新文件("Image.dat"));} catch(IOException ex){ex.printStackTrace();}新的ConvertImage();}公共ConvertImage(){EventQueue.invokeLater(new Runnable(){@Override公共无效run(){尝试 {UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());} catch(ClassNotFoundException ex){} catch(InstantiationException ex){}捕获(前为IllegalAccessException){} catch(ex UnsupportedLookAndFeelException ex){}JFrame frame = new JFrame("Test");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setLayout(new BorderLayout());frame.add(new TestPane());frame.pack();frame.setLocationRelativeTo(null);frame.setVisible(true);}});}公共类TestPane扩展了JPanel {private int imgWidth = 0;private int imgHeight = 0;private BufferedReader br = null;私有BufferedImage imgBuffer;私有int偏移量;公共TestPane(){尝试 {br = new BufferedReader(new FileReader(new File("Image.dat"))));字符串标头= br.readLine();String []部分= header.split("x");imgWidth = Integer.parseInt(parts [0]);imgHeight = Integer.parseInt(parts [1]);imgBuffer =新的BufferedImage(imgWidth,imgHeight,BufferedImage.TYPE_INT_ARGB);计时器计时器=新计时器(1000,新ActionListener(){@Override公共无效actionPerformed(ActionEvent e){Graphics2D g2d = null;尝试 {字符串文本= br.readLine();if(text!= null){//将字符串解码回压缩的字节数组字节[]解码= Base64.decode(文本);GZIPInputStream zis = null;尝试 {//解压缩字节数组zis = new GZIPInputStream(new ByteArrayInputStream(decode));//建立像素的文字表示StringBuilder sb =新的StringBuilder(128);byte []缓冲区=新的byte [1024];int bytesRead = -1;而((bytesRead = zis.read(buffer))> -1){sb.append(new String(buffer,0,bytesRead,"UTF-8"));}//将像素拆分为单个打包的intString []元素= sb.toString().split(,");g2d = imgBuffer.createGraphics();对于(String element:elements){点p = getPointAt(offset,imgWidth,imgHeight);g2d.setColor(new Color(Integer.parseInt(element),true));g2d.drawLine(p.x,p.y,p.x,p.y);偏移++;}g2d.dispose();repaint();} catch(Exception exp){exp.printStackTrace();}} 别的 {尝试 {br.close();} catch(Exception exp){}((计时器)e.getSource()).stop();}} catch(IOException ex){ex.printStackTrace();尝试 {br.close();} catch(Exception exp){}((计时器)e.getSource()).stop();} 最后 {尝试 {g2d.dispose();} catch(Exception exp){}}}});timer.start();} catch(IOException ex){ex.printStackTrace();尝试 {br.close();} catch(Exception e){}}}@Override公共维度getPreferredSize(){返回新的Dimension(imgWidth,imgHeight);}@Override受保护的void paintComponent(Graphics g){super.paintComponent(g);int x =(getWidth()-imgBuffer.getWidth())/2;int y =(getHeight()-imgBuffer.getHeight())/2;g.drawImage(imgBuffer,x,y,this);}}受保护的静态void exportImage(文件输入,文件输出)抛出IOException {BufferedImage img = ImageIO.read(in);int width = img.getWidth();int height = img.getHeight();//计算图像的总长度"int imageLength =宽度*高度;//计算我们将产生的每一行的长度//这是每个块的像素数int runLength = Math.round((width * height)* 0.1f);//写入输出的位置BufferedWriter bw = null;尝试 {bw = new BufferedWriter(new FileWriter(out));bw.write(width +"x" + height);bw.newLine();//开始转换像素...int偏移= 0;while(offset< imageLength){//计算下一个缓冲区运行的大小,我们不想//超出图片的末尾int bufferSize = runLength;if(offset + bufferSize> imageLength){bufferSize = imageLength-偏移量;}//创建一个缓冲区以将像素结果存储在...StringBuilder sb =新的StringBuilder(bufferSize * 2);for(int index = 0; index< bufferSize; index ++){点p = getPointAt(offset + index,width,height);如果(sb.length()> 0){sb.append(,");}//存储像素sb.append(img.getRGB(p.x,p.y));}//将内容写入压缩流中...ByteArrayOutputStream baos = new ByteArrayOutputStream();GZIPOutputStream zos =新的GZIPOutputStream(baos);zos.write(sb.toString().getBytes());zos.flush();zos.close();//将压缩结果编码为Base64编码的字符串= Base64.encode(baos.toByteArray());//写内容bw.write(已编码);bw.newLine();//跳至下一个大块"偏移量+ = bufferSize;}} catch(IOException exp){exp.printStackTrace();} 最后 {尝试 {bw.close();} catch(Exception e){}}}公共静态Point getPointAt(int index,int width,int height){点p = new Point();p.y =索引/宽度;p.x =索引%宽度;返回p;}}
I am working on a game where you only download the jar, and when you download the jar, the game will download the new cache.
Meanwhile I want to show a nice background, without loading it off a link, I've thought of this idea, I am not sure if it is possible though.
Every image is loaded and is being drawn pixel by pixel, is it possible to get ALL pixels colors of an image, width, height and then print the values & then put them in an array eg:
public int[] imagePixels = new int[]{PUT PIXELS HERE...};
And then simply use a method to draw that background? Is that possible to do?
Are there any better solutions for this like packing the image into the jar or something?
Explanation:
You have an image, I want to load that image and load every single pixel, we start from row 0, by width & height.
I want to collect every single pixel and save it into an image, so then I can load the image without using any file, and just draw the pixels off the array.
Okay, so the basic problems you will face. Most image formats employee some kind of compression on the image data, they may also append important data about the image to the end of the file, such as the color model information, which makes rendering them as they are read some what difficult.
What you need is some way to write "chunks" of the image to a file that can be easily read back but does not dramatically increase the file size.
My test image started at 301.68 kb and my "chunk" file format end up as 1.42 mb, which I wasn't particularly happy with until I tested a uncompressed file which ended up at 5.63 mb...think I can live for the momement.
The example uses the inbuilt GZip
compression, you can get better compression by using something like Apache-Commons-Compress
On paper, what this basically does...
- Reads a chunk of the pixel data, writing this to a comma-separated
String
, where it each value is a given pixel value from the image. The example reads 10% of the file per chunk. - Each chunk is then compressed using a
GZip
compression - The resulting compressed bytes are then encoded using a
Base64
encoding. Personally I'd prefer to useApache-Commons-Encode
as it places less relience on an internal/private class. - The resulting encoded
String
is then written to aFile
and a new line is placed at the end of the line.
The image is loaded in reverse...
- A line is read from the file (Base64 encoded
String
) - The
String
is decoded (to a compressedbyte
array) - The
byte
array is then decompressed into a comma separatedString
- The comma separated
String
is thensplit
and the resulting pixel data is drawn to a backing buffer - The resulting backing buffer is updated to the screen...
The theory is all good, the implementation is...a little messy, sorry, could be a little tidier, but you get the idea.
The intention with this idea would be to not read the entire Image.dat
file at once, but instead, leaving it where it is and read a line at a time...this allows for latency.
Now, in this example, I've used a javax.swing.Timer
to inject a little pause, to be honest, it would be better to use a SwingWorker
...but I'm sure you get the idea...
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ConvertImage {
public static void main(String[] args) {
try {
exportImage(new File("/path/to/your/image.jpg"), new File("Image.dat"));
} catch (IOException ex) {
ex.printStackTrace();
}
new ConvertImage();
}
public ConvertImage() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private int imgWidth = 0;
private int imgHeight = 0;
private BufferedReader br = null;
private BufferedImage imgBuffer;
private int offset;
public TestPane() {
try {
br = new BufferedReader(new FileReader(new File("Image.dat")));
String header = br.readLine();
String[] parts = header.split("x");
imgWidth = Integer.parseInt(parts[0]);
imgHeight = Integer.parseInt(parts[1]);
imgBuffer = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_ARGB);
Timer timer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Graphics2D g2d = null;
try {
String text = br.readLine();
if (text != null) {
// Decode the String back to a compressed byte array
byte[] decode = Base64.decode(text);
GZIPInputStream zis = null;
try {
// Decompress the byte array
zis = new GZIPInputStream(new ByteArrayInputStream(decode));
// Build the text representation of the pixels
StringBuilder sb = new StringBuilder(128);
byte[] buffer = new byte[1024];
int bytesRead = -1;
while ((bytesRead = zis.read(buffer)) > -1) {
sb.append(new String(buffer, 0, bytesRead, "UTF-8"));
}
// Split the pixels into individual packed ints
String[] elements = sb.toString().split(",");
g2d = imgBuffer.createGraphics();
for (String element : elements) {
Point p = getPointAt(offset, imgWidth, imgHeight);
g2d.setColor(new Color(Integer.parseInt(element), true));
g2d.drawLine(p.x, p.y, p.x, p.y);
offset++;
}
g2d.dispose();
repaint();
} catch (Exception exp) {
exp.printStackTrace();
}
} else {
try {
br.close();
} catch (Exception exp) {
}
((Timer) e.getSource()).stop();
}
} catch (IOException ex) {
ex.printStackTrace();
try {
br.close();
} catch (Exception exp) {
}
((Timer) e.getSource()).stop();
} finally {
try {
g2d.dispose();
} catch (Exception exp) {
}
}
}
});
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
try {
br.close();
} catch (Exception e) {
}
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(imgWidth, imgHeight);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = (getWidth() - imgBuffer.getWidth()) / 2;
int y = (getHeight() - imgBuffer.getHeight()) / 2;
g.drawImage(imgBuffer, x, y, this);
}
}
protected static void exportImage(File in, File out) throws IOException {
BufferedImage img = ImageIO.read(in);
int width = img.getWidth();
int height = img.getHeight();
// Calculate the total "length" of the image
int imageLength = width * height;
// Calculate the length of each line we will produce
// This is the number of pixels per chunk
int runLength = Math.round((width * height) * 0.1f);
// The place to write the output
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter(out));
bw.write(width + "x" + height);
bw.newLine();
// Start converting the pixels...
int offset = 0;
while (offset < imageLength) {
// Calculate the size of the next buffer run, we don't want to
// over run the end of the image
int bufferSize = runLength;
if (offset + bufferSize > imageLength) {
bufferSize = imageLength - offset;
}
// Create a buffer to store the pixel results in...
StringBuilder sb = new StringBuilder(bufferSize * 2);
for (int index = 0; index < bufferSize; index++) {
Point p = getPointAt(offset + index, width, height);
if (sb.length() > 0) {
sb.append(",");
}
// Store the pixel
sb.append(img.getRGB(p.x, p.y));
}
// Write the contents to a compressed stream...
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream zos = new GZIPOutputStream(baos);
zos.write(sb.toString().getBytes());
zos.flush();
zos.close();
// Encode the compressed results to Base64
String encoded = Base64.encode(baos.toByteArray());
// Write the content...
bw.write(encoded);
bw.newLine();
// Jump to the next "chunk"
offset += bufferSize;
}
} catch (IOException exp) {
exp.printStackTrace();
} finally {
try {
bw.close();
} catch (Exception e) {
}
}
}
public static Point getPointAt(int index, int width, int height) {
Point p = new Point();
p.y = index / width;
p.x = index % width;
return p;
}
}
这篇关于Java将图像像素保存到数组中绘制图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!