2021年8月25日 星期三

X window 簡單的繪圖及貼圖程式

 主程式 main.cpp:

#include <opencv2/opencv.hpp> // should include first to prevent conflic
#include <opencv2/highgui.hpp>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/cursorfont.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
typedef unsigned char u8int;
typedef const char*   c_str;
Display *display;
Window     child;
GC        gCtx;      
XColor     fgColor, black, white ,red, green, blue;
XImage  *bg = nullptr;
int width   = 0;
int height  = 0;
void drawRect(int x, int y, int w, int h){
    XDrawRectangle(display, child, gCtx, x, y, w, h);
}
void drawLine(int x1, int y1, int x2, int y2){
    XDrawLine(display, child, gCtx, x1, y1, x2, y2);
}
void drawArc(int x, int y, int w, int h, int ang1, int ang2){
    XDrawArc(display, child, gCtx, x, y, w, h, ang1 << 6, ang2 << 6);
}
void setColorMap(XColor &c, const char *str){
    auto colormap = DefaultColormap(display, 0);
    XParseColor(display, colormap, str, &c);
    XAllocColor(display, colormap, &c);
}
void fgSetColor(XColor &c) {
    fgColor = c;
    XSetForeground(display, gCtx, c.pixel);
}
void setBackGround(u8int *src=nullptr, int sw=0, int sh=0, int sch=0){
    if (bg) XDestroyImage(bg);
    XWindowAttributes info;
    XGetWindowAttributes(display, child, &info);
    Visual *v = info.visual;
    width  = info.width ;
    height = info.height;
    int depth = info.depth;
    int sline = sch * sw;
    int dline= 4 * width;
    int size = dline * height;
    u8int *fb = (u8int *)malloc(size);
    if(! fb) {
        bg = nullptr;
        return;
    }
    bg = XCreateImage(display, v, depth,
        ZPixmap, 0, (char *)fb, width, height, 32, 0
    );
    if(! bg) {
        free(fb);
        return;
    }
    if(! src) {
        memset(fb, 0, size);
        return;
    }
    u8int *rgb = src;
    int zch = sch > 4 ? 4 : sch;
    float xs = (float)sw / width;
    float ys = (float)sh / height;
    float fy = 0;
    for(int y = 0; y < height; y ++) {
        float fx = 0;
        int d = 0;
        int s = 0;
        for (int x = 0; x < width; x ++) {
            fb[d] = rgb[s];
            for(int z = 1; z < zch; z ++) fb[d + z] = rgb[s + z];
            fx += xs;
            d += 4;
            s = sch * (int)fx;
        }
        fy += ys;
        fb += dline;
        rgb = src + sline * (int)fy;
    }
    XPutImage(display, child, gCtx, bg, 0, 0, 0, 0, width, height);
}
void setBackGround(cv::Mat &cam){
    return setBackGround((u8int *)cam.data,
        cam.cols,
        cam.rows,
        cam.channels()
    );
}
void bgRestore(int x0, int y0, int sw = 0, int sh = 0) {
    if (x0 < width && y0 < height){
        if (x0 < 0) x0 = 0;
        if (y0 < 0) y0 = 0;
        if (x0 + sw >=  width) sw = width  - x0;
        if (y0 + sh >= height) sh = height - y0;
        if (sw > 0 && sh > 0) XPutImage(display, child, gCtx,
            bg, x0, y0,
            x0, y0, sw, sh
        );
    }
}
int main(int argc, char **argv){    
    if(argc < 2) {
        printf("no file\n");
        return 1;
    }
    cv::Mat jpeg = cv::imread(argv[1]);// 8UC3
    display = XOpenDisplay(getenv("DISPLAY"));
    if (! display)  {
        printf("X error\n");
        return 3;
    }
    child = XCreateSimpleWindow(display,
        XDefaultRootWindow(display),
        0, 0, 600, 600,
        0,// border width
        WhitePixel(display, 0),
        BlackPixel(display, 0)
    );
    XSetStandardProperties(display, child, "MyXapp",
        "Icon", None, nullptr, 0, nullptr
    );
    gCtx = XCreateGC(display, child, 0, 0);
    setColorMap(black , "#000000");
    setColorMap(white , "#FFFFFF");
    setColorMap(red   , "#FF0000");
    setColorMap(green , "#00FF00");
    setColorMap(blue  , "#0000FF");  
    unsigned char shape[8] = {0xff, 0xff,0xff, 0xff, 0xff, 0xff,0xff, 0xff};
    unsigned char mask[8]  = {0xff, 0xff,0xff, 0, 0, 0xff,0xff, 0xff};// 1: show, 0: hidden
    Pixmap shapeid = XCreateBitmapFromData(display, child, (char *)shape, 8, 8);
    Pixmap maskid  = XCreateBitmapFromData(display, child, (char *)mask, 8, 8);
    Cursor cursorID = XCreatePixmapCursor(display, shapeid, maskid, &green, &black, 0, 0);
    XDefineCursor(display, child, cursorID);
    XFreePixmap(display, shapeid);
    XFreePixmap(display, maskid);
    XFreeCursor(display, cursorID);
    XSelectInput(display, child, ExposureMask | ButtonPressMask);
    XMapWindow(display, child);
    XFlush(display);
    fgSetColor(red);
    XEvent evt;
    
    printf("press mouse right button to exit.\n");
    while(true) {
        XNextEvent(display, &evt);
        if (evt.type == Expose) {
            if(evt.xexpose.count > 0) continue; // until count = 0
            setBackGround(jpeg);
            int right = width  - 1;
            int down  = height - 1;
            int cx    = width  / 2;
            int cy    = height / 2;
            drawLine(0, cy, right, cy);
            drawLine(cx, 0, cx, down);
            drawArc(0, 0, right, down, 0, 360);
            drawRect(0, 0, right, down);
            bgRestore(cx - 100, cy - 100, 200, 200);
        }        
        if (evt.type == ButtonPress)  {
            if(evt.xbutton.button == 3) {
                printf("press right\n");
                break;
            }
        }
    }
    if (bg) XDestroyImage(bg);
    XFreeGC(display, gCtx);
    XDestroyWindow(display, child);
    XCloseDisplay(display);    
}

事先準備好一個 test.jpeg, 編譯程式並執行:

g++   main.cpp  -lX11  `pkg-config  --cflags  --libs  opencv4`  && ./a.out  test.jpeg

2021年8月20日 星期五

linux 上使用 opencv 抓取螢幕影像

 不囉唆, 直接看原始碼 x1.cpp:
#include <opencv2/opencv.hpp>
#include <X11/Xutil.h>
#include <stdio.h>
using namespace std;
int main(int argc, char ** argv){
    XWindowAttributes x = { 0 };// x window 屬性(參數群)
    auto display = XOpenDisplay(nullptr);   // 打開 x window server 的顯示器
    auto root = DefaultRootWindow(display); // get default root?
    XGetWindowAttributes(display, root, &x);// 取得螢幕參數
    int height = x.height;
    int width  = x.width;
    auto buffer = XGetImage(display, root,
        0, 0, width, height,
        AllPlanes, ZPixmap
    );// 全螢幕影像

    int cvType = CV_8UC4;// default 4 bytes's A,R,G,B
    switch (buffer->bits_per_pixel) {
        case 24: cvType = CV_8UC3; break;// 3 bytes
        case 16: cvType = CV_8UC2; break;// 2 bytes
        case  8: cvType = CV_8UC1; break;// 1 bytes
    }
    const char *name = "ScreenCapture";
    cv::namedWindow(name, cv::WINDOW_AUTOSIZE);
    cv::Mat m(height, width, cvType, buffer->data);  // 暫時包成 cv::Mat
    cv::imshow(name, m); // 顯示全螢幕影像

    cv::resize(m, m, cv::Size(), 0.25, 0.25, cv::INTER_LINEAR);// 長寬各縮 1/4
    XDestroyImage(buffer) ;// 釋放 buffer
    XCloseDisplay(display);// 關閉 x window session

    cv::imshow("1/16 Screen Shot", m); // 顯示 1/16 螢幕影像
    cv::waitKey(0);
}
用 g++ 編譯並執行程式:
     g++  x1.cpp  -lX11 `pkg-config  --cflags  --libs  opencv4`  &&  ./a.out

2021年8月12日 星期四

linux 上是使用 kolinc-jvm 編譯 kotlin 程式, 程式透過 jni 呼叫原生 c 函式

 1. 上官網 https://github.com/JetBrains/kotlin/releases/download/v1.5.21/kotlin-compiler-1.5.21.zip下載 kotlin 編譯器, 開啟終端機進入下載目錄, 將它解壓縮到目錄 kotlinc 內

       cd  Downloads  &&  unzip  kotlin-compiler-1.5.21.zip

2.  安裝 openjdk 11, 將程式安裝到 /usr/lib/jvm/java-11-openjdk-amd64

       sudo  apt-get  install  openjdk-11-jdk

3. 建一個專案 project 目錄(mkdir project), 在裏面編寫一個 Makefile 方便編譯及跑程式:
buildDir  = build
main_jar  = $(buildDir)/main.jar
jniLib_jar= $(buildDir)/jniLib.jar
libjni_so = $(buildDir)/libnative.so

jdk11     = /usr/lib/jvm/java-11-openjdk-amd64/include
openjdk   = -I$(jdk11)  -I$(jdk11)/linux
opencv2   = `pkg-config  --cflags  --libs  opencv4`

kotlinDir =  /home/mint/Downloads/kotlinc
ktc       = $(kotlinDir)/bin/kotlinc  -include-runtime
coroutine = -cp $(kotlinDir)/lib/kotlinx-coroutines-core.jar

javaLib   = $(coroutine):$(jniLib_jar)
cppLib    = -shared  -fPIC  $(openjdk)  $(opencv2)

run: main  native
    java  -Djava.library.path=$(buildDir)  $(javaLib):$(main_jar)  MainKt

main:  $(buildDir)  $(main_jar)

native: $(libjni_so)

$(main_jar): main.kt  $(jniLib_jar)
    $(ktc) -d  $@  $<  $(javaLib)

$(jniLib_jar): jniLib.kt
    $(ktc) -d  $@  $<  $(coroutine)

$(libjni_so): cpp/native.cpp
    g++  -o  $@  $<  $(cppLib)

$(buildDir):
    [ -d $@ ] || mkdir $@ && echo $@ exist already

clean:
    [ -d $(buildDir) ] || echo $(buildDir) not exist && rm -rf $(buildDir)

4. 在專案目錄內寫一個 kotlin 主程式碼 main.kt:

import  kotlinx.coroutines.*
import  jniLib.*
fun  main( ) {
  runBlocking{
     GlobalScope.launch(Dispatchers.IO) { // split nonblocking coroutine
          println("coroutine hello1")
     }
     NativeClass( ).hello( ) // call  jni  c  routine
  }
}

5.  在專案目錄內寫一個 kotlin 介面程式庫 jniLib.kt:

package jniLib
class NativeClass {  
  external fun hello( ) // external  function written in c++ language
  init {
      System.loadLibrary("native")
  }
}

6. 在專案目錄內再建個 cpp 子目錄(mkdir  cpp), 在 cpp 目錄內編寫一個用 c 寫的 jni  程式碼 cpp/native.cpp:

#include <stdio.h>
#include <jni.h>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
const char *window = "mouseEvent";
void mouseEvent(int event, int x, int y, int flags, void *args){
    static cv::Scalar green(0,   255,   0);
    static cv::Scalar red(0,   0,   255);
    cv::Mat &image = *(cv::Mat *)args;
    switch(event) {
        case cv::EVENT_LBUTTONDOWN: // left mouse button click
                printf("left   click row: %d, col: %d\n", x, y);
                cv::circle(image, cv::Point(x, y), 2, red);
                cv::imshow(window, image);
                break;
        case cv::EVENT_RBUTTONDOWN:// right mouse button click
                printf("right  click row: %d, col: %d\n", x, y);
                cv::line(image, cv::Point(0, 0), cv::Point(x, y), green, 2, cv::FILLED);
                cv::imshow(window, image);
                break;
        case cv::EVENT_MBUTTONDOWN:// middle mouse button click
                printf("middle click row: %d, col: %d\n", x, y);
                break;
        case cv::EVENT_MOUSEMOVE:// mouse movment
                // printf("mouse move  row: %d, col: %d\n", x, y);
                break;
    }
}
extern "C" {
    JNIEXPORT void JNICALL Java_jniLib_NativeClass_hello(JNIEnv *env, jobject obj) {        
        cv::Mat fg(800, 800, CV_8UC3);// w x h
        cv::namedWindow(window, cv::WINDOW_NORMAL);
        cv::setMouseCallback(window, mouseEvent, &fg);
        cv::imshow(window, fg);
        cv::waitKey(0);//press any key to exit
    }
}

上述步驟 3,4,5,6 存檔後, 在專案目錄內開啟終端機執行 make run 跑看看, 不用 Gradle, 速度快多了

後記: 使用 cmake 語法, 編譯 c++ 動態程式庫 libnative.so:

1. 先在專案目錄內, 編輯一個 CMakeLists.txt:
cmake_minimum_required(VERSION 3.10.2)
project(native)
include_directories(
    /usr/lib/jvm/java-11-openjdk-amd64/include
    /usr/lib/jvm/java-11-openjdk-amd64/include/linux
    /usr/include/opencv4
)
set(opencvLib   -lopencv_core   -lopencv_highgui   -lopencv_imgproc)
add_library(native   SHARED   cpp/native.cpp)
target_link_libraries(native   PUBLIC   ${opencvLib})

2. 建一個子目錄 build, 在裏面執行 cmake .. 產生 Makefile, 接著 make 就能生成 libnative.so:
    [ -d build ] || mkdir   build
    cd   build   &&   cmake  ..  &&  make

2021年8月1日 星期日

關於 UTF8 與 Unicode

 UTF8 編碼是 1 ~ 4 bytes 字元編碼, Unicode 則是固定 4 bytes 字元編碼:

#include "stdio.h"
typedef unsigned char u8int;
typedef const char*   c_str;

void toHex(u8int *utf8) {
        if (utf8) {
            int wc = 0;// wide char unicode
            while (*utf8 != 0) {
                if ((*utf8 & 0xc0) == 0x80) { // 10 xx xxxx, bits accumulate
                    wc <<= 6;// self-shift 6 bits to left
                    wc |= *utf8 & 0x3f;// accumulate 6 bits
                } else {// ready to decode
                    if (wc > 0x80) printf("%5x, ", wc); // previous one

                    if (*utf8 < 0x80) { // 0x xx xxxx, 1 bytes utf8
                        printf("%2x, ", *utf8);// decode immediatelly
                        wc = 0; // reset to 0 to prevent overflow
                    } // decode later
                    else if (*utf8 < 0xe0) wc = *utf8 & 0x1f;// 2 bytes utf8, 5 bits begin +  6 bits later = 11 bits total                       
                    else if (*utf8 < 0xf0) wc = *utf8 &  0xf;// 3 bytes utf8, 4 bits begin + 12 bits later = 16 bits total                       
                    else                   wc = *utf8 &  0x7;// 4 bytes utf8, 3 bits begin + 18 bits later = 21 bits total                       
                }
                utf8 ++;
            }
            if (wc > 0x80) printf("%5x", wc);
        }
}

void putwc(int wc){ // putchar for wide char unicode
    if(wc < 0x80)       printf("%c", wc); // 7  bits: 0xxxxxxx
    else if(wc < 0x800) printf("%c%c",
            0xc0 | (wc >> 7),
            0x80 | (wc & 0x3f)
        ); // 11 bits: 110xxxxx 10xxxxxx , 5 + 6 bits
    else if(wc < 0x10000) printf("%c%c%c",
            0xe0 |  (wc >> 12),
            0x80 | ((wc >>  6) & 0x3f),
            0x80 |  (wc        & 0x3f)
        );// 16 bits: 1110xxxx 10xxxxxx 10xxxxxx, 4 + 6 + 6 bits
    else printf("%c%c%c%c",
            0xf0 | ((wc >> 18) & 7),
            0x80 | ((wc >> 12) & 0x3f),
            0x80 | ((wc >>  6) & 0x3f),
            0x80 |  (wc        & 0x3f)
        );// 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx, 3 + 6 + 6 + 6 bits
}

void toHex(c_str utf8) { return toHex((u8int *)utf8); }
int main(){
    c_str src = "Taiwan,台灣,🀄,0123456789,\U0001F004";
    toHex(src);
    printf("\n%s\n", src);   
    putwc(0x1f004);
    printf("\n");
    return 0;
}

編譯並執行  g++ a.c && ./a.out

54, 61, 69, 77, 61, 6e, 2c,  53f0,  7063, 2c, 1f004, 2c, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 2c, 1f004
Taiwan,台灣,🀄,0123456789,🀄
🀄

簡單 c 程式碼, 根據五行八卦相生相剋推斷吉凶

#include "stdio.h" // 五行: //               木2 //      水1           火3 //         金0     土4 // // 先天八卦 vs 五行 //                    ...