updated Animation Plugin for NativeWindow interface
bug:5114637 Uses new ANativeWindow plugin API, supports either software rendering or GL rendering via flag in RenderingThread.h Note: Currently crashes on close Change-Id: Ia7338a6c38c0ca9db02c19814d99b29970cc7b8e
This commit is contained in:
@@ -61,6 +61,7 @@ LOCAL_C_INCLUDES += \
|
|||||||
|
|
||||||
LOCAL_SHARED_LIBRARIES := \
|
LOCAL_SHARED_LIBRARIES := \
|
||||||
libnativehelper \
|
libnativehelper \
|
||||||
|
libandroid \
|
||||||
libutils \
|
libutils \
|
||||||
libcutils \
|
libcutils \
|
||||||
libEGL \
|
libEGL \
|
||||||
|
|||||||
@@ -24,30 +24,55 @@
|
|||||||
*/
|
*/
|
||||||
#include "RenderingThread.h"
|
#include "RenderingThread.h"
|
||||||
|
|
||||||
#include "ANPOpenGL_npapi.h"
|
#include "ANPNativeWindow_npapi.h"
|
||||||
|
|
||||||
extern ANPLogInterfaceV0 gLogI;
|
extern ANPLogInterfaceV0 gLogI;
|
||||||
extern ANPOpenGLInterfaceV0 gOpenGLI;
|
extern ANPNativeWindowInterfaceV0 gNativeWindowI;
|
||||||
|
|
||||||
RenderingThread::RenderingThread(NPP npp) : android::Thread() {
|
RenderingThread::RenderingThread(NPP npp) : android::Thread() {
|
||||||
m_npp = npp;
|
m_npp = npp;
|
||||||
m_width = -1;
|
m_width = -1;
|
||||||
m_height = -1;
|
m_height = -1;
|
||||||
gLogI.log(kError_ANPLogType, "Created Rendering Thread");
|
|
||||||
|
m_ANW = NULL;
|
||||||
|
#if (!USE_SOFTWARE_RENDERING)
|
||||||
|
m_eglSurface = EGL_NO_SURFACE;
|
||||||
|
m_eglContext = EGL_NO_CONTEXT;
|
||||||
|
m_eglDisplay = EGL_NO_DISPLAY;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
android::status_t RenderingThread::readyToRun() {
|
android::status_t RenderingThread::readyToRun() {
|
||||||
|
while (m_ANW == NULL) {
|
||||||
gLogI.log(kError_ANPLogType, "in ready to run");
|
m_ANW = gNativeWindowI.acquireNativeWindow(m_npp);
|
||||||
|
|
||||||
EGLContext context = gOpenGLI.acquireContext(m_npp);
|
|
||||||
|
|
||||||
gLogI.log(kError_ANPLogType, "context: %p", context);
|
|
||||||
|
|
||||||
if (context == EGL_NO_CONTEXT) {
|
|
||||||
gLogI.log(kError_ANPLogType, "Unable to create EGLContext for a TextureProducer thread");
|
|
||||||
return android::UNKNOWN_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if (!USE_SOFTWARE_RENDERING)
|
||||||
|
m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||||
|
|
||||||
|
//initialize context
|
||||||
|
EGLint numConfigs;
|
||||||
|
static const EGLint configAttribs[] = {
|
||||||
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||||
|
EGL_RED_SIZE, 8,
|
||||||
|
EGL_GREEN_SIZE, 8,
|
||||||
|
EGL_BLUE_SIZE, 8,
|
||||||
|
EGL_ALPHA_SIZE, 8,
|
||||||
|
EGL_NONE
|
||||||
|
};
|
||||||
|
|
||||||
|
eglChooseConfig(m_eglDisplay, configAttribs, &m_eglConfig, 1, &numConfigs);
|
||||||
|
checkGlError("eglChooseConfig");
|
||||||
|
|
||||||
|
static const EGLint contextAttribs[] = {
|
||||||
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||||
|
EGL_NONE
|
||||||
|
};
|
||||||
|
|
||||||
|
m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, NULL, contextAttribs);
|
||||||
|
checkGlError("eglCreateContext");
|
||||||
|
#endif
|
||||||
|
|
||||||
return android::NO_ERROR;
|
return android::NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,34 +134,70 @@ GLenum RenderingThread::getType(SkBitmap::Config config)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingThread::createTextureWithBitmap(GLuint texture, SkBitmap& bitmap) {
|
void RenderingThread::setupNativeWindow(ANativeWindow* ANW, const SkBitmap& bitmap)
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
{
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
int result = ANativeWindow_setBuffersGeometry(ANW, bitmap.width(),
|
||||||
checkGlError("glBindTexture");
|
bitmap.height(), WINDOW_FORMAT_RGBA_8888);
|
||||||
SkBitmap::Config config = bitmap.getConfig();
|
|
||||||
int internalformat = getInternalFormat(config);
|
if (android::NO_ERROR != result) {
|
||||||
int type = getType(config);
|
gLogI.log(kError_ANPLogType, "ERROR setBuffersGeometry() status is (%d)", result);
|
||||||
bitmap.lockPixels();
|
}
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, internalformat, bitmap.width(), bitmap.height(),
|
|
||||||
0, internalformat, type, bitmap.getPixels());
|
#if (!USE_SOFTWARE_RENDERING)
|
||||||
bitmap.unlockPixels();
|
if (m_eglSurface != EGL_NO_SURFACE) {
|
||||||
checkGlError("glTexImage2D");
|
gLogI.log(kDebug_ANPLogType, "destroying old surface");
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
eglDestroySurface(m_eglDisplay, m_eglSurface);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
}
|
||||||
|
|
||||||
|
m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, ANW, NULL);
|
||||||
|
checkGlError("eglCreateWindowSurface");
|
||||||
|
|
||||||
|
eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
|
||||||
|
|
||||||
|
//optional: enable async mode
|
||||||
|
//eglSwapInterval(m_eglDisplay, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
updateNativeWindow(ANW, bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingThread::updateTextureWithBitmap(GLuint texture, SkBitmap& bitmap) {
|
void RenderingThread::updateNativeWindow(ANativeWindow* ANW,
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
const SkBitmap& bitmap)
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
{
|
||||||
checkGlError("glBindTexture");
|
#if USE_SOFTWARE_RENDERING
|
||||||
SkBitmap::Config config = bitmap.getConfig();
|
|
||||||
int internalformat = getInternalFormat(config);
|
//STEP 1: lock the ANW, getting a buffer
|
||||||
int type = getType(config);
|
ANativeWindow_Buffer buffer;
|
||||||
|
if (ANativeWindow_lock(ANW, &buffer, NULL) < 0 ) // todo: use rect parameter for efficiency
|
||||||
|
return;
|
||||||
|
|
||||||
|
//STEP 2: draw into the buffer
|
||||||
|
uint8_t* img = (uint8_t*)buffer.bits;
|
||||||
|
int row, col;
|
||||||
|
int bpp = 4; // Here we only deal with RGBA8888 format.
|
||||||
bitmap.lockPixels();
|
bitmap.lockPixels();
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(),
|
uint8_t* bitmapOrigin = static_cast<uint8_t*>(bitmap.getPixels());
|
||||||
internalformat, type, bitmap.getPixels());
|
// Copy line by line to handle offsets and stride
|
||||||
|
for (row = 0 ; row < bitmap.height(); row ++) {
|
||||||
|
uint8_t* dst = &(img[(buffer.stride * (row + 0) + 0) * bpp]);
|
||||||
|
uint8_t* src = &(bitmapOrigin[bitmap.width() * row * bpp]);
|
||||||
|
memcpy(dst, src, bpp * bitmap.width());
|
||||||
|
}
|
||||||
bitmap.unlockPixels();
|
bitmap.unlockPixels();
|
||||||
checkGlError("glTexSubImage2D");
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
//STEP 3: push the buffer to the queue
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
ANativeWindow_unlockAndPost(ANW);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
//rotate the intensity of the green channel, other channels fixed
|
||||||
|
static int i = 0;
|
||||||
|
i = (i >= 245) ? 0 : i+10;
|
||||||
|
|
||||||
|
glClearColor(0.6, (i*1.0/256), 0.6, 0.6);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
eglSwapBuffers(m_eglDisplay, m_eglSurface);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
#ifndef RenderingThread__DEFINED
|
#ifndef RenderingThread__DEFINED
|
||||||
#define RenderingThread__DEFINED
|
#define RenderingThread__DEFINED
|
||||||
|
|
||||||
|
#define USE_SOFTWARE_RENDERING false
|
||||||
|
#define MS_PER_FRAME 17 // approx 60 fps
|
||||||
|
|
||||||
class RenderingThread : public android::Thread {
|
class RenderingThread : public android::Thread {
|
||||||
public:
|
public:
|
||||||
@@ -44,13 +46,14 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
NPP m_npp;
|
NPP m_npp;
|
||||||
|
ANativeWindow* m_ANW;
|
||||||
|
|
||||||
static void printGLString(const char *name, GLenum s);
|
static void printGLString(const char *name, GLenum s);
|
||||||
static void checkGlError(const char* op);
|
static void checkGlError(const char* op);
|
||||||
static GLenum getInternalFormat(SkBitmap::Config config);
|
static GLenum getInternalFormat(SkBitmap::Config config);
|
||||||
static GLenum getType(SkBitmap::Config config);
|
static GLenum getType(SkBitmap::Config config);
|
||||||
static void createTextureWithBitmap(GLuint texture, SkBitmap& bitmap);
|
void setupNativeWindow(ANativeWindow* ANW, const SkBitmap& bitmap);
|
||||||
static void updateTextureWithBitmap(GLuint texture, SkBitmap& bitmap);
|
void updateNativeWindow(ANativeWindow* ANW, const SkBitmap& bitmap);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual bool threadLoop() = 0;
|
virtual bool threadLoop() = 0;
|
||||||
@@ -58,6 +61,13 @@ private:
|
|||||||
android::Mutex m_sync;
|
android::Mutex m_sync;
|
||||||
int m_width;
|
int m_width;
|
||||||
int m_height;
|
int m_height;
|
||||||
|
|
||||||
|
#if (!USE_SOFTWARE_RENDERING)
|
||||||
|
EGLDisplay m_eglDisplay;
|
||||||
|
EGLSurface m_eglSurface;
|
||||||
|
EGLContext m_eglContext;
|
||||||
|
EGLConfig m_eglConfig;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,14 +23,12 @@
|
|||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
#include "AnimationThread.h"
|
#include "AnimationThread.h"
|
||||||
#include "ANPOpenGL_npapi.h"
|
|
||||||
|
|
||||||
#include <EGL/egl.h>
|
|
||||||
#include <GLES2/gl2.h>
|
|
||||||
#include <utils/SystemClock.h>
|
#include <utils/SystemClock.h>
|
||||||
|
#include "ANPNativeWindow_npapi.h"
|
||||||
|
|
||||||
extern ANPLogInterfaceV0 gLogI;
|
extern ANPLogInterfaceV0 gLogI;
|
||||||
extern ANPOpenGLInterfaceV0 gOpenGLI;
|
extern ANPNativeWindowInterfaceV0 gNativeWindowI;
|
||||||
|
|
||||||
AnimationThread::AnimationThread(NPP npp) : RenderingThread(npp) {
|
AnimationThread::AnimationThread(NPP npp) : RenderingThread(npp) {
|
||||||
m_counter = 0;
|
m_counter = 0;
|
||||||
@@ -52,6 +50,8 @@ AnimationThread::AnimationThread(NPP npp) : RenderingThread(npp) {
|
|||||||
|
|
||||||
m_startExecutionTime = 0;
|
m_startExecutionTime = 0;
|
||||||
m_startTime = android::uptimeMillis();
|
m_startTime = android::uptimeMillis();
|
||||||
|
m_stallTime = android::uptimeMillis();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimationThread::~AnimationThread() {
|
AnimationThread::~AnimationThread() {
|
||||||
@@ -84,15 +84,14 @@ static void bounce(float* x, float* dx, const float max) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool AnimationThread::threadLoop() {
|
bool AnimationThread::threadLoop() {
|
||||||
|
if (android::uptimeMillis() - m_stallTime < MS_PER_FRAME)
|
||||||
m_startIdleTime = android::uptimeMillis();
|
return true;
|
||||||
|
m_stallTime = android::uptimeMillis();
|
||||||
ANPTextureInfo textureInfo = gOpenGLI.lockTexture(m_npp);
|
|
||||||
GLuint textureId = textureInfo.textureId;
|
|
||||||
|
|
||||||
m_idleTime += android::uptimeMillis() - m_startIdleTime;
|
m_idleTime += android::uptimeMillis() - m_startIdleTime;
|
||||||
m_startExecutionTime = android::uptimeMillis();
|
m_startExecutionTime = android::uptimeMillis();
|
||||||
|
|
||||||
|
bool reCreateFlag = false;
|
||||||
int width, height;
|
int width, height;
|
||||||
getDimensions(width, height);
|
getDimensions(width, height);
|
||||||
|
|
||||||
@@ -110,6 +109,7 @@ bool AnimationThread::threadLoop() {
|
|||||||
// change the ball's speed to match the size
|
// change the ball's speed to match the size
|
||||||
m_dx = width * .005f;
|
m_dx = width * .005f;
|
||||||
m_dy = height * .007f;
|
m_dy = height * .007f;
|
||||||
|
reCreateFlag = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup variables
|
// setup variables
|
||||||
@@ -131,20 +131,15 @@ bool AnimationThread::threadLoop() {
|
|||||||
m_paint->setColor(0xAAFF0000);
|
m_paint->setColor(0xAAFF0000);
|
||||||
m_canvas->drawOval(m_oval, *m_paint);
|
m_canvas->drawOval(m_oval, *m_paint);
|
||||||
|
|
||||||
if (textureInfo.width == width && textureInfo.height == height) {
|
if (!reCreateFlag) {
|
||||||
updateTextureWithBitmap(textureId, *m_bitmap);
|
updateNativeWindow(m_ANW, *m_bitmap);
|
||||||
} else {
|
} else {
|
||||||
createTextureWithBitmap(textureId, *m_bitmap);
|
setupNativeWindow(m_ANW, *m_bitmap);
|
||||||
textureInfo.width = width;
|
|
||||||
textureInfo.height = height;
|
|
||||||
textureInfo.internalFormat = GL_RGBA;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_executionTime += android::uptimeMillis() - m_startExecutionTime;
|
m_executionTime += android::uptimeMillis() - m_startExecutionTime;
|
||||||
m_counter++;
|
m_counter++;
|
||||||
|
|
||||||
gOpenGLI.releaseTexture(m_npp, &textureInfo);
|
|
||||||
|
|
||||||
if (android::uptimeMillis() - m_lastPrintTime > 5000) {
|
if (android::uptimeMillis() - m_lastPrintTime > 5000) {
|
||||||
float fps = m_counter / ((android::uptimeMillis() - m_startTime) / 1000);
|
float fps = m_counter / ((android::uptimeMillis() - m_startTime) / 1000);
|
||||||
float spf = ((android::uptimeMillis() - m_startTime)) / m_counter;
|
float spf = ((android::uptimeMillis() - m_startTime)) / m_counter;
|
||||||
@@ -160,5 +155,6 @@ bool AnimationThread::threadLoop() {
|
|||||||
m_startTime = android::uptimeMillis();
|
m_startTime = android::uptimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_startIdleTime = android::uptimeMillis(); // count delay between frames
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ private:
|
|||||||
int64_t m_startTime;
|
int64_t m_startTime;
|
||||||
int64_t m_startExecutionTime;
|
int64_t m_startExecutionTime;
|
||||||
int64_t m_startIdleTime;
|
int64_t m_startIdleTime;
|
||||||
|
int64_t m_stallTime;
|
||||||
|
|
||||||
float m_x;
|
float m_x;
|
||||||
float m_y;
|
float m_y;
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ ANPSurfaceInterfaceV0 gSurfaceI;
|
|||||||
ANPSystemInterfaceV0 gSystemI;
|
ANPSystemInterfaceV0 gSystemI;
|
||||||
ANPTypefaceInterfaceV0 gTypefaceI;
|
ANPTypefaceInterfaceV0 gTypefaceI;
|
||||||
ANPWindowInterfaceV1 gWindowI;
|
ANPWindowInterfaceV1 gWindowI;
|
||||||
ANPOpenGLInterfaceV0 gOpenGLI;
|
ANPNativeWindowInterfaceV0 gNativeWindowI;
|
||||||
|
|
||||||
#define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))
|
#define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))
|
||||||
#define DEBUG_PLUGIN_EVENTS 0
|
#define DEBUG_PLUGIN_EVENTS 0
|
||||||
@@ -115,18 +115,18 @@ NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs,
|
|||||||
uint32_t size;
|
uint32_t size;
|
||||||
ANPInterface* i;
|
ANPInterface* i;
|
||||||
} gPairs[] = {
|
} gPairs[] = {
|
||||||
{ kAudioTrackInterfaceV0_ANPGetValue, sizeof(gSoundI), &gSoundI },
|
{ kAudioTrackInterfaceV0_ANPGetValue, sizeof(gSoundI), &gSoundI },
|
||||||
{ kBitmapInterfaceV0_ANPGetValue, sizeof(gBitmapI), &gBitmapI },
|
{ kBitmapInterfaceV0_ANPGetValue, sizeof(gBitmapI), &gBitmapI },
|
||||||
{ kCanvasInterfaceV0_ANPGetValue, sizeof(gCanvasI), &gCanvasI },
|
{ kCanvasInterfaceV0_ANPGetValue, sizeof(gCanvasI), &gCanvasI },
|
||||||
{ kEventInterfaceV0_ANPGetValue, sizeof(gEventI), &gEventI },
|
{ kEventInterfaceV0_ANPGetValue, sizeof(gEventI), &gEventI },
|
||||||
{ kLogInterfaceV0_ANPGetValue, sizeof(gLogI), &gLogI },
|
{ kLogInterfaceV0_ANPGetValue, sizeof(gLogI), &gLogI },
|
||||||
{ kPaintInterfaceV0_ANPGetValue, sizeof(gPaintI), &gPaintI },
|
{ kPaintInterfaceV0_ANPGetValue, sizeof(gPaintI), &gPaintI },
|
||||||
{ kPathInterfaceV0_ANPGetValue, sizeof(gPathI), &gPathI },
|
{ kPathInterfaceV0_ANPGetValue, sizeof(gPathI), &gPathI },
|
||||||
{ kSurfaceInterfaceV0_ANPGetValue, sizeof(gSurfaceI), &gSurfaceI },
|
{ kSurfaceInterfaceV0_ANPGetValue, sizeof(gSurfaceI), &gSurfaceI },
|
||||||
{ kSystemInterfaceV0_ANPGetValue, sizeof(gSystemI), &gSystemI },
|
{ kSystemInterfaceV0_ANPGetValue, sizeof(gSystemI), &gSystemI },
|
||||||
{ kTypefaceInterfaceV0_ANPGetValue, sizeof(gTypefaceI), &gTypefaceI },
|
{ kTypefaceInterfaceV0_ANPGetValue, sizeof(gTypefaceI), &gTypefaceI },
|
||||||
{ kWindowInterfaceV1_ANPGetValue, sizeof(gWindowI), &gWindowI },
|
{ kWindowInterfaceV1_ANPGetValue, sizeof(gWindowI), &gWindowI },
|
||||||
{ kOpenGLInterfaceV0_ANPGetValue, sizeof(gOpenGLI), &gOpenGLI },
|
{ kNativeWindowInterfaceV0_ANPGetValue, sizeof(gNativeWindowI), &gNativeWindowI },
|
||||||
};
|
};
|
||||||
for (size_t i = 0; i < ARRAY_COUNT(gPairs); i++) {
|
for (size_t i = 0; i < ARRAY_COUNT(gPairs); i++) {
|
||||||
gPairs[i].i->inSize = gPairs[i].size;
|
gPairs[i].i->inSize = gPairs[i].size;
|
||||||
@@ -234,6 +234,9 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc
|
|||||||
obj->pluginType = kVideo_PluginType;
|
obj->pluginType = kVideo_PluginType;
|
||||||
obj->activePlugin = new VideoPlugin(instance);
|
obj->activePlugin = new VideoPlugin(instance);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
gLogI.log(kError_ANPLogType, "PluginType %s unknown!", argv[i]);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
#include <npfunctions.h>
|
#include <npfunctions.h>
|
||||||
#include <npruntime.h>
|
#include <npruntime.h>
|
||||||
#include "android_npapi.h"
|
#include "android_npapi.h"
|
||||||
#include "ANPOpenGL_npapi.h"
|
#include "ANPNativeWindow_npapi.h"
|
||||||
#include "ANPSurface_npapi.h"
|
#include "ANPSurface_npapi.h"
|
||||||
#include "ANPSystem_npapi.h"
|
#include "ANPSystem_npapi.h"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user