NPAPI:CoreGraphicsDrawing: Difference between revisions

(Clarify that this spec assumes the Carbon model, and explain the precedence, since it's a common point of confusion)
 
(25 intermediate revisions by 4 users not shown)
Line 1: Line 1:
== NOT READY ==
= Status =


This page is not ready for viewing yet - it is very much a work in progress as I attempt to format the proposal.
Accepted, ready for implementation.
 
== Contributors ==
 
* Last modified: April 22, 2010
* Authors: Tim Omernick (Apple)
* Contributors: Josh Aas (Mozilla Corporation)


== Overview ==
== Overview ==


Part of what made Apple's transition from Mac OS 9 to Mac OS X so smooth was that we provided backward-compatible APIs, like QuickDraw, so developers did not need to rewrite their applications for the new platform. Unfortunately, this plan worked a little bit too well -- some apps, including Netscape plugins, were never updated to use the modern APIs!
The Core Graphics drawing model is an alternative drawing model for 32-bit Mac OS X plugins and the default drawing model for 64-bit Mac OS X plugins.


The Netscape Plugin API is showing its age.  It assumes that browsers and plugins use QuickDraw, and that QuickDraw is the only way to draw.  On Mac OS X, however, QuickDraw is a second-class citizen. It is a pain for a modern Mac browser to host Netscape plugins, because it must create and maintain a QuickDraw port for the sole purpose of hosting legacy plugins.
<i>Note: This specification pre-dates the Cocoa specification, so it assumes the Carbon event model. Discussions of "changes" refer to differences from the QuickDraw drawing model. When using CoreGraphics with the [[NPAPI:CocoaEventModel|Cocoa event model]], changes in the Cocoa model may supersede information given here.</i>


This problem is about to get worse in two ways:
== Negotiating Core Graphics Drawing ==


1) QuickDraw is deprecated, and has been for some time. While it is sticking around for a while longer, it will probably be removed in a future version of Mac OS X.
For documentation on negotiating drawing models, see [[NPAPI:Models]]. The drawing model variables for Core Graphics are:


2) Many of Apple's frameworks and libraries are being updated for 64-bit.  QuickDraw is not invited to the 64-bit party.  There will be no 64-bit QuickDraw.
* NPDrawingModelCoreGraphics (NPDrawingModel = 1)
* NPNVsupportsCoreGraphicsBool (NPNVariable = 2001)


Our proposed solution is to change the Mac Netscape Plugin API (NPAPI) so that it is graphics-API agnostic.  That is, plugins are free to use whatever drawing API they like, so long as the API is available on the system and is supported by the browser.
== Excluding QuickDraw ==


The bottom line:  we want to make it very easy for developers to move their plugins from QuickDraw to CoreGraphics, while maintaining compatibility with QuickDraw-only browsers.
QuickDraw is default drawing model for 32-bit Mac OS X plugins. The QuickDraw drawing model does not exist for 64-bit Mac OS X. The drawing model variables for Quickdraw are:


== Excluding QuickDraw ==
* NPDrawingModelQuickDraw (NPDrawingModel = 0)
* NPNVsupportsQuickDrawBool (NPNVariable = 2000)


Plugins and browsers that are compiled 64-bit must entirely exclude QuickDraw support, since there will be no 64-bit QuickDraw.
Plugins and browsers that are compiled 64-bit must entirely exclude QuickDraw support, since there will be no 64-bit QuickDraw.
Line 26: Line 34:


  #if defined(XP_MACOSX) && defined(__LP64__)
  #if defined(XP_MACOSX) && defined(__LP64__)
#define NP_NO_QUICKDRAW
    #define NP_NO_QUICKDRAW
#endif
 
You'll see this #define used throughout this proposal.
 
== The Drawing Model ==
 
We're introducing the concept of the "drawing model" for Mac plugins.  When the plugin starts, it negotiates a drawing model with the browser.  The drawing model determines the type of graphics context created by the browser for the plugin.
 
A plugin may call NPN_GetValue() with the following NPNVariables to query the browser for its supported drawing models:
 
#ifndef NP_NO_QUICKDRAW
/* TRUE if the browser supports the QuickDraw drawing model */
NPNVsupportsQuickDrawBool = 2000
#endif
/* TRUE if the browser supports the CoreGraphics drawing model */
NPNVsupportsCoreGraphicsBool = 2001
 
Once the plugin finds a supported drawing model, it calls NPN_SetValue() to tell the browser which drawing model it will use.  We're adding a new NPNVariable for this:
 
NPNVpluginDrawingModel = 1000 /* The NPDrawingModel specified by the plugin */
 
The value for the NPNVpluginDrawingModel is an NPDrawingModel, a new enumeration we're adding:
 
#ifdef XP_MACOSX
/* The drawing model for a Mac OS X plugin. These are the possible values
  * for the NPNVpluginDrawingModel variable.
  */
typedef enum {
#ifndef NP_NO_QUICKDRAW
  NPDrawingModelQuickDraw = 0,
#endif
  NPDrawingModelCoreGraphics = 1
} NPDrawingModel;
  #endif
  #endif


If the browser does not support any of the plugin's drawing models, then the plugin should return an error from NPP_New() so that it is not started.
== The Core Graphics Drawing Model ==
 
Here is an example of a CoreGraphics-only plugin negotiating the drawing model:
 
static NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode,
                        int16 argc, char* argn[], char* argv[], NPSavedData* saved)
{
    // Check if the browser supports the CoreGraphics drawing model
    NPBool supportsCoreGraphics = FALSE;
    if (browser->getvalue(instance, NPNVsupportsCoreGraphicsBool, &supportsCoreGraphics) != NPERR_NO_ERROR || !supportsCoreGraphics)
        return NPERR_INCOMPATIBLE_VERSION_ERROR;
    // Set the drawing model
    if (browser->setvalue(instance, NPNVpluginDrawingModel, (void *)NPDrawingModelCoreGraphics) != NPERR_NO_ERROR)
        return NPERR_INCOMPATIBLE_VERSION_ERROR;
    return NPERR_NO_ERROR;
}
 
== The QuickDraw drawing model ==
 
QuickDraw is the drawing model used by all plugins today.  The QuickDraw drawing model uses all the existing Mac NPAPI data structures and conventions.  QuickDraw is the default drawing model, used when a plugin does not negotiate a drawing model.  It is also used when the plugin explicitly sets the drawing model to NPDrawingModelQuickDraw.
 
In 64-bit, the QuickDraw drawing model does not exist, so CoreGraphics is the default drawing model.
 
The CoreGraphics drawing model


If a plugin sets the drawing model to NPDrawingModelCoreGraphics, then the meanings of some of the NPAPI data structures change:
If a plugin sets the drawing model to NPDrawingModelCoreGraphics, then the meanings of some of the NPAPI data structures change:


- NPWindow's 'window' field becomes a NP_CGContext:
- NPWindow's 'window' field (under the Carbon event model) becomes a NP_CGContext:


  /* NP_CGContext is the type of the NPWindow's 'window' when the
  /* NP_CGContext is the type of the NPWindow's 'window' when the
Line 99: Line 51:
     WindowRef window;
     WindowRef window;
  } NP_CGContext;
  } NP_CGContext;
'''Note:''' the CG context here is always flipped for historical reasons.


- NPRegion becomes a CGPathRef instead of a RgnHandle:
- NPRegion becomes a CGPathRef instead of a RgnHandle:
Line 123: Line 77:
== Bridging QuickDraw and CoreGraphics ==
== Bridging QuickDraw and CoreGraphics ==


You might be asking yourself, "Great...  So now I can make a plugin that draws using CoreGraphics, but it will only run in browsers that support this NPAPI extension?!"
There is a way for plugins to draw using CoreGraphics, yet remain compatible with QuickDraw-only browsers.  The idea is to use QDBeginCGContext() and QDEndCGContext() to obtain a CGContextRef for the CGrafPtr provided by the browser.
 
Don't worry!  There is a way for plugins to draw using CoreGraphics, yet remain compatible with QuickDraw-only browsers.  The idea is to use QDBeginCGContext() and QDEndCGContext() to obtain a CGContextRef for the CGrafPtr provided by the browser. We will be shipping some sample code to demonstrate this technique, but here is a little snippet for your enjoyment:


  static CGContextRef beginQDPluginUpdate(NPWindow *window)
  static CGContextRef beginQDPluginUpdate(NPWindow *window)
  {
  {
    // window->window is an NPPort* since the browser is using QuickDraw
    // window->window is an NPPort* since the browser is using QuickDraw
    NP_Port *npPort = ((NP_Port *)window->window);  
    NP_Port *npPort = ((NP_Port *)window->window);<br>
    // Get the CGContext for the port
    // Get the CGContext for the port
    CGContextRef cgContext;
    CGContextRef cgContext;
    QDBeginCGContext(npPort->port, &cgContext);
    QDBeginCGContext(npPort->port, &cgContext);
    CGContextSaveGState(cgContext);
    CGContextSaveGState(cgContext);<br>
    // Set the CG clip path to the port's clip region -- QDBeginCGContext()
    // Set the CG clip path to the port's clip region -- QDBeginCGContext()
    // does not automatically respect the QuickDraw port's clip region.
    // does not automatically respect the QuickDraw port's clip region.
    RgnHandle clipRegion = NewRgn();
    RgnHandle clipRegion = NewRgn();
    GetPortClipRegion(npPort->port, clipRegion);
    GetPortClipRegion(npPort->port, clipRegion);
    Rect portBounds;
    Rect portBounds;
    GetPortBounds(npPort->port, &portBounds);
    GetPortBounds(npPort->port, &portBounds);
    ClipCGContextToRegion(cgContext, &portBounds, clipRegion);
    ClipCGContextToRegion(cgContext, &portBounds, clipRegion);
    DisposeRgn(clipRegion);
    DisposeRgn(clipRegion);<br>
    // Flip the CG context vertically -- its origin is at the lower left,
    // Flip the CG context vertically -- its origin is at the lower left,
    // but QuickDraw's origin is at the upper left.
    // but QuickDraw's origin is at the upper left.
    CGContextTranslateCTM(cgContext, 0.0, portBounds.bottom - portBounds.top);
    CGContextTranslateCTM(cgContext, 0.0, portBounds.bottom - portBounds.top);
    CGContextScaleCTM(cgContext, 1.0, -1.0);
    CGContextScaleCTM(cgContext, 1.0, -1.0);<br>
    return cgContext;
    return cgContext;
  }<br>
  }<br>
  static void endQDPluginUpdate(NPWindow *window, CGContextRef cgContext)
  static void endQDPluginUpdate(NPWindow *window, CGContextRef cgContext)
  {
  {
    // Restore state (it was saved in beginQDPluginUpdate())
    // Restore state (it was saved in beginQDPluginUpdate())
    CGContextRestoreGState(cgContext);
    CGContextRestoreGState(cgContext);<br>
    // If we had to prepare the CGContext for use in a QuickDraw-only browser,
    // If we had to prepare the CGContext for use in a QuickDraw-only browser,
    // restore its state and notify QD that the CG drawing sequence is over.
    // restore its state and notify QD that the CG drawing sequence is over.
    CGContextFlush(cgContext);
    CGContextFlush(cgContext);
    QDEndCGContext(((NP_Port *)window->window)->port, &cgContext);
    QDEndCGContext(((NP_Port *)window->window)->port, &cgContext);
  }
  }


This trick will not work once QuickDraw is removed from Mac OS X.  It is intended for plugin developers that want to draw using CoreGraphics, yet remain compatible with QuickDraw-only browsers that haven't adopted these proposed NPAPI extensions.
This trick will not work once QuickDraw is removed from Mac OS X.  It is intended for plugin developers that want to draw using CoreGraphics, yet remain compatible with QuickDraw-only browsers that haven't adopted these proposed NPAPI extensions.
== Your feedback is important! ==
If you are at all involved in Mac browser or plugin development, these changes will affect you.  So make your voice heard!  We're open to any questions or comments you might have about these proposed changes. Please post comments on the plugin-futures mailing list.

Latest revision as of 07:10, 9 March 2011

Status

Accepted, ready for implementation.

Contributors

  • Last modified: April 22, 2010
  • Authors: Tim Omernick (Apple)
  • Contributors: Josh Aas (Mozilla Corporation)

Overview

The Core Graphics drawing model is an alternative drawing model for 32-bit Mac OS X plugins and the default drawing model for 64-bit Mac OS X plugins.

Note: This specification pre-dates the Cocoa specification, so it assumes the Carbon event model. Discussions of "changes" refer to differences from the QuickDraw drawing model. When using CoreGraphics with the Cocoa event model, changes in the Cocoa model may supersede information given here.

Negotiating Core Graphics Drawing

For documentation on negotiating drawing models, see NPAPI:Models. The drawing model variables for Core Graphics are:

  • NPDrawingModelCoreGraphics (NPDrawingModel = 1)
  • NPNVsupportsCoreGraphicsBool (NPNVariable = 2001)

Excluding QuickDraw

QuickDraw is default drawing model for 32-bit Mac OS X plugins. The QuickDraw drawing model does not exist for 64-bit Mac OS X. The drawing model variables for Quickdraw are:

  • NPDrawingModelQuickDraw (NPDrawingModel = 0)
  • NPNVsupportsQuickDrawBool (NPNVariable = 2000)

Plugins and browsers that are compiled 64-bit must entirely exclude QuickDraw support, since there will be no 64-bit QuickDraw.

We are adding a #define to npapi.h to make this easier:

#if defined(XP_MACOSX) && defined(__LP64__)
    #define NP_NO_QUICKDRAW
#endif

The Core Graphics Drawing Model

If a plugin sets the drawing model to NPDrawingModelCoreGraphics, then the meanings of some of the NPAPI data structures change:

- NPWindow's 'window' field (under the Carbon event model) becomes a NP_CGContext:

/* NP_CGContext is the type of the NPWindow's 'window' when the
 * plugin specifies NPDrawingModelCoreGraphics as its drawing model.
 */
typedef struct NP_CGContext
{
    CGContextRef context;
    WindowRef window;
} NP_CGContext;

Note: the CG context here is always flipped for historical reasons.

- NPRegion becomes a CGPathRef instead of a RgnHandle:

#if defined(XP_MAC)
typedef RgnHandle NPRegion;
#elif defined(XP_MACOSX)
/* NPRegion's type depends on the drawing model specified by the plugin
 * (see  NPNVpluginDrawingModel).
 * NPQDRegion represents a QuickDraw RgnHandle, and NPCGRegion represents
 * a CoreGraphics CGPathRef.
 */
typedef void *NPRegion;
#ifndef NP_NO_QUICKDRAW
typedef RgnHandle NPQDRegion;
#endif
typedef CGPathRef NPCGRegion;
#endif /* XP_MAC */

Optimized drawing

From a performance standpoint, it is important for some plugins to restrict drawing to the window's "invalid region". This allows for more efficient repainting. Historically, Mac Netscape plugins have done this by getting the window or port's invalid region. CoreGraphics plugins should instead use the CGContextRef's clip path. Before sending an updateEvt to the plugin, the browser will intersect the CGContext's clip path with the plugin's invalid region. The plugin may then use functions like CGContextGetClipBoundingBox() to restrict drawing to only the clip region.

Bridging QuickDraw and CoreGraphics

There is a way for plugins to draw using CoreGraphics, yet remain compatible with QuickDraw-only browsers. The idea is to use QDBeginCGContext() and QDEndCGContext() to obtain a CGContextRef for the CGrafPtr provided by the browser.

static CGContextRef beginQDPluginUpdate(NPWindow *window)
{
    // window->window is an NPPort* since the browser is using QuickDraw
    NP_Port *npPort = ((NP_Port *)window->window);
// Get the CGContext for the port CGContextRef cgContext; QDBeginCGContext(npPort->port, &cgContext); CGContextSaveGState(cgContext);
// Set the CG clip path to the port's clip region -- QDBeginCGContext() // does not automatically respect the QuickDraw port's clip region. RgnHandle clipRegion = NewRgn(); GetPortClipRegion(npPort->port, clipRegion); Rect portBounds; GetPortBounds(npPort->port, &portBounds); ClipCGContextToRegion(cgContext, &portBounds, clipRegion); DisposeRgn(clipRegion);
// Flip the CG context vertically -- its origin is at the lower left, // but QuickDraw's origin is at the upper left. CGContextTranslateCTM(cgContext, 0.0, portBounds.bottom - portBounds.top); CGContextScaleCTM(cgContext, 1.0, -1.0);
return cgContext; }
static void endQDPluginUpdate(NPWindow *window, CGContextRef cgContext) { // Restore state (it was saved in beginQDPluginUpdate()) CGContextRestoreGState(cgContext);
// If we had to prepare the CGContext for use in a QuickDraw-only browser, // restore its state and notify QD that the CG drawing sequence is over. CGContextFlush(cgContext); QDEndCGContext(((NP_Port *)window->window)->port, &cgContext); }

This trick will not work once QuickDraw is removed from Mac OS X. It is intended for plugin developers that want to draw using CoreGraphics, yet remain compatible with QuickDraw-only browsers that haven't adopted these proposed NPAPI extensions.