使用Linux Framebuffer繪制32位真彩圖形:
并發了朋友圈表示這件事結束了,玩了一天,玩惡心了。
但是我依然是想做出一個可以拖拽的不規則GUI界面(用皮鞋或者小小的照片做界面輪廓)來的。是以半夜就爬起來繼續折騰。
無奈,沒有找到擷取滑鼠焦點的好方法,都太複雜,要知道,我是希望在framebuffer上玩啊,不希望依賴那些已經內建在GUI裡面的東西。
我不就想模拟個拖拽嘛,簡單,用線程控制圖檔在螢幕上漂移,即:
// setPoint方法已經抽象獨立了出來,成為一個static方法,以免main函數太長。
while(true) {
setPoint(width, height, xoffset%200, yoffset%50);
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
xoffset += 2;
yoffset += 2;
}
這個代碼測試下來, 閃爍太厲害了! 根本就沒法看:
怎麼辦?如果簡單的圖檔漂移都這麼閃爍,那如果滑鼠拖拽移動圖檔,結局注定令人遺憾。怎麼辦?
找根源!根源就是 畫圖的時間太久了! 我可是一個像素一個像素畫的啊!
當然了,我知道,如果Java通過JNI将一個像素數組傳遞到本地代碼,然後本地代碼直接
memcpy,那将是令人賽裡布瑞特的。可是我并不知道如何從Java往本地代碼傳遞大數組…另外,我注意是想把事情做純粹些。
我不想把事情交給庫去解決,我要自己解決! (可能賺錢的經理們又要笑我了,但我就是這樣,鄙視業務邏輯。)
利用雙緩沖來解決問題。
意思就是說,
逐像素點畫圖這件耗時的操作,不要直接操作顯存,而是操作一塊預先配置設定好的和顯存一樣大小的緩沖區,等逐點畫圖完成之後,一次性将該緩沖區的内容memcpy到事先mmap好的顯存位址空間。
關于 雙緩沖 技術我就不多說了,這技術的解釋已經爛大街了,諸如什麼流水線相關的形而上解釋,看着都煩了,不過确實是那麼回事。
直接上代碼吧,先看Java代碼 Drawimage.java:
import java.awt.image.*;
import java.io.*;
import javax.imageio.ImageIO;
public class Drawimage {
static File src = null;
static BufferedImage img = null;
native static void setFB(int x, int y, int rgb);
native static void show(int x);
static {
System.loadLibrary("setFB");
static void setPoint(int width, int height, int xoffset, int yoffset) {
int i, j, rgb, a1;
for (i = 1; i < width - 1; i++) {
for (j = 1; j < height-1; j++) {
rgb = img.getRGB(i, j);
a1 = (rgb>>24)&0xFF;
if (a1 != 0) {
Drawimage.setFB(i + xoffset, j + yoffset, rgb);
public static void main(String[] args) throws IOException {
int i, j, width, height, xoffset = 0, yoffset = 0;
src = new File(args[0]);
img = ImageIO.read(src);
width = img.getWidth();
height = img.getHeight();
Drawimage.show(0); // 清空之前的圖形
Drawimage.show(1); // 顯示當下的圖形
try {鄭州不孕不育醫院:http://www.zzfkyy120.com/
Thread.sleep(100); // 這個時間頻率最好和你的顯示器重新整理頻率切合。
xoffset += 2; // x漂移
yoffset += 2; // y漂移
再看本地代碼 setFB.c :
#include
unsigned int *mem = NULL;
// 定義第二緩沖區
unsigned int *back_buffer = NULL;
static struct fb_var_screeninfo info;
void setPixel(int x, int y, int c)
{
int idx;
if (x < 0 || x >= info.xres || y < 0 || y >= info.yres) {
return;
idx = y*info.xres + x;
// 操作第二緩沖區,而不是直接操作顯存
back_buffer[idx] = c;
JNIEXPORT void JNICALL Java_Drawimage_show (JNIEnv *env, jclass class, int
a)
// show指令下達,說明畫圖操作已經完成,這裡一次性替換顯存的内容
// 注意,替換的時機最好是顯示器重新整理的時機,完美契合!!
if (a) {
memcpy(mem, back_buffer, info.xres*info.yres*info.bits_per_pixel/8);
memset(back_buffer, 0, info.xres*info.yres*info.bits_per_pixel/8);
} else {
memset(mem, 0, info.xres*info.yres*info.bits_per_pixel/8);
JNIEXPORT void JNICALL Java_Drawimage_setFB (JNIEnv *env, jclass class,
jint x, jint y, int rgb)
static int fd = -1;
if (fd == -1) {
fd = open("/dev/fb0", O_RDWR);
if (ioctl(fd, FBIOGET_VSCREENINFO, &info)) {
exit(1);
info.bits_per_pixel = 8;
info.xres = 800;
info.yres = 600;
if (ioctl(fd, FBIOPUT_VSCREENINFO, &info)) {
mem = (unsigned int *)mmap(NULL, info.xres*info.yres*info.bits_per_pixel/8,
PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (mem == NULL) {
printf("exit\n");
back_buffer = (unsigned int *)mmap(NULL,
info.xres*info.yres*info.bits_per_pixel/8, PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS, -1, 0);
if (back_buffer == NULL) {
printf("back_buffer exit\n");
setPixel(x, y, rgb);