Code render text by SKia - phunm211/skia-pack GitHub Wiki

#include <jni.h>
#include <android/native_window_jni.h>
#include <android/log.h>
#include <EGL/egl.h>
#include <GLES3/gl32.h>

#include "include/ports/SkFontMgr_directory.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkTypeface.h"
#include "include/core/SkSurface.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkPaint.h"
#include "include/gpu/ganesh/GrDirectContext.h"
#include "include/gpu/ganesh/gl/GrGLInterface.h"
#include "include/gpu/ganesh/gl/GrGLTypes.h"
#include "include/gpu/ganesh/GrBackendSurface.h"
#include "gpu/ganesh/gl/GrGLDirectContext.h"
#include "gpu/ganesh/gl/GrGLBackendSurface.h"
#include "gpu/ganesh/SkSurfaceGanesh.h"
#include "SkFont.h"
#include "gpu/ganesh/gl/GrGLAssembleInterface.h"

#define LOG_TAG "NativeRenderer"
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
static ANativeWindow* nativeWindow = nullptr;
static EGLDisplay eglDisplay = EGL_NO_DISPLAY;
static EGLSurface eglSurface = EGL_NO_SURFACE;
static EGLContext eglContext = EGL_NO_CONTEXT;

sk_sp<GrDirectContext> grContext;
sk_sp<SkSurface> skSurface;

extern "C" JNIEXPORT void JNICALL
Java_com_example_helloskia_MainActivity_nativeInit(JNIEnv* env, jobject, jobject surface) {
    // 1. Get Native Window
    nativeWindow = ANativeWindow_fromSurface(env, surface);
    if (!nativeWindow) {
        LOGE("nativeInit: ANativeWindow_fromSurface failed. jsurface: %p", surface);
        return;
    }
    LOGI("nativeInit: ANativeWindow_fromSurface successful (nativeWindow: %p)", nativeWindow);

    // 2. Get EGL Display
    eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if (eglDisplay == EGL_NO_DISPLAY) {
        LOGE("nativeInit: eglGetDisplay failed.");
        ANativeWindow_release(nativeWindow); // Dọn dẹp window đã lấy
        nativeWindow = nullptr;
        return;
    }
    LOGI("nativeInit: eglGetDisplay successful (eglDisplay: %p)", eglDisplay);

    // 3. Initialize EGL
    EGLint majorVersion, minorVersion;
    if (!eglInitialize(eglDisplay, &majorVersion, &minorVersion)) {
        LOGE("nativeInit: eglInitialize failed.");
        eglDisplay = EGL_NO_DISPLAY; // Đánh dấu là không có display hợp lệ
        ANativeWindow_release(nativeWindow);
        nativeWindow = nullptr;
        return;
    }
    LOGI("nativeInit: EGL Initialized. Version: %d.%d", majorVersion, minorVersion);

    EGLConfig eglConfig;
    EGLint numConfigs;
    EGLint configAttribs[] = {
            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
            EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
            EGL_DEPTH_SIZE, 16,
            EGL_NONE
    };

    EGLint esVersionToTry = 3; // Thử ES3 trước
    if (!eglChooseConfig(eglDisplay, configAttribs, &eglConfig, 1, &numConfigs) || numConfigs < 1) {
        LOGI("nativeInit: eglChooseConfig for ES%d failed or no configs found (numConfigs: %d). Error: 0x%x. Trying ES2...", esVersionToTry, numConfigs, eglGetError());
        esVersionToTry = 2;
        configAttribs[1] = EGL_OPENGL_ES2_BIT; // Chuyển sang ES2
        LOGI("nativeInit: Attempting to choose EGLConfig for OpenGL ES %d", esVersionToTry);
        if (!eglChooseConfig(eglDisplay, configAttribs, &eglConfig, 1, &numConfigs) || numConfigs < 1) {
            LOGE("nativeInit: eglChooseConfig for ES%d also failed or no configs found (numConfigs: %d).", esVersionToTry, numConfigs);
            return;
        }
    }
    LOGI("nativeInit: eglChooseConfig successful for ES%d (numConfigs: %d, selectedConfig: %p)", esVersionToTry, numConfigs, eglConfig);
    EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE };
    eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttribs);
    if (eglContext == EGL_NO_CONTEXT) {
        LOGE("nativeInit: eglCreateContext failed for ES version %d.", esVersionToTry);
        return;
    }

    eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, nativeWindow, nullptr);
    if (eglSurface == EGL_NO_SURFACE) {
        LOGE("nativeInit: eglCreateWindowSurface failed!");
        // Gọi eglGetError() NGAY LẬP TỨC để lấy mã lỗi chính xác
        EGLint error = eglGetError();
        LOGE("nativeInit: eglCreateWindowSurface error code: 0x%x", error);

        // Phân tích một số mã lỗi phổ biến:
        switch (error) {
            case EGL_BAD_MATCH:
                LOGE("EGL_BAD_MATCH: Check if native_window or native_display is valid, "
                     "or if an EGLConfig does not support rendering to a window, "
                     "or if the EGLConfig does not support the EGL_SURFACE_TYPE attribute EGL_WINDOW_BIT.");
                break;
            case EGL_BAD_CONFIG:
                LOGE("EGL_BAD_CONFIG: egl_config is not a valid EGLConfig.");
                break;
            case EGL_BAD_NATIVE_WINDOW:
                LOGE("EGL_BAD_NATIVE_WINDOW: native_window is not a valid window.");
                break;
            case EGL_BAD_ALLOC:
                LOGE("EGL_BAD_ALLOC: Allocation failed (out of memory or other resources).");
                break;
            case EGL_BAD_ATTRIBUTE:
                LOGE("EGL_BAD_ATTRIBUTE: One or more attributes in attrib_list is invalid or "
                     "inconsistent (e.g., an attribute repeated).");
                break;
            default:
                LOGE("EGL_UNKNOWN_ERROR: An unknown error occurred.");
        }

        // Dọn dẹp EGLContext đã được tạo trước đó
        if (eglContext != EGL_NO_CONTEXT) {
            eglDestroyContext(eglDisplay, eglContext);
            eglContext = EGL_NO_CONTEXT;
        }
        // Dọn dẹp EGLDisplay
        if (eglDisplay != EGL_NO_DISPLAY) {
            eglTerminate(eglDisplay);
            eglDisplay = EGL_NO_DISPLAY;
        }
        // Dọn dẹp NativeWindow
        if (nativeWindow) {
            ANativeWindow_release(nativeWindow);
            nativeWindow = nullptr;
        }
        return; // Thoát khỏi hàm init vì đã thất bại
    }
    LOGI("nativeInit: eglCreateWindowSurface successful (eglSurface: %p)", eglSurface);

    // 6. Make EGLContext Current
    LOGI("nativeInit: Making EGL context current...");
    if (!eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
        LOGE("nativeInit: eglMakeCurrent failed.");
        return;
    }
    LOGI("nativeInit: eglMakeCurrent successful.");

    auto ptr = eglGetProcAddress("glGetString");
    if (!ptr) {
        LOGE("eglGetProcAddress(glGetString) returned nullptr!");
    } else {
        LOGI("eglGetProcAddress(glGetString) OK: %p", ptr);
    }



    // 7. Initialize Skia GrContext
    LOGI("nativeInit: Creating GrGLInterface...");
    auto interface = GrGLMakeAssembledInterface(
            nullptr,
            [](void*, const char name[]) -> GrGLFuncPtr {
                auto ptr = eglGetProcAddress(name);
                if (!ptr) {
                    LOGE("eglGetProcAddress failed for %s", name);
                }
                return reinterpret_cast<GrGLFuncPtr>(ptr);
            });
    if (!interface) {
        LOGE("nativeInit: GrGLMakeNativeInterface failed to create an interface.");
        return;
    }
    LOGI("nativeInit: GrGLInterface created successfully.");

    LOGI("nativeInit: Creating GrDirectContext...");
    grContext = GrDirectContexts::MakeGL(interface);
    if (!grContext) {
        LOGE("nativeInit: GrDirectContexts::MakeGL failed to create a context.");
        // Interface được GrDirectContext sở hữu, không cần giải phóng riêng nếu MakeGL thành công.
        // Nếu MakeGL thất bại, interface sẽ tự động được giải phóng (do là sk_sp).
        return;
    }
    LOGI("nativeInit: GrDirectContext created successfully (grContext: %p)", grContext.get());

    int width = ANativeWindow_getWidth(nativeWindow);
    int height = ANativeWindow_getHeight(nativeWindow);

    GrGLFramebufferInfo fbInfo;
    fbInfo.fFBOID = 0;
    fbInfo.fFormat = GL_RGBA8;

    int sampleCount = 0;     // không dùng MSAA
    int stencilBits = 8;     // stencil buffer 8 bits

    auto backendRT = GrBackendRenderTargets::MakeGL(width, height, sampleCount, stencilBits, fbInfo);

    SkColorType colorType = kRGBA_8888_SkColorType;
    skSurface = SkSurfaces::WrapBackendRenderTarget(
            grContext.get(), backendRT,
            kBottomLeft_GrSurfaceOrigin,
            colorType,
            SkColorSpace::MakeSRGB(),
            nullptr);
}

extern "C" JNIEXPORT void JNICALL
Java_com_example_helloskia_MainActivity_nativeRender(JNIEnv*, jobject) {
    if (!skSurface) return;
    SkCanvas* canvas = skSurface->getCanvas();

    canvas->clear(SK_ColorWHITE);

    SkPaint paint;
    paint.setColor(SK_ColorGREEN);
    canvas->drawCircle(200, 200, 100, paint);

    paint.setAntiAlias(true);
    paint.setColor(SK_ColorBLUE);

    auto data = SkData::MakeFromFileName("/system/fonts/Roboto-Regular.ttf");
    if (!data) {
        LOGE("Failed to load font file");
        return;
    }


    sk_sp<SkFontMgr> fontMgr = SkFontMgr_New_Custom_Directory("/system/fonts");
    sk_sp<SkTypeface> typeface = fontMgr->matchFamilyStyle("Roboto", SkFontStyle());

    SkFont font(typeface, 50);

    canvas->drawString("Hello anh An béo by Skia", 100, 400, font, paint);

    grContext->flushAndSubmit();
    eglSwapBuffers(eglDisplay, eglSurface);
}

extern "C" JNIEXPORT void JNICALL
Java_com_example_helloskia_MainActivity_nativeDestroy(JNIEnv*, jobject) {
    skSurface.reset();
    grContext.reset();

    if (eglDisplay != EGL_NO_DISPLAY) {
        eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        if (eglContext != EGL_NO_CONTEXT) {
            eglDestroyContext(eglDisplay, eglContext);
        }
        if (eglSurface != EGL_NO_SURFACE) {
            eglDestroySurface(eglDisplay, eglSurface);
        }
        eglTerminate(eglDisplay);
    }

    if (nativeWindow) {
        ANativeWindow_release(nativeWindow);
        nativeWindow = nullptr;
    }

    eglDisplay = EGL_NO_DISPLAY;
    eglContext = EGL_NO_CONTEXT;
    eglSurface = EGL_NO_SURFACE;
}
⚠️ **GitHub.com Fallback** ⚠️