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 ANPTypefaceInterfaceV0 gTypefaceI;
39
40 ///////////////////////////////////////////////////////////////////////////////
41
PaintPlugin(NPP inst)42 PaintPlugin::PaintPlugin(NPP inst) : SurfaceSubPlugin(inst) {
43
44 m_isTouchActive = false;
45 m_isTouchCurrentInput = true;
46 m_activePaintColor = s_redColor;
47
48 memset(&m_drawingSurface, 0, sizeof(m_drawingSurface));
49 memset(&m_inputToggle, 0, sizeof(m_inputToggle));
50 memset(&m_colorToggle, 0, sizeof(m_colorToggle));
51 memset(&m_clearSurface, 0, sizeof(m_clearSurface));
52
53 // initialize the drawing surface
54 m_surface = NULL;
55 m_vm = NULL;
56
57 // initialize the path
58 m_touchPath = gPathI.newPath();
59 if(!m_touchPath)
60 gLogI.log(inst, kError_ANPLogType, "----%p Unable to create the touch path", inst);
61
62 // initialize the paint colors
63 m_paintSurface = gPaintI.newPaint();
64 gPaintI.setFlags(m_paintSurface, gPaintI.getFlags(m_paintSurface) | kAntiAlias_ANPPaintFlag);
65 gPaintI.setColor(m_paintSurface, 0xFFC0C0C0);
66 gPaintI.setTextSize(m_paintSurface, 18);
67
68 m_paintButton = gPaintI.newPaint();
69 gPaintI.setFlags(m_paintButton, gPaintI.getFlags(m_paintButton) | kAntiAlias_ANPPaintFlag);
70 gPaintI.setColor(m_paintButton, 0xFFA8A8A8);
71
72 // initialize the typeface (set the colors)
73 ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
74 gPaintI.setTypeface(m_paintSurface, tf);
75 gTypefaceI.unref(tf);
76
77 //register for touch events
78 ANPEventFlags flags = kTouch_ANPEventFlag;
79 NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags);
80 if (err != NPERR_NO_ERROR) {
81 gLogI.log(inst, kError_ANPLogType, "Error selecting input events.");
82 }
83 }
84
~PaintPlugin()85 PaintPlugin::~PaintPlugin() {
86 gPathI.deletePath(m_touchPath);
87 gPaintI.deletePaint(m_paintSurface);
88 gPaintI.deletePaint(m_paintButton);
89 surfaceDestroyed();
90 }
91
supportsDrawingModel(ANPDrawingModel model)92 bool PaintPlugin::supportsDrawingModel(ANPDrawingModel model) {
93 return (model == kSurface_ANPDrawingModel);
94 }
95
getCanvas(ANPRectI * dirtyRect)96 ANPCanvas* PaintPlugin::getCanvas(ANPRectI* dirtyRect) {
97
98 ANPBitmap bitmap;
99 JNIEnv* env = NULL;
100 if (!m_surface || m_vm->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 && m_vm->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/2) - (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 clear canvas button
185 m_clearSurface.left = W - buttonWidth - 5;
186 m_clearSurface.top = H - buttonHeight - 5;
187 m_clearSurface.right = m_clearSurface.left + buttonWidth;
188 m_clearSurface.bottom = m_clearSurface.top + buttonHeight;
189 gCanvasI.drawRect(canvas, &m_clearSurface, m_paintButton);
190 const char* clearText = "Clear";
191 gCanvasI.drawText(canvas, clearText, strlen(clearText), m_clearSurface.left + 5,
192 m_clearSurface.top - fontMetrics.fTop, m_paintSurface);
193
194 // draw the drawing surface box (5 px from the edge)
195 m_drawingSurface.left = 5;
196 m_drawingSurface.top = 5;
197 m_drawingSurface.right = W - 5;
198 m_drawingSurface.bottom = m_colorToggle.top - 5;
199 gCanvasI.drawRect(canvas, &m_drawingSurface, m_paintSurface);
200
201 // release the canvas
202 releaseCanvas(canvas);
203 }
204
getColorText()205 const char* PaintPlugin::getColorText() {
206
207 if (m_activePaintColor == s_blueColor)
208 return "Blue";
209 else if (m_activePaintColor == s_greenColor)
210 return "Green";
211 else
212 return "Red";
213 }
214
isFixedSurface()215 bool PaintPlugin::isFixedSurface() {
216 return true;
217 }
218
surfaceCreated(JNIEnv * env,jobject surface)219 void PaintPlugin::surfaceCreated(JNIEnv* env, jobject surface) {
220 env->GetJavaVM(&m_vm);
221 m_surface = env->NewGlobalRef(surface);
222 drawCleanPlugin();
223 }
224
surfaceChanged(int format,int width,int height)225 void PaintPlugin::surfaceChanged(int format, int width, int height) {
226 // get the plugin's dimensions according to the DOM
227 PluginObject *obj = (PluginObject*) inst()->pdata;
228 const int pW = obj->window->width;
229 const int pH = obj->window->height;
230 // compare to the plugin's surface dimensions
231 if (pW != width || pH != height)
232 gLogI.log(inst(), kError_ANPLogType,
233 "----%p Invalid Surface Dimensions (%d,%d):(%d,%d)",
234 inst(), pW, pH, width, height);
235 }
surfaceDestroyed()236 void PaintPlugin::surfaceDestroyed() {
237 JNIEnv* env = NULL;
238 if (m_surface && m_vm->GetEnv((void**) &env, JNI_VERSION_1_4) == JNI_OK) {
239 env->DeleteGlobalRef(m_surface);
240 m_surface = NULL;
241 }
242 }
243
handleEvent(const ANPEvent * evt)244 int16 PaintPlugin::handleEvent(const ANPEvent* evt) {
245 switch (evt->eventType) {
246 case kTouch_ANPEventType: {
247 float x = (float) evt->data.touch.x;
248 float y = (float) evt->data.touch.y;
249 if (kDown_ANPTouchAction == evt->data.touch.action && m_isTouchCurrentInput) {
250
251 ANPRectF* rect = validTouch(evt->data.touch.x, evt->data.touch.y);
252 if(rect == &m_drawingSurface) {
253 m_isTouchActive = true;
254 gPathI.moveTo(m_touchPath, x, y);
255 paintTouch();
256 return 1;
257 }
258
259 } else if (kMove_ANPTouchAction == evt->data.touch.action && m_isTouchActive) {
260 gPathI.lineTo(m_touchPath, x, y);
261 paintTouch();
262 return 1;
263 } else if (kUp_ANPTouchAction == evt->data.touch.action && m_isTouchActive) {
264 gPathI.lineTo(m_touchPath, x, y);
265 paintTouch();
266 m_isTouchActive = false;
267 gPathI.reset(m_touchPath);
268 return 1;
269 } else if (kCancel_ANPTouchAction == evt->data.touch.action) {
270 m_isTouchActive = false;
271 gPathI.reset(m_touchPath);
272 return 1;
273 }
274
275 break;
276 }
277 case kMouse_ANPEventType: {
278
279 if (m_isTouchActive)
280 gLogI.log(inst(), kError_ANPLogType, "----%p Received unintended mouse event", inst());
281
282 if (kDown_ANPMouseAction == evt->data.mouse.action) {
283 ANPRectF* rect = validTouch(evt->data.mouse.x, evt->data.mouse.y);
284 if (rect == &m_drawingSurface)
285 paintMouse(evt->data.mouse.x, evt->data.mouse.y);
286 else if (rect == &m_inputToggle)
287 toggleInputMethod();
288 else if (rect == &m_colorToggle)
289 togglePaintColor();
290 else if (rect == &m_clearSurface)
291 drawCleanPlugin();
292 }
293 return 1;
294 }
295 default:
296 break;
297 }
298 return 0; // unknown or unhandled event
299 }
300
validTouch(int x,int y)301 ANPRectF* PaintPlugin::validTouch(int x, int y) {
302
303 //convert to float
304 float fx = (int) x;
305 float fy = (int) y;
306
307 if (fx > m_drawingSurface.left && fx < m_drawingSurface.right && fy > m_drawingSurface.top && fy < m_drawingSurface.bottom)
308 return &m_drawingSurface;
309 else if (fx > m_inputToggle.left && fx < m_inputToggle.right && fy > m_inputToggle.top && fy < m_inputToggle.bottom)
310 return &m_inputToggle;
311 else if (fx > m_colorToggle.left && fx < m_colorToggle.right && fy > m_colorToggle.top && fy < m_colorToggle.bottom)
312 return &m_colorToggle;
313 else if (fx > m_clearSurface.left && fx < m_clearSurface.right && fy > m_clearSurface.top && fy < m_clearSurface.bottom)
314 return &m_clearSurface;
315 else
316 return NULL;
317 }
318
toggleInputMethod()319 void PaintPlugin::toggleInputMethod() {
320 m_isTouchCurrentInput = !m_isTouchCurrentInput;
321
322 // lock only the input toggle and redraw the canvas
323 ANPCanvas* lockedCanvas = getCanvas(&m_inputToggle);
324 drawCleanPlugin(lockedCanvas);
325 }
326
togglePaintColor()327 void PaintPlugin::togglePaintColor() {
328 if (m_activePaintColor == s_blueColor)
329 m_activePaintColor = s_redColor;
330 else if (m_activePaintColor == s_greenColor)
331 m_activePaintColor = s_blueColor;
332 else
333 m_activePaintColor = s_greenColor;
334
335 // lock only the color toggle and redraw the canvas
336 ANPCanvas* lockedCanvas = getCanvas(&m_colorToggle);
337 drawCleanPlugin(lockedCanvas);
338 }
339
paintMouse(int x,int y)340 void PaintPlugin::paintMouse(int x, int y) {
341 //TODO do not paint outside the drawing surface
342
343 //create the paint color
344 ANPPaint* fillPaint = gPaintI.newPaint();
345 gPaintI.setFlags(fillPaint, gPaintI.getFlags(fillPaint) | kAntiAlias_ANPPaintFlag);
346 gPaintI.setStyle(fillPaint, kFill_ANPPaintStyle);
347 gPaintI.setColor(fillPaint, m_activePaintColor);
348
349 // handle the simple "mouse" paint (draw a point)
350 ANPRectF point;
351 point.left = (float) x-3;
352 point.top = (float) y-3;
353 point.right = (float) x+3;
354 point.bottom = (float) y+3;
355
356 // get a canvas that is only locked around the point and draw it
357 ANPCanvas* canvas = getCanvas(&point);
358 gCanvasI.drawOval(canvas, &point, fillPaint);
359
360 // clean up
361 releaseCanvas(canvas);
362 gPaintI.deletePaint(fillPaint);
363 }
364
paintTouch()365 void PaintPlugin::paintTouch() {
366 //TODO do not paint outside the drawing surface
367
368 //create the paint color
369 ANPPaint* strokePaint = gPaintI.newPaint();
370 gPaintI.setFlags(strokePaint, gPaintI.getFlags(strokePaint) | kAntiAlias_ANPPaintFlag);
371 gPaintI.setColor(strokePaint, m_activePaintColor);
372 gPaintI.setStyle(strokePaint, kStroke_ANPPaintStyle);
373 gPaintI.setStrokeWidth(strokePaint, 6.0);
374 gPaintI.setStrokeCap(strokePaint, kRound_ANPPaintCap);
375 gPaintI.setStrokeJoin(strokePaint, kRound_ANPPaintJoin);
376
377 // handle the complex "touch" paint (draw a line)
378 ANPRectF bounds;
379 gPathI.getBounds(m_touchPath, &bounds);
380
381 // get a canvas that is only locked around the point and draw the path
382 ANPCanvas* canvas = getCanvas(&bounds);
383 gCanvasI.drawPath(canvas, m_touchPath, strokePaint);
384
385 // clean up
386 releaseCanvas(canvas);
387 gPaintI.deletePaint(strokePaint);
388 }
389