• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2009, The Android Open Source Project
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *  * Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  *  * Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "PaintPlugin.h"
27 
28 #include <fcntl.h>
29 #include <math.h>
30 #include <string.h>
31 
32 extern NPNetscapeFuncs*         browser;
33 extern ANPLogInterfaceV0        gLogI;
34 extern ANPCanvasInterfaceV0     gCanvasI;
35 extern ANPPaintInterfaceV0      gPaintI;
36 extern ANPPathInterfaceV0       gPathI;
37 extern ANPSurfaceInterfaceV0    gSurfaceI;
38 extern ANPSystemInterfaceV0     gSystemI;
39 extern ANPTypefaceInterfaceV0   gTypefaceI;
40 extern ANPWindowInterfaceV0     gWindowI;
41 
42 ///////////////////////////////////////////////////////////////////////////////
43 
PaintPlugin(NPP inst)44 PaintPlugin::PaintPlugin(NPP inst) : SurfaceSubPlugin(inst) {
45 
46     m_isTouchActive = false;
47     m_isTouchCurrentInput = true;
48     m_activePaintColor = s_redColor;
49 
50     memset(&m_drawingSurface, 0, sizeof(m_drawingSurface));
51     memset(&m_inputToggle,  0, sizeof(m_inputToggle));
52     memset(&m_colorToggle, 0, sizeof(m_colorToggle));
53     memset(&m_fullScreenToggle, 0, sizeof(m_fullScreenToggle));
54     memset(&m_clearSurface,  0, sizeof(m_clearSurface));
55 
56     // initialize the drawing surface
57     m_surface = NULL;
58 
59     // initialize the path
60     m_touchPath = gPathI.newPath();
61     if(!m_touchPath)
62         gLogI.log(kError_ANPLogType, "----%p Unable to create the touch path", inst);
63 
64     // initialize the paint colors
65     m_paintSurface = gPaintI.newPaint();
66     gPaintI.setFlags(m_paintSurface, gPaintI.getFlags(m_paintSurface) | kAntiAlias_ANPPaintFlag);
67     gPaintI.setColor(m_paintSurface, 0xFFC0C0C0);
68     gPaintI.setTextSize(m_paintSurface, 18);
69 
70     m_paintButton = gPaintI.newPaint();
71     gPaintI.setFlags(m_paintButton, gPaintI.getFlags(m_paintButton) | kAntiAlias_ANPPaintFlag);
72     gPaintI.setColor(m_paintButton, 0xFFA8A8A8);
73 
74     // initialize the typeface (set the colors)
75     ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
76     gPaintI.setTypeface(m_paintSurface, tf);
77     gTypefaceI.unref(tf);
78 
79     //register for touch events
80     ANPEventFlags flags = kTouch_ANPEventFlag;
81     NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags);
82     if (err != NPERR_NO_ERROR) {
83         gLogI.log(kError_ANPLogType, "Error selecting input events.");
84     }
85 }
86 
~PaintPlugin()87 PaintPlugin::~PaintPlugin() {
88     gPathI.deletePath(m_touchPath);
89     gPaintI.deletePaint(m_paintSurface);
90     gPaintI.deletePaint(m_paintButton);
91 
92     setContext(NULL);
93     destroySurface();
94 }
95 
getCanvas(ANPRectI * dirtyRect)96 ANPCanvas* PaintPlugin::getCanvas(ANPRectI* dirtyRect) {
97 
98     ANPBitmap bitmap;
99     JNIEnv* env = NULL;
100     if (!m_surface || gVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK ||
101         !gSurfaceI.lock(env, m_surface, &bitmap, dirtyRect)) {
102             return NULL;
103         }
104 
105     ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
106 
107     // clip the canvas to the dirty rect b/c the surface is only required to
108     // copy a minimum of the dirty rect and may copy more. The clipped canvas
109     // however will never write to pixels outside of the clipped area.
110     if (dirtyRect) {
111         ANPRectF clipR;
112         clipR.left = dirtyRect->left;
113         clipR.top = dirtyRect->top;
114         clipR.right = dirtyRect->right;
115         clipR.bottom = dirtyRect->bottom;
116         gCanvasI.clipRect(canvas, &clipR);
117     }
118 
119     return canvas;
120 }
121 
getCanvas(ANPRectF * dirtyRect)122 ANPCanvas* PaintPlugin::getCanvas(ANPRectF* dirtyRect) {
123 
124     ANPRectI newRect;
125     newRect.left = (int) dirtyRect->left;
126     newRect.top = (int) dirtyRect->top;
127     newRect.right = (int) dirtyRect->right;
128     newRect.bottom = (int) dirtyRect->bottom;
129 
130     return getCanvas(&newRect);
131 }
132 
releaseCanvas(ANPCanvas * canvas)133 void PaintPlugin::releaseCanvas(ANPCanvas* canvas) {
134     JNIEnv* env = NULL;
135     if (m_surface && gVM->GetEnv((void**) &env, JNI_VERSION_1_4) == JNI_OK) {
136         gSurfaceI.unlock(env, m_surface);
137     }
138     gCanvasI.deleteCanvas(canvas);
139 }
140 
drawCleanPlugin(ANPCanvas * canvas)141 void PaintPlugin::drawCleanPlugin(ANPCanvas* canvas) {
142     NPP instance = this->inst();
143     PluginObject *obj = (PluginObject*) instance->pdata;
144 
145     // if no canvas get a locked canvas
146     if (!canvas)
147         canvas = getCanvas();
148 
149     if (!canvas)
150         return;
151 
152     const float buttonWidth = 60;
153     const float buttonHeight = 30;
154     const int W = obj->window->width;
155     const int H = obj->window->height;
156 
157     // color the plugin canvas
158     gCanvasI.drawColor(canvas, 0xFFCDCDCD);
159 
160     // get font metrics
161     ANPFontMetrics fontMetrics;
162     gPaintI.getFontMetrics(m_paintSurface, &fontMetrics);
163 
164     // draw the input toggle button
165     m_inputToggle.left = 5;
166     m_inputToggle.top = H - buttonHeight - 5;
167     m_inputToggle.right = m_inputToggle.left + buttonWidth;
168     m_inputToggle.bottom = m_inputToggle.top + buttonHeight;
169     gCanvasI.drawRect(canvas, &m_inputToggle, m_paintButton);
170     const char* inputText = m_isTouchCurrentInput ? "Touch" : "Mouse";
171     gCanvasI.drawText(canvas, inputText, strlen(inputText), m_inputToggle.left + 5,
172                       m_inputToggle.top - fontMetrics.fTop, m_paintSurface);
173 
174     // draw the color selector button
175     m_colorToggle.left = (W/3) - (buttonWidth/2);
176     m_colorToggle.top = H - buttonHeight - 5;
177     m_colorToggle.right = m_colorToggle.left + buttonWidth;
178     m_colorToggle.bottom = m_colorToggle.top + buttonHeight;
179     gCanvasI.drawRect(canvas, &m_colorToggle, m_paintButton);
180     const char* colorText = getColorText();
181     gCanvasI.drawText(canvas, colorText, strlen(colorText), m_colorToggle.left + 5,
182                       m_colorToggle.top - fontMetrics.fTop, m_paintSurface);
183 
184     // draw the full-screen toggle button
185     m_fullScreenToggle.left = ((W*2)/3) - (buttonWidth/2);
186     m_fullScreenToggle.top = H - buttonHeight - 5;
187     m_fullScreenToggle.right = m_fullScreenToggle.left + buttonWidth;
188     m_fullScreenToggle.bottom = m_fullScreenToggle.top + buttonHeight;
189     gCanvasI.drawRect(canvas, &m_fullScreenToggle, m_paintButton);
190     const char* fullScreenText = "Full";
191     gCanvasI.drawText(canvas, fullScreenText, strlen(fullScreenText),
192                       m_fullScreenToggle.left + 5,
193                       m_fullScreenToggle.top - fontMetrics.fTop, m_paintSurface);
194 
195     // draw the clear canvas button
196     m_clearSurface.left = W - buttonWidth - 5;
197     m_clearSurface.top = H - buttonHeight - 5;
198     m_clearSurface.right = m_clearSurface.left + buttonWidth;
199     m_clearSurface.bottom = m_clearSurface.top + buttonHeight;
200     gCanvasI.drawRect(canvas, &m_clearSurface, m_paintButton);
201     const char* clearText = "Clear";
202     gCanvasI.drawText(canvas, clearText, strlen(clearText), m_clearSurface.left + 5,
203                       m_clearSurface.top - fontMetrics.fTop, m_paintSurface);
204 
205     // draw the drawing surface box (5 px from the edge)
206     m_drawingSurface.left = 5;
207     m_drawingSurface.top = 5;
208     m_drawingSurface.right = W - 5;
209     m_drawingSurface.bottom = m_colorToggle.top - 5;
210     gCanvasI.drawRect(canvas, &m_drawingSurface, m_paintSurface);
211 
212     // release the canvas
213     releaseCanvas(canvas);
214 }
215 
getColorText()216 const char* PaintPlugin::getColorText() {
217 
218     if (m_activePaintColor == s_blueColor)
219         return "Blue";
220     else if (m_activePaintColor == s_greenColor)
221         return "Green";
222     else
223         return "Red";
224 }
225 
getSurface()226 jobject PaintPlugin::getSurface() {
227     if (m_surface) {
228         return m_surface;
229     }
230 
231     // load the appropriate java class and instantiate it
232     JNIEnv* env = NULL;
233     if (gVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
234         gLogI.log(kError_ANPLogType, " ---- getSurface: failed to get env");
235         return NULL;
236     }
237 
238     const char* className = "com.android.sampleplugin.PaintSurface";
239     jclass paintClass = gSystemI.loadJavaClass(inst(), className);
240 
241     if(!paintClass) {
242         gLogI.log(kError_ANPLogType, " ---- getSurface: failed to load class");
243         return NULL;
244     }
245 
246     PluginObject *obj = (PluginObject*) inst()->pdata;
247     const int pW = obj->window->width;
248     const int pH = obj->window->height;
249 
250     jmethodID constructor = env->GetMethodID(paintClass, "<init>", "(Landroid/content/Context;III)V");
251     jobject paintSurface = env->NewObject(paintClass, constructor, m_context, (int)inst(), pW, pH);
252 
253     if(!paintSurface) {
254         gLogI.log(kError_ANPLogType, " ---- getSurface: failed to construct object");
255         return NULL;
256     }
257 
258     m_surface = env->NewGlobalRef(paintSurface);
259     return m_surface;
260 }
261 
destroySurface()262 void PaintPlugin::destroySurface() {
263     JNIEnv* env = NULL;
264     if (m_surface && gVM->GetEnv((void**) &env, JNI_VERSION_1_4) == JNI_OK) {
265 
266         // detach the native code from the object
267         jclass javaClass = env->GetObjectClass(m_surface);
268         jmethodID invalMethod = env->GetMethodID(javaClass, "invalidateNPP", "()V");
269         env->CallVoidMethod(m_surface, invalMethod);
270 
271         env->DeleteGlobalRef(m_surface);
272         m_surface = NULL;
273     }
274 }
275 
handleEvent(const ANPEvent * evt)276 int16_t PaintPlugin::handleEvent(const ANPEvent* evt) {
277     switch (evt->eventType) {
278         case kTouch_ANPEventType: {
279             float x = (float) evt->data.touch.x;
280             float y = (float) evt->data.touch.y;
281             if (kDown_ANPTouchAction == evt->data.touch.action && m_isTouchCurrentInput) {
282 
283                 ANPRectF* rect = validTouch(evt->data.touch.x, evt->data.touch.y);
284                 if(rect == &m_drawingSurface) {
285                     m_isTouchActive = true;
286                     gPathI.moveTo(m_touchPath, x, y);
287                     paintTouch();
288                     return 1;
289                 }
290 
291             } else if (kMove_ANPTouchAction == evt->data.touch.action && m_isTouchActive) {
292                 gPathI.lineTo(m_touchPath, x, y);
293                 paintTouch();
294                 return 1;
295             } else if (kUp_ANPTouchAction == evt->data.touch.action && m_isTouchActive) {
296                 gPathI.lineTo(m_touchPath, x, y);
297                 paintTouch();
298                 m_isTouchActive = false;
299                 gPathI.reset(m_touchPath);
300                 return 1;
301             } else if (kCancel_ANPTouchAction == evt->data.touch.action) {
302                 m_isTouchActive = false;
303                 gPathI.reset(m_touchPath);
304                 return 1;
305             } else if (kDoubleTap_ANPTouchAction == evt->data.touch.action) {
306                 gWindowI.requestCenterFitZoom(inst());
307                 return 1;
308 
309             }
310             break;
311         }
312         case kMouse_ANPEventType: {
313 
314             if (m_isTouchActive)
315                 gLogI.log(kError_ANPLogType, "----%p Received unintended mouse event", inst());
316 
317             if (kDown_ANPMouseAction == evt->data.mouse.action) {
318                 ANPRectF* rect = validTouch(evt->data.mouse.x, evt->data.mouse.y);
319                 if (rect == &m_drawingSurface)
320                     paintMouse(evt->data.mouse.x, evt->data.mouse.y);
321                 else if (rect == &m_inputToggle)
322                     toggleInputMethod();
323                 else if (rect == &m_colorToggle)
324                     togglePaintColor();
325                 else if (rect == &m_fullScreenToggle)
326                     gWindowI.requestFullScreen(inst());
327                 else if (rect == &m_clearSurface)
328                     drawCleanPlugin();
329             }
330             return 1;
331         }
332         case kCustom_ANPEventType: {
333 
334             switch (evt->data.other[0]) {
335                 case kSurfaceCreated_CustomEvent:
336                     gLogI.log(kDebug_ANPLogType, " ---- customEvent: surfaceCreated");
337                     /* The second draw call is added to cover up a problem in this
338                        plugin and is not a recommended usage pattern. This plugin
339                        does not correctly make partial updates to the double
340                        buffered surface and this second call hides that problem.
341                      */
342                     drawCleanPlugin();
343                     drawCleanPlugin();
344                     break;
345                 case kSurfaceChanged_CustomEvent: {
346                     gLogI.log(kDebug_ANPLogType, " ---- customEvent: surfaceChanged");
347 
348                     int width = evt->data.other[1];
349                     int height = evt->data.other[2];
350 
351                     PluginObject *obj = (PluginObject*) inst()->pdata;
352                     const int pW = obj->window->width;
353                     const int pH = obj->window->height;
354                     // compare to the plugin's surface dimensions
355                     if (pW != width || pH != height)
356                         gLogI.log(kError_ANPLogType,
357                                   "----%p Invalid Surface Dimensions (%d,%d):(%d,%d)",
358                                   inst(), pW, pH, width, height);
359                     break;
360                 }
361                 case kSurfaceDestroyed_CustomEvent:
362                     gLogI.log(kDebug_ANPLogType, " ---- customEvent: surfaceDestroyed");
363                     break;
364             }
365             break; // end KCustom_ANPEventType
366         }
367         default:
368             break;
369     }
370     return 0;   // unknown or unhandled event
371 }
372 
validTouch(int x,int y)373 ANPRectF* PaintPlugin::validTouch(int x, int y) {
374 
375     //convert to float
376     float fx = (int) x;
377     float fy = (int) y;
378 
379     if (fx > m_drawingSurface.left && fx < m_drawingSurface.right && fy > m_drawingSurface.top && fy < m_drawingSurface.bottom)
380         return &m_drawingSurface;
381     else if (fx > m_inputToggle.left && fx < m_inputToggle.right && fy > m_inputToggle.top && fy < m_inputToggle.bottom)
382         return &m_inputToggle;
383     else if (fx > m_colorToggle.left && fx < m_colorToggle.right && fy > m_colorToggle.top && fy < m_colorToggle.bottom)
384         return &m_colorToggle;
385     else if (fx > m_fullScreenToggle.left && fx < m_fullScreenToggle.right && fy > m_fullScreenToggle.top && fy < m_fullScreenToggle.bottom)
386         return &m_fullScreenToggle;
387     else if (fx > m_clearSurface.left && fx < m_clearSurface.right && fy > m_clearSurface.top && fy < m_clearSurface.bottom)
388         return &m_clearSurface;
389     else
390         return NULL;
391 }
392 
toggleInputMethod()393 void PaintPlugin::toggleInputMethod() {
394     m_isTouchCurrentInput = !m_isTouchCurrentInput;
395 
396     // lock only the input toggle and redraw the canvas
397     ANPCanvas* lockedCanvas = getCanvas(&m_inputToggle);
398     drawCleanPlugin(lockedCanvas);
399 }
400 
togglePaintColor()401 void PaintPlugin::togglePaintColor() {
402     if (m_activePaintColor == s_blueColor)
403         m_activePaintColor = s_redColor;
404     else if (m_activePaintColor == s_greenColor)
405         m_activePaintColor = s_blueColor;
406     else
407         m_activePaintColor = s_greenColor;
408 
409     // lock only the color toggle and redraw the canvas
410     ANPCanvas* lockedCanvas = getCanvas(&m_colorToggle);
411     drawCleanPlugin(lockedCanvas);
412 }
413 
paintMouse(int x,int y)414 void PaintPlugin::paintMouse(int x, int y) {
415     //TODO do not paint outside the drawing surface
416 
417     //create the paint color
418     ANPPaint* fillPaint = gPaintI.newPaint();
419     gPaintI.setFlags(fillPaint, gPaintI.getFlags(fillPaint) | kAntiAlias_ANPPaintFlag);
420     gPaintI.setStyle(fillPaint, kFill_ANPPaintStyle);
421     gPaintI.setColor(fillPaint, m_activePaintColor);
422 
423     // handle the simple "mouse" paint (draw a point)
424     ANPRectF point;
425     point.left =   (float) x-3;
426     point.top =    (float) y-3;
427     point.right =  (float) x+3;
428     point.bottom = (float) y+3;
429 
430     // get a canvas that is only locked around the point and draw it
431     ANPCanvas* canvas = getCanvas(&point);
432     gCanvasI.drawOval(canvas, &point, fillPaint);
433 
434     // clean up
435     releaseCanvas(canvas);
436     gPaintI.deletePaint(fillPaint);
437 }
438 
paintTouch()439 void PaintPlugin::paintTouch() {
440     //TODO do not paint outside the drawing surface
441 
442     //create the paint color
443     ANPPaint* strokePaint = gPaintI.newPaint();
444     gPaintI.setFlags(strokePaint, gPaintI.getFlags(strokePaint) | kAntiAlias_ANPPaintFlag);
445     gPaintI.setColor(strokePaint, m_activePaintColor);
446     gPaintI.setStyle(strokePaint, kStroke_ANPPaintStyle);
447     gPaintI.setStrokeWidth(strokePaint, 6.0);
448     gPaintI.setStrokeCap(strokePaint, kRound_ANPPaintCap);
449     gPaintI.setStrokeJoin(strokePaint, kRound_ANPPaintJoin);
450 
451     // handle the complex "touch" paint (draw a line)
452     ANPRectF bounds;
453     gPathI.getBounds(m_touchPath, &bounds);
454 
455     // get a canvas that is only locked around the point and draw the path
456     ANPCanvas* canvas = getCanvas(&bounds);
457     gCanvasI.drawPath(canvas, m_touchPath, strokePaint);
458 
459     // clean up
460     releaseCanvas(canvas);
461     gPaintI.deletePaint(strokePaint);
462 }
463