How to draw text on an EGL texture using Skia SkBitmap?

1,217 views
Skip to first unread message

Joe Droider

unread,
Jan 21, 2014, 2:15:06 PM1/21/14
to skia-d...@googlegroups.com
I am looking for a way to draw text on EGL texture using SkBitmap.

I posted this on Stackoverflow with code snippet at http://stackoverflow.com/questions/21265220/how-to-draw-text-on-an-egl-texture-using-skia-skbitmap

Thanks for your help.

Brian Salomon

unread,
Jan 22, 2014, 9:35:19 AM1/22/14
to skia-d...@googlegroups.com
Here is one way:

Make your OpenGL context current.
Make a GrContext
Use GrContext::wrapBackendTexture with the kRenderTarget_GrBackendTextureFlag option to create a GrTexture for your texture ID.
Create an SkSurface using SkSurface::NewRenderTargetDirect()
The surface will have an SkCanvas accessible via SkSurface::getCanvas(). You can draw whatever you like to the SkCanvas and then call SkCanvas::flush() when you are finished drawing.

Brian



--
You received this message because you are subscribed to the Google Groups "skia-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to skia-discuss...@googlegroups.com.
To post to this group, send email to skia-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/skia-discuss.
For more options, visit https://groups.google.com/groups/opt_out.

Joe Droider

unread,
Jan 22, 2014, 11:54:49 AM1/22/14
to skia-d...@googlegroups.com
Brian
Thanks for your response. I don't see SkSurface in the Skia code that came with Android 4.0.3.

Is there another way to accomplish this using older Skia library?

Brian Salomon

unread,
Jan 22, 2014, 12:07:36 PM1/22/14
to skia-d...@googlegroups.com
Are you writing an Android app? If so, you can build a Skia from a recent revision and ship that with the app. I'm not sure whether the revision of Skia in 4.0.3 predates GPU support.

Brian


--

Mike Reed

unread,
Jan 22, 2014, 12:12:19 PM1/22/14
to skia-d...@googlegroups.com
.. in fact you *have to* link with your own copy, as android's NDK does not expose its skia entrypoints (so you might as well take the most recent).

Joe Droider

unread,
Jan 22, 2014, 12:18:57 PM1/22/14
to skia-d...@googlegroups.com
I am not building an SDK/NDK android app. I am building a C++ app with main() and no dependency on Android framework services being there and running.

Android 4.0.3 has GPU support for Skia. I see src/gpu/SkGPUDevice etc classes in the source tree. I don't want to bring in different version of Skia in the source tree.

Thanks for fast response.


Brian Salomon

unread,
Jan 22, 2014, 12:23:52 PM1/22/14
to skia-d...@googlegroups.com
Ok. Instead of using SkSurface you'd create an SkGpuDevice using the GrTexture. Then create an SkCanvas and then install the device using SkCanvas::setDevice() and draw to the canvas, flushing when done. I'm curious, why are you using the version of Skia from Android 4.0.3?

Brian


Joe Droider

unread,
Jan 22, 2014, 12:56:31 PM1/22/14
to skia-d...@googlegroups.com
We built an embedded system using Android as the platform. I am troubleshooting UI issue and I want to make sure I am using everything that came with the platform.

The app that I am building kinda debugging tool to verify UI interaction at frame buffer level.

I looked at SkGPUDevice. It is expecting a GrContext etc.
Can you point me to some sample code that does this?

I have piece of code like below that sets up EGL context etc before drawing something. I appreciate if you can point me to examples that get me to SkGpuDevice.

    FramebufferNativeWindow *pWindow = getFBAsWindow();
    if (NULL == pWindow)
        return -1;
    // initialize opengl and egl
    const EGLint attribs[] =
    { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_DEPTH_SIZE, 0,
            EGL_NONE };
    EGLint w, h, dummy;
    EGLint numConfigs;
    EGLConfig config;
    EGLSurface surface;
    EGLContext context;

    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

    eglInitialize(display, 0, 0);
    eglChooseConfig(display, attribs, &config, 1, &numConfigs);
    surface = eglCreateWindowSurface(display, config, pWindow, NULL);
    context = eglCreateContext(display, config, NULL, NULL);
    eglQuerySurface(display, surface, EGL_WIDTH, &w);
    eglQuerySurface(display, surface, EGL_HEIGHT, &h);

    if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
        return NO_INIT;

    mDisplay = display;
    mContext = context;
    mSurface = surface;
    mWidth = w;
    mHeight = h;
    //mFlingerSurfaceControl = control;
    mFlingerSurface = pWindow;

    mAndroidAnimation = true;


Joe Droider

unread,
Jan 22, 2014, 1:17:56 PM1/22/14
to skia-d...@googlegroups.com
May be I should rephrase my question. What I am looking for is a way to draw on the surface returned by eglCreateWindowSurface() call above.

Joe Droider

unread,
Jan 23, 2014, 6:35:05 PM1/23/14
to skia-d...@googlegroups.com
Brian

I am able to create GPU based canvas and draw on it. But it is not showing up on the screen.

Below is how I am creating canvas.


SkCanvas *getGLBackedCanvas(GrContext **context)
{
    SkCanvas *result = NULL;

    GrContext *skiaContext = GrContext::CreateGLShaderContext();
    GrRenderTarget* renderTarget = skiaContext->createRenderTargetFrom3DApiState();
    result = new SkGpuCanvas(skiaContext, renderTarget);

    if (NULL != context)
        *context = skiaContext;
    return result;
}


My drawing code is below


    GrContext *context = NULL;

    // Paint screen with green color
    //glShadeModel (GL_FLAT);
    glDisable (GL_DITHER);
    glDisable (GL_SCISSOR_TEST);
    glClearColor(0, 1, 0, 1);
    glClear (GL_COLOR_BUFFER_BIT);
    eglSwapBuffers(mDisplay, mSurface);

    SkGraphics::Init();
    SkCanvas *canvas = getGLBackedCanvas(&context);

    if (NULL == canvas || NULL == context)
    {
        LOGD("Unable to create canvas/context");
        return false;
    }
    SkPaint textAttribs;

    textAttribs.setColor(0xFF0000FF);
    textAttribs.setTextSize(SkIntToScalar(24));

    const nsecs_t startTime = systemTime();
    int frame_count = 0;
    do
    {
        nsecs_t now = systemTime();
        double time = now - startTime;

        glEnable (GL_BLEND);
        canvas->drawColor(0xFF0000FF);
        canvas->drawText("Hello world", strlen("Hello world"), 400, 400,
                textAttribs);
        canvas->drawRectCoords(300, 600, 500, 800, textAttribs);
        context->flush();

        EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
        if (res == EGL_FALSE)
            break;
        frame_count++;
        if (0 == (frame_count % 150))
            LOGD("GLTest: Completed %d frames", frame_count);
        // 12fps: don't animate too fast to preserve CPU
        const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
        if (sleepTime > 0)
            usleep(sleepTime);
        break;
    } while (!exitPending());


I see the following in the logs repeated when I run this program.

ERROR    logcat: hd[0]: libEGL(1508): called unimplemented OpenGL ES API

I appreciate if you can provide some pointers.

Thanks

Brian Salomon

unread,
Jan 24, 2014, 9:31:28 AM1/24/14
to skia-d...@googlegroups.com

Some comments and questions inline.

On Thu, Jan 23, 2014 at 6:35 PM, Joe Droider <droid...@gmail.com> wrote:
Brian

I am able to create GPU based canvas and draw on it. But it is not showing up on the screen.

Below is how I am creating canvas.


SkCanvas *getGLBackedCanvas(GrContext **context)
{
    SkCanvas *result = NULL;

    GrContext *skiaContext = GrContext::CreateGLShaderContext(); 
    GrRenderTarget* renderTarget = skiaContext->createRenderTargetFrom3DApiState();

Just FYI: In more recent versions of Skia the createRenderTargetFrom3DApiState() function has been removed. Instead we require to caller to give us the FBO ID and additional information.

 
    result = new SkGpuCanvas(skiaContext, renderTarget);

    if (NULL != context)
        *context = skiaContext;
    return result;
}


My drawing code is below


    GrContext *context = NULL;

    // Paint screen with green color
    //glShadeModel (GL_FLAT);
    glDisable (GL_DITHER);
    glDisable (GL_SCISSOR_TEST);
    glClearColor(0, 1, 0, 1);
    glClear (GL_COLOR_BUFFER_BIT);
    eglSwapBuffers(mDisplay, mSurface);

This makes it to the screen?
 

    SkGraphics::Init();
    SkCanvas *canvas = getGLBackedCanvas(&context);

    if (NULL == canvas || NULL == context)
    {
        LOGD("Unable to create canvas/context");
        return false;
    }
    SkPaint textAttribs;

    textAttribs.setColor(0xFF0000FF);
    textAttribs.setTextSize(SkIntToScalar(24));

    const nsecs_t startTime = systemTime();
    int frame_count = 0;
    do
    {
        nsecs_t now = systemTime();
        double time = now - startTime;

        glEnable (GL_BLEND);

I doubt this is the problem but Skia doesn't expect you to change the GL state. If you do change it you should call skiaContext->resetContext(). This lets the GrContext know that it should make no assumptions about the current GL state.
 
        canvas->drawColor(0xFF0000FF);
        canvas->drawText("Hello world", strlen("Hello world"), 400, 400,
                textAttribs);
        canvas->drawRectCoords(300, 600, 500, 800, textAttribs);
        context->flush();

        EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);

What does the screen look like at this point?
 
        if (res == EGL_FALSE)
            break;
        frame_count++;
        if (0 == (frame_count % 150))
            LOGD("GLTest: Completed %d frames", frame_count);
        // 12fps: don't animate too fast to preserve CPU
        const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
        if (sleepTime > 0)
            usleep(sleepTime);
        break;
    } while (!exitPending());


I see the following in the logs repeated when I run this program.

ERROR    logcat: hd[0]: libEGL(1508): called unimplemented OpenGL ES API

Interesting. In a debug build of Skia do you see any additional logging. When built in debug mode Skia should call glGetError() after every GL call it makes and print if an error is found.
 

I appreciate if you can provide some pointers.

Thanks

Joe Droider

unread,
Jan 24, 2014, 1:02:13 PM1/24/14
to skia-d...@googlegroups.com
My responses below in green.


On Friday, January 24, 2014 8:31:28 AM UTC-6, Brian Salomon wrote:

Some comments and questions inline.

On Thu, Jan 23, 2014 at 6:35 PM, Joe Droider <droid...@gmail.com> wrote:
Brian

I am able to create GPU based canvas and draw on it. But it is not showing up on the screen.

Below is how I am creating canvas.


SkCanvas *getGLBackedCanvas(GrContext **context)
{
    SkCanvas *result = NULL;

    GrContext *skiaContext = GrContext::CreateGLShaderContext(); 
    GrRenderTarget* renderTarget = skiaContext->createRenderTargetFrom3DApiState();

Just FYI: In more recent versions of Skia the createRenderTargetFrom3DApiState() function has been removed. Instead we require to caller to give us the FBO ID and additional information.
I looked at the sample at https://github.com/ngocdaothanh/SkiaOpenGLESAndroid/blob/master/jni/main.cpp. The skia that Android 4.0.3 uses doesn't have those apis.

 
    result = new SkGpuCanvas(skiaContext, renderTarget);

    if (NULL != context)
        *context = skiaContext;
    return result;
}


My drawing code is below


    GrContext *context = NULL;

    // Paint screen with green color
    //glShadeModel (GL_FLAT);
    glDisable (GL_DITHER);
    glDisable (GL_SCISSOR_TEST);
    glClearColor(0, 1, 0, 1);
    glClear (GL_COLOR_BUFFER_BIT);
    eglSwapBuffers(mDisplay, mSurface);

This makes it to the screen?
It does. I see screen change to green color.
 

    SkGraphics::Init();
    SkCanvas *canvas = getGLBackedCanvas(&context);

    if (NULL == canvas || NULL == context)
    {
        LOGD("Unable to create canvas/context");
        return false;
    }
    SkPaint textAttribs;

    textAttribs.setColor(0xFF0000FF);
    textAttribs.setTextSize(SkIntToScalar(24));

    const nsecs_t startTime = systemTime();
    int frame_count = 0;
    do
    {
        nsecs_t now = systemTime();
        double time = now - startTime;

        glEnable (GL_BLEND);

I doubt this is the problem but Skia doesn't expect you to change the GL state. If you do change it you should call skiaContext->resetContext(). This lets the GrContext know that it should make no assumptions about the current GL state.
 I am going to try it next.
        canvas->drawColor(0xFF0000FF);
        canvas->drawText("Hello world", strlen("Hello world"), 400, 400,
                textAttribs);
        canvas->drawRectCoords(300, 600, 500, 800, textAttribs);
        context->flush();

        EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);

What does the screen look like at this point?
Screen is painted with black color. No text nor the rectangle was drawn.
 
        if (res == EGL_FALSE)
            break;
        frame_count++;
        if (0 == (frame_count % 150))
            LOGD("GLTest: Completed %d frames", frame_count);
        // 12fps: don't animate too fast to preserve CPU
        const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
        if (sleepTime > 0)
            usleep(sleepTime);
        break;
    } while (!exitPending());


I see the following in the logs repeated when I run this program.

ERROR    logcat: hd[0]: libEGL(1508): called unimplemented OpenGL ES API

Interesting. In a debug build of Skia do you see any additional logging. When built in debug mode Skia should call glGetError() after every GL call it makes and print if an error is found.
I didn't see any gl errors in the logas after the above line. Android build system has different way of configuring debug build options. Let me fiddle with that.

Joe Droider

unread,
Feb 6, 2014, 12:55:47 PM2/6/14
to skia-d...@googlegroups.com
I found a different way to render text using Skia (without GPU backend) and OpenGL.

My findings are here: http://stackoverflow.com/questions/21265220/how-to-draw-text-on-an-egl-texture-using-skia-skbitmap

Gabriel Durante

unread,
Nov 9, 2018, 10:10:20 AM11/9/18
to skia-discuss
I'm getting the same issue regarding canvas->drawText. The text is not rendered properly.
drawPaint and drawRect are working fine, however drawString / drawText draw nothing on the screen.

I'm using GrGLMakeNativeInterface with opengles 3.1 driver (vc5 broadcom).
Any hint ?

Thanks
BR

Hal Canary

unread,
Nov 9, 2018, 10:46:03 AM11/9/18
to skia-d...@googlegroups.com
Do you have a Typeface?  Does your typeface have glyphs for the characters you are drawing?

--
You received this message because you are subscribed to the Google Groups "skia-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to skia-discuss...@googlegroups.com.
To post to this group, send email to skia-d...@googlegroups.com.

Gabriel Durante

unread,
Nov 14, 2018, 9:52:03 AM11/14/18
to skia-discuss
auto tf = SkTypeface::MakeFromName("Arial", SkFontStyle()); was returning null.
I figured out that some modules that uses freetype were not compiled (I'm porting it to a new platform). So I compiled them.

auto tf = SkTypeface::MakeFromName("sans-serif", SkFontStyle::Bold());
Now I'm getting "Could not create FT_Face" inside SkFontHost_FreeType.cpp

Gabriel Durante

unread,
Nov 14, 2018, 1:03:27 PM11/14/18
to skia-discuss
It's working now.
SkData::MakeFromFileName(".ttf");

Adapted libfreetype to use android-like config (FT_CONFIG_MODULES_H and FT_CONFIG_OPTIONS_H).

Thanks Hal for pointing about the typeface hint.
BR
Reply all
Reply to author
Forward
0 new messages