上一个例子是将最后的画面输出为png的图片。不够直观,这次结合framebuffer来显示,会直观的显示在LCD上。这次基于framebuffer 和skia 结合搭建GUI这个文章。
按照之前的了解 skia是故意不和底层相关,比如这次的framebuffer。这使得它可以只专心绘图,具体显示在哪里则不管。同样与参考了Skia and framebuffer。代码不贴了稍后上传,以下是运行截图:
我已经是激动不已了,迫不及待想要把freetype harfbuzz集成进来显示字体,看字体的渲染效果。
代码:
#include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <linux/fb.h> #include <linux/kd.h> #include <sys/mman.h> #include <sys/ioctl.h> #include <sys/time.h> #include <string.h> #include <errno.h> #include "SkBitmap.h" #include "SkDevice.h" #include "SkPaint.h" #include "SkRect.h" #include "SkStream.h" #include "SkTemplates.h" #include "SkMatrix.h" #include "SkImageEncoder.h" #include "SkImageDecoder.h" #ifndef __ANDROID__ #define FRAMEBUFFER "/dev/fb0" #define OUT_DIR "/tmp" #else #define FRAMEBUFFER "/dev/graphics/fb0" #define OUT_DIR "/sdcard/" #endif struct fb_var_screeninfo vinfo; struct fb_fix_screeninfo finfo; char *frameBuffer = 0; #define SKIA_SCALE 0 //打印fb驱动中fix结构信息,注:在fb驱动加载后,fix结构不可被修改。 void printFixedInfo() { printf("Fixed screen info:\n" "\tid: %s\n" "\tsmem_start: 0x%lx\n" "\tsmem_len: %d\n" "\ttype: %d\n" "\ttype_aux: %d\n" "\tvisual: %d\n" "\txpanstep: %d\n" "\typanstep: %d\n" "\tywrapstep: %d\n" "\tline_length: %d\n" "\tmmio_start: 0x%lx\n" "\tmmio_len: %d\n" "\taccel: %d\n" "\n", finfo.id, finfo.smem_start, finfo.smem_len, finfo.type, finfo.type_aux, finfo.visual, finfo.xpanstep, finfo.ypanstep, finfo.ywrapstep, finfo.line_length, finfo.mmio_start, finfo.mmio_len, finfo.accel); } //打印fb驱动中var结构信息,注:fb驱动加载后,var结构可根据实际需要被重置 void printVariableInfo() { printf("Variable screen info:\n" "\txres: %d\n" "\tyres: %d\n" "\txres_virtual: %d\n" "\tyres_virtual: %d\n" "\tyoffset: %d\n" "\txoffset: %d\n" "\tbits_per_pixel: %d\n" "\tgrayscale: %d\n" "\tred: offset: -, length: -, msb_right: -\n" "\tgreen: offset: -, length: -, msb_right: -\n" "\tblue: offset: -, length: -, msb_right: -\n" "\ttransp: offset: -, length: -, msb_right: -\n" "\tnonstd: %d\n" "\tactivate: %d\n" "\theight: %d\n" "\twidth: %d\n" "\taccel_flags: 0x%x\n" "\tpixclock: %d\n" "\tleft_margin: %d\n" "\tright_margin: %d\n" "\tupper_margin: %d\n" "\tlower_margin: %d\n" "\thsync_len: %d\n" "\tvsync_len: %d\n" "\tsync: %d\n" "\tvmode: %d\n" "\n", vinfo.xres, vinfo.yres, vinfo.xres_virtual, vinfo.yres_virtual, vinfo.xoffset, vinfo.yoffset, vinfo.bits_per_pixel, vinfo.grayscale, vinfo.red.offset, vinfo.red.length, vinfo.red.msb_right, vinfo.green.offset, vinfo.green.length, vinfo.green.msb_right, vinfo.blue.offset, vinfo.blue.length, vinfo.blue.msb_right, vinfo.transp.offset, vinfo.transp.length, vinfo.transp.msb_right, vinfo.nonstd, vinfo.activate, vinfo.height, vinfo.width, vinfo.accel_flags, vinfo.pixclock, vinfo.left_margin, vinfo.right_margin, vinfo.upper_margin, vinfo.lower_margin, vinfo.hsync_len, vinfo.vsync_len, vinfo.sync, vinfo.vmode); } //画大小为width*height的同色矩阵,8alpha+8reds+8greens+8blues void drawRect_rgb32(int x0, int y0, int width, int height, int color) { const int bytesPerPixel = 4; const int stride = finfo.line_length / bytesPerPixel; int *dest = (int *) (frameBuffer) + (y0 + vinfo.yoffset) * stride + (x0 + vinfo.xoffset); int x, y; for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { dest[x] = color; } dest += stride; } } //画大小为width*height的同色矩阵,5reds+6greens+5blues void drawRect_rgb16(int x0, int y0, int width, int height, int color) { const int bytesPerPixel = 2; const int stride = finfo.line_length / bytesPerPixel; const int red = (color & 0xff0000) >> (16 + 3); const int green = (color & 0xff00) >> (8 + 2); const int blue = (color & 0xff) >> 3; const short color16 = blue | (green << 5) | (red << (5 + 6)); short *dest = (short *) (frameBuffer) + (y0 + vinfo.yoffset) * stride + (x0 + vinfo.xoffset); int x, y; for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { dest[x] = color16; } dest += stride; } } //画大小为width*height的同色矩阵,5reds+5greens+5blues void drawRect_rgb15(int x0, int y0, int width, int height, int color) { const int bytesPerPixel = 2; const int stride = finfo.line_length / bytesPerPixel; const int red = (color & 0xff0000) >> (16 + 3); const int green = (color & 0xff00) >> (8 + 3); const int blue = (color & 0xff) >> 3; const short color15 = blue | (green << 5) | (red << (5 + 5)) | 0x8000; short *dest = (short *) (frameBuffer) + (y0 + vinfo.yoffset) * stride + (x0 + vinfo.xoffset); int x, y; for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { dest[x] = color15; } dest += stride; } } void drawRect(int x0, int y0, int width, int height, int color) { switch (vinfo.bits_per_pixel) { case 32: drawRect_rgb32(x0, y0, width, height, color); break; case 16: drawRect_rgb16(x0, y0, width, height, color); break; case 15: drawRect_rgb15(x0, y0, width, height, color); break; default: printf("Warning: drawRect() not implemented for color depth %i\n", vinfo.bits_per_pixel); break; } } #define PERFORMANCE_RUN_COUNT 5 void performSpeedTest(void *fb, int fbSize) { int i, j, run; struct timeval startTime, endTime; unsigned long long results[PERFORMANCE_RUN_COUNT]; unsigned long long average; unsigned int *testImage; unsigned int randData[17] = { 0x3A428472, 0x724B84D3, 0x26B898AB, 0x7D980E3C, 0x5345A084, 0x6779B66B, 0x791EE4B4, 0x6E8EE3CC, 0x63AF504A, 0x18A21B33, 0x0E26EB73, 0x022F708E, 0x1740F3B0, 0x7E2C699D, 0x0E8A570B, 0x5F2C22FB, 0x6A742130 }; printf("Frame Buffer Performance test...\n"); for (run = 0; run < PERFORMANCE_RUN_COUNT; ++run) { testImage = (unsigned int *) malloc(fbSize); j = run; for (i = 0; i < (int) (fbSize / sizeof(int)); ++i) { testImage[i] = randData[j]; j++; if (j >= 17) j = 0; } gettimeofday(&startTime, NULL); memcpy(fb, testImage, fbSize); gettimeofday(&endTime, NULL); long secsDiff = endTime.tv_sec - startTime.tv_sec; results[run] = secsDiff * 1000000 + (endTime.tv_usec - startTime.tv_usec); free(testImage); } average = 0; for (i = 0; i < PERFORMANCE_RUN_COUNT; ++i) average += results[i]; average = average / PERFORMANCE_RUN_COUNT; printf(" Average: %llu usecs\n", average); printf(" Bandwidth: %.03f MByte/Sec\n", (fbSize / 1048576.0) / ((double) average / 1000000.0)); printf(" Max. FPS: %.03f fps\n\n", 1000000.0 / (double) average); memset(fb, 0, fbSize); } //图片解码 将图片解码到bitmap的内存中,然后可以填充到framebuffer中。 bool SkFileDecoder(SkBitmap* bitmap, const char srcPath[], int bits_per_pixel) { SkFILEStream stream(srcPath); if (!stream.isValid()) { printf("ERROR: bad filename <%s>\n", srcPath); return false; } stream.rewind(); SkImageDecoder* codec = SkImageDecoder::Factory(&stream); if (NULL == codec) { printf("ERROR: no codec found for <%s>\n", srcPath); return false; } SkBitmap::Config config; switch (bits_per_pixel) { case 32: config = SkBitmap::kARGB_8888_Config; break; case 16: config = SkBitmap::kRGB_565_Config; break; default: printf("Warning: drawRect() not implemented for color depth %i\n", bits_per_pixel); break; } SkAutoTDelete < SkImageDecoder > ad(codec); #if SKIA_SCALE SkBitmap tmp_bitmap; stream.rewind(); if (!codec->decode(&stream, &tmp_bitmap, config , SkImageDecoder::kDecodeBounds_Mode)) { printf("ERROR: codec failed for <%s>\n", srcPath); return false; } int maxpix = (tmp_bitmap.width() > tmp_bitmap.height()) ? tmp_bitmap.width():tmp_bitmap.width(); int minpix = (tmp_bitmap.width() < tmp_bitmap.height()) ? tmp_bitmap.width():tmp_bitmap.width(); int sample1 = maxpix/1028 + 1; int sample2 = maxpix/726 + 1; codec->setSampleSize(sample1>sample2 ? sample1:sample2); #endif stream.rewind(); if (!codec->decode(&stream, bitmap, config, SkImageDecoder::kDecodePixels_Mode)) { printf("ERROR: codec failed for <%s>\n", srcPath); return false; } return true; } int main(int argc, char **argv) { const char *devfile = FRAMEBUFFER; long int screensize = 0; int fbFd = 0; fbFd = open(devfile, O_RDWR); if (fbFd == -1) { perror("Error: cannot open framebuffer device"); exit(1); } //获取finfo信息并显示 if (ioctl(fbFd, FBIOGET_FSCREENINFO, &finfo) == -1) { perror("Error reading fixed information"); exit(2); } printFixedInfo(); //获取vinfo信息并显示 if (ioctl(fbFd, FBIOGET_VSCREENINFO, &vinfo) == -1) { perror("Error reading variable information"); exit(3); } printVariableInfo(); screensize = finfo.smem_len; frameBuffer = (char *) mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbFd, 0); if (frameBuffer == MAP_FAILED ) { perror("Error: Failed to map framebuffer device to memory"); exit(4); } //测试virt fb的性能 performSpeedTest(frameBuffer, screensize); memset(frameBuffer, 0, screensize); printf("Will draw 3 rectangles on the screen,\n" "they should be colored red, green and blue (in that order).\n"); drawRect(vinfo.xres / 8, vinfo.yres / 8, vinfo.xres / 4, vinfo.yres / 4, 0xffff0000); drawRect(vinfo.xres * 3 / 8, vinfo.yres * 3 / 8, vinfo.xres / 4, vinfo.yres / 4, 0xff00ff00); drawRect(vinfo.xres * 5 / 8, vinfo.yres * 5 / 8, vinfo.xres / 4, vinfo.yres / 4, 0xff0000ff); sleep(5); printf(" Done.\n"); memset(frameBuffer, 0, screensize); SkBitmap::Config config; switch (vinfo.bits_per_pixel) { case 32: config = SkBitmap::kARGB_8888_Config; break; case 16: config = SkBitmap::kRGB_565_Config; break; default: break; } SkBitmap bitmap; bitmap.setConfig(config, vinfo.xres, vinfo.yres); bitmap.allocPixels(); SkCanvas canvas(bitmap); SkRect r; SkColor color = 0; SkPaint paint; paint.setARGB(255, 255, 0, 0); paint.setStrokeWidth(4); canvas.drawPoint(80, 80, 0xffff0000); memcpy(frameBuffer, bitmap.getPixels(), screensize); sleep(1); bitmap.eraseColor(0); SkIRect rect { 100, 100, 300, 300 }; canvas.drawIRect(rect, paint); memcpy(frameBuffer, bitmap.getPixels(), screensize); sleep(1); bitmap.eraseColor(0); canvas.drawCircle(400, 300, 50, paint); memcpy(frameBuffer, bitmap.getPixels(), screensize); sleep(1); bitmap.eraseColor(0); canvas.drawLine(160, 10, 320, 110, paint); memcpy(frameBuffer, bitmap.getPixels(), screensize); sleep(1); bitmap.eraseColor(0); paint.setARGB(255, 255, 0, 0); canvas.drawCircle(400, 300, 200, paint); paint.setARGB(255, 255, 0, 155); canvas.drawCircle(400, 300, 195, paint); paint.setARGB(255, 0, 255, 255); canvas.drawLine(220, 300, 400, 300, paint); memcpy(frameBuffer, bitmap.getPixels(), screensize); for (int i = 0; i <= 0xffffffff; i++) { bitmap.eraseColor(0); SkBitmap Ibitmap; char name[32] = ""; snprintf(name, sizeof(name), "/sdcard/%d.png", i % 7); SkFileDecoder(&Ibitmap, name, vinfo.bits_per_pixel); SkIRect src { 0, 0, bitmap.width(), bitmap.height() }; SkRect dst { 0, 0, Ibitmap.width(), bitmap.height() }; canvas.drawBitmapRect(Ibitmap, &src, dst); //SkScalar degree = 0; //SkScalar scal = 1; //SkScalar x = bitmap.width(); //SkScalar y = bitmap.height(); memcpy(frameBuffer, bitmap.getPixels(), screensize); sleep(2); } munmap(frameBuffer, screensize); //解除内存映射,与mmap对应 close(fbFd); return 0; }