NPRuntime graphical plug-in supports two models for drawing:
Windowed plug-ins have their own native window (native to the underlying toolkit / OS), and perform all drawing and event handling for that window without involving the browser.
Windowless plug-ins draw directly to the browser's window, or to an off-screen bitmap. A windowless plug-in can be opaque or transparent and draws itself only in response to a paint message from the browser.
KreaTV's platform-independent npruntime graphical plug-in support is based on windowless plugins, as it doesn't have a general conceptual windowing system. Full details of windowless plugins can be found at:
Gecko_Plugin_API_Reference:Drawing_and_Event_Handling
Note: There are a few variations in the Windowless API depending on the platform. The kreatv implementation most closely resembles the Unix/X11 case described in the Mozilla documentation; in particular, the destination "drawable" is passed in the paint event, not in the NPP_SetWindow
call.
To specify that a plug-in is windowless, use NPN_SetValue
with NPPVpluginWindowBool
as the value of variable and false as the value of value. The plug-in will make this call from its NPP_New
method.
NPError NPP_New(NPMIMEType pluginType,
NPP instance,
uint16 mode,
int16 argc,
char* argn[],
char* argv[],
NPSavedData* saved)
{
...
TPlugin * pPlugin = new TPlugin(instance);
...
instance->pdata = (void *)pPlugin;
...
}
TPlugin::TPlugin(NPP pNPInstance):
...
{
...
//Specify that we're a windowless plug-in.
NPN_SetValue(pNPInstance, NPPVpluginWindowBool, false);
...
}
NPError NPN_SetValue(NPP instance, NPPVariable variable, void *value);
Below is the related data type.
namespace ekioh {
class EKPluginPixMap;
};
typedef struct _YourPluginInstance
{
...
ekioh::EKPluginPixMap* pixmap;
uint32_t x, y;
uint32_t width, height;
...
} YourPluginInstance;
class TPlugin
{
private:
...
YourPluginInstance pluginInstance;
...
public:
TPlugin(NPP pNPInstance);
~TPlugin();
...
NPError SetWindow(NPWindow* pNPWindow);
int16 HandleEvent(NPEvent * event);
...
};
Browser indicates we're using a "drawable" instead of a window in NPP_SetWindow()
, and defines the plug-in bounds on the drawable. The actual drawable is passed in the paint event.
NPError NPP_SetWindow (NPP instance, NPWindow* pNPWindow)
{
...
TPlugin * pPlugin = (TPlugin *)instance->pdata;
...
//Please call this when a window is created, moved, sized, or destroyed.
pPlugin->SetWindow(pNPWindow);
...
}
//Save the window information.
NPError TPlugin::SetWindow(NPWindow* pNPWindow)
{
...
pluginInstance.x = pNPWindow->x;
pluginInstance.y = pNPWindow->y;
pluginInstance.width = pNPWindow->width;
pluginInstance.height = pNPWindow->height;
...
return TRUE;
}
The drawable is a simple structure describing a pixmap:
enum EKPixelFormat {
EKPixelFormat_ARGB32
};
struct EKPluginPixMap {
uint32_t width; ///< width in pixels
uint32_t height; ///< height in pixels
uint32_t stride; ///< stride in bytes
uint32_t bitsPerPixel; ///< bits per pixel
EKPixelFormat pixelFormat; ///< format of pixel data
uint8_t* buf; ///< pointer to pixel data
};
You need to decide what you want to draw on the screen and convert it in the format of EKPixelFormat_ARGB32. Below is a very simple sample.
//sample pixmap: a square area
const uint32_t pixmapData[5 x 5] = { 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, //a black line.
0xFF0000FF, 0xFF0000FF, 0xFF0000FF, 0xFF0000FF, 0xFF0000FF, //a blue line.
0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, 0xFF00FF00, //a green line.
0xFFFF0000, 0xFFFF0000, 0xFFFF0000, 0xFFFF0000, 0xFFFF0000, //a red line.
0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, //a black line.
};
const int width=5, height = 5;
pluginInstance.pixmap = new ekioh::EKPluginPixMap;
pluginInstance.pixmap->width = width;
pluginInstance.pixmap->height = height;
pluginInstance.pixmap->bitsPerPixel = 32;
pluginInstance.pixmap->stride = sizeof(uint32_t) * width;
pluginInstance.pixmap->buffer = (uint8_t *) pixmapData;
Plug-in uses NPN_InvalidateRect()
to invalidate an area prior to repainting or refreshing (asynchronous redraw). Then NPP_HandleEvent
method will pass an update event or a paint message to the Plug-in.
Plug-in uses NPN_ForceRedraw()
to force immediate update of any previously invalidated areas (synchronous redraw).
Note: A plug-in must not draw into its drawable unless it receives a paint message.
On all platforms, platform-specific events(paint, input and so on) are passed to windowless plugins through the NPP_HandleEvent
method. The plug-in must return true from NPP_HandleEvent
if it has handled the event and false if it has not.
int16 NPP_HandleEvent(NPP instance, NPEvent *event);
{
...
TPlugin * pPlugin = (TPlugin *)instance->pdata;
if (pPlugin) {
pPlugin->HandleEvent(event);
}
...
}
int16 TPlugin::HandleEvent(NPEvent *event)
{
...
ekioh::EKPluginEvent* ekEvent = static_cast(event);
switch (ekEvent->type) {
case ekioh::EKPluginEvent::Paint:
paintPixmap(&pluginInstance, *ekEvent)//Here draw the pixmap in window.
break;
}
...
}
Events are also a simple structure. The only event currently supported is a paint event, that instructs the plug-in to redraw an area.
struct EKPluginEvent {
{
Paint
};
int version; ///< version of this structure
EventType type;
union {
struct {
// The plug-in must only draw to the area of this pixmap defined
// by the NPWindow structure in the NPP_SetWindow call.
EKPluginPixMap pixmap;
// Dirty rectangle relative to top-left of pixmap.
int x0;
int y0;
int x1;
int y1;
} paint;
};
};
This structure may change in the future to support additional events or hardware-accelerated pixmaps. The version field (currently 0) will be used to indicate such changes.
The drawable passed to the plug-in is essentially the browser's offscreen buffer. The coordinates passed to the plug-in in NPP_SetWindow
and paint events are relative to the origin of the browser's offscreen buffer, not the origin of the plug-in, so the plug-in needs to take care to draw in the correct area.
The kreatv plug-in drawable and events are defined in a new EKPluginEvent.h
header file.
Below shows how to draw the previous sample pixmap(a square).
//Paint the sample square pixmap in window.
static void paintPixmap(PluginInstance *This, ekioh::EKPluginEvent& event)
{
float scaleX = float(This->pixmap->width) / This->width;
float scaleY = float(This->pixmap->height) / This->height;
const uint32_t* src = (uint32_t *) This->pixmap->buffer;
uint8_t* dst = event.paint.pixmap.buffer +
(event.paint.pixmap.stride * event.paint.y0) +
((event.paint.pixmap.bitsPerPixel / 8) * event.paint.x0);
for (int y = event.paint.y0; y < event.paint.y1; y++) {
uint32_t* d = (uint32_t *) dst;
int srcY = (y - This->y) * scaleY;
for (int x = event.paint.x0; x < event.paint.x1; x++) {
int srcX = (x - This->x) * scaleX;
*d++ = *(src + ((This->pixmap->stride / 4) * srcY) + srcX);
}
dst += event.paint.pixmap.stride;
}
}