Because I was brand new to skia it took me several days of experimenting to get drawing working to an existing opengl context.
This seemed harder to get working than I expected -- mostly due to some missing documentation, lack of a very simple example, and my lack of experience with the concepts behind
SkGpuDevice, GrContext, GrGLInterface, GrRenderTarget, SkCanvas, etc.
There were also a few context settings that Skia needed help with before things started working.
I decided I should post this to help make it easier for anyone else trying this the first time.
Here's what I did. If there is an easier way, please let me know....
// First time creation:
SkGpuDevice *curGpuDevice = NULL;
GrContext *curContext = NULL;
GrRenderTarget *curRenderTarget = NULL;
SkCanvas *curCanvas = NULL;
// Set up your OpenGL context which includes a stencil buffer
// this can be default back-buffer 0 or an FBO with its own stencil buffer attached (see end for example)
...
// Save GL state before calling skia stuff
glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
// Using default backbuffer to draw to. (Could be an FBO id)
GLuint bufferID = 0;
// Create a GrGl native interface
SkAutoTUnref<const GrGLInterface> glInterface;
glInterface.reset(GrGLCreateNativeInterface());
// Create the OpenGL backend GrContext
curContext = GrContext::Create(kOpenGL_GrBackend, (GrBackendContext) glInterface.get()));
// Define render target description to point to our buffer
GrBackendRenderTargetDesc desc;
desc.fWidth = SkScalarRoundToInt(myWidth);
desc.fHeight = SkScalarRoundToInt(myHeight);
desc.fConfig = kSkia8888_GrPixelConfig;
desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
desc.fSampleCnt = 0;
desc.fStencilBits = 8;
desc.fRenderTargetHandle = bufferID;
// Create the render target
SkSurfaceProps surfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
GrRenderTarget *curRenderTarget = curContext->wrapBackendRenderTarget(desc));
// Create GPU device with rendertarget and use to create the canvas
curGpuDevice = SkGpuDevice::Create(curRenderTarget, surfaceProps));
curCanvas = new SkCanvas(curGpuDevice);
curContext->resetContext();
// Reset client state
glPopClientAttrib();
// Every time you draw:
// Bind to the buffer
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bufferID);
// Do your normal opengl drawing
...
// Save the client state before skia drawing
glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
// Mark the skia context dirty now because we drew
curContext->resetContext();
// Skia doesn't seem to set this for itself. Needed if this is not the mode you left it in
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// Do some skia drawing for example...
// If drawing to FBO, clear it -- don't if drawing directly into context
// curCanvas->drawColor(SK_ColorTRANSPARENT, SkXfermode::kSrc_Mode);
// better solution for antialiasing would be to blit the backbuffer into FBO
SkPaint paint;
SkColor col = SkColorSetARGB(0x99, 0 ,0xFF,0);
paint.setColor(col);
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kFill_Style);
curCanvas->drawCircle(432, 332, 64, paint);
// flush skia canvas to execute the Skia drawing command
curCanvas->flush();
// Restore gl client attributes after skia drawing
glPopClientAttrib();
// Important -- Turn off any program left from Skia before you draw your stuff again
glUseProgram(0);
// Bind back to main buffer if we are using FBO
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
DoSomethingWithYourFBO(); // or swapbuffers if using back-buffer
In case of using FBO I created it this way. Antialiasing may be better if you draw directly to your backbuffer depending on what you
are doing with your FBO...
glGenTextures(1, &mFBOTextureID);
glBindTexture(GL_TEXTURE_2D, mFBOTextureID);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); //GL_REPEAT
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); //GL_REPEAT
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //GL_LINEAR
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); //GL_LINEAR
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, mWidth, mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
// Use a single buffer for stencil and depth
glGenRenderbuffers(1, &mFboStencilId);
glBindRenderbuffer(GL_RENDERBUFFER, mFboStencilId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mWidth, mHeight);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// Generate Framebuffer and bind it
glGenFramebuffers(1, &mFBOID);
glBindFramebuffer(GL_FRAMEBUFFER, mFBOID);
// Attach texture to color
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D, mFBOTextureID, 0);
// Attach depth buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mFboStencilId);
// Attach stencil buffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mFboStencilId);
GLenum fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (fboStatus != GL_FRAMEBUFFER_COMPLETE)
fprintf(stderr, "FBO Error in skia buffer!\n");
// Unbind
glBindFramebufferEXT(GL_FRAMEBUFFER, 0);