blob: 42222228a385864c47bacb58f8fdf80c9487caad [file] [log] [blame]
Derek Sollenberger4fb83e62009-07-27 16:40:13 -04001/*
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
32extern NPNetscapeFuncs* browser;
33extern ANPLogInterfaceV0 gLogI;
34extern ANPCanvasInterfaceV0 gCanvasI;
35extern ANPPaintInterfaceV0 gPaintI;
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -040036extern ANPPathInterfaceV0 gPathI;
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040037extern ANPSurfaceInterfaceV0 gSurfaceI;
38extern ANPTypefaceInterfaceV0 gTypefaceI;
39
40///////////////////////////////////////////////////////////////////////////////
41
Derek Sollenberger08581f12009-09-08 18:36:29 -040042PaintPlugin::PaintPlugin(NPP inst) : SurfaceSubPlugin(inst) {
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040043
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040044 m_isTouchActive = false;
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -040045 m_isTouchCurrentInput = true;
46 m_activePaintColor = s_redColor;
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040047
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
Derek Sollenberger51cce582009-12-01 08:26:20 -050053 // initialize the java interface
54 m_javaInterface = NULL;
55
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040056 // initialize the drawing surface
Derek Sollenberger08581f12009-09-08 18:36:29 -040057 m_surface = NULL;
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040058
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -040059 // initialize the path
60 m_touchPath = gPathI.newPath();
61 if(!m_touchPath)
Derek Sollenbergere62ce172009-11-30 11:52:06 -050062 gLogI.log(kError_ANPLogType, "----%p Unable to create the touch path", inst);
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -040063
64 // initialize the paint colors
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040065 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
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -040074 // initialize the typeface (set the colors)
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040075 ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
76 gPaintI.setTypeface(m_paintSurface, tf);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040077 gTypefaceI.unref(tf);
78
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040079 //register for touch events
80 ANPEventFlags flags = kTouch_ANPEventFlag;
81 NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags);
82 if (err != NPERR_NO_ERROR) {
Derek Sollenbergere62ce172009-11-30 11:52:06 -050083 gLogI.log(kError_ANPLogType, "Error selecting input events.");
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040084 }
85}
86
87PaintPlugin::~PaintPlugin() {
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -040088 gPathI.deletePath(m_touchPath);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040089 gPaintI.deletePaint(m_paintSurface);
90 gPaintI.deletePaint(m_paintButton);
Derek Sollenberger51cce582009-12-01 08:26:20 -050091 setJavaInterface(NULL);
Derek Sollenberger08581f12009-09-08 18:36:29 -040092 surfaceDestroyed();
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040093}
94
95bool PaintPlugin::supportsDrawingModel(ANPDrawingModel model) {
96 return (model == kSurface_ANPDrawingModel);
97}
98
99ANPCanvas* PaintPlugin::getCanvas(ANPRectI* dirtyRect) {
100
101 ANPBitmap bitmap;
Derek Sollenberger08581f12009-09-08 18:36:29 -0400102 JNIEnv* env = NULL;
Derek Sollenbergerb8947ee2009-10-05 14:40:18 -0400103 if (!m_surface || gVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK ||
Derek Sollenberger08581f12009-09-08 18:36:29 -0400104 !gSurfaceI.lock(env, m_surface, &bitmap, dirtyRect)) {
105 return NULL;
106 }
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400107
108 ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
109
110 // clip the canvas to the dirty rect b/c the surface is only required to
111 // copy a minimum of the dirty rect and may copy more. The clipped canvas
112 // however will never write to pixels outside of the clipped area.
113 if (dirtyRect) {
114 ANPRectF clipR;
115 clipR.left = dirtyRect->left;
116 clipR.top = dirtyRect->top;
117 clipR.right = dirtyRect->right;
118 clipR.bottom = dirtyRect->bottom;
119 gCanvasI.clipRect(canvas, &clipR);
120 }
121
122 return canvas;
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400123}
124
125ANPCanvas* PaintPlugin::getCanvas(ANPRectF* dirtyRect) {
126
127 ANPRectI newRect;
128 newRect.left = (int) dirtyRect->left;
129 newRect.top = (int) dirtyRect->top;
130 newRect.right = (int) dirtyRect->right;
131 newRect.bottom = (int) dirtyRect->bottom;
132
133 return getCanvas(&newRect);
134}
135
136void PaintPlugin::releaseCanvas(ANPCanvas* canvas) {
Derek Sollenberger08581f12009-09-08 18:36:29 -0400137 JNIEnv* env = NULL;
Derek Sollenbergerb8947ee2009-10-05 14:40:18 -0400138 if (m_surface && gVM->GetEnv((void**) &env, JNI_VERSION_1_4) == JNI_OK) {
Derek Sollenberger08581f12009-09-08 18:36:29 -0400139 gSurfaceI.unlock(env, m_surface);
140 }
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400141 gCanvasI.deleteCanvas(canvas);
142}
143
144void PaintPlugin::drawCleanPlugin(ANPCanvas* canvas) {
145 NPP instance = this->inst();
146 PluginObject *obj = (PluginObject*) instance->pdata;
147
148 // if no canvas get a locked canvas
149 if (!canvas)
150 canvas = getCanvas();
151
152 if (!canvas)
153 return;
154
155 const float buttonWidth = 60;
156 const float buttonHeight = 30;
157 const int W = obj->window->width;
158 const int H = obj->window->height;
159
160 // color the plugin canvas
161 gCanvasI.drawColor(canvas, 0xFFCDCDCD);
162
163 // get font metrics
164 ANPFontMetrics fontMetrics;
165 gPaintI.getFontMetrics(m_paintSurface, &fontMetrics);
166
167 // draw the input toggle button
168 m_inputToggle.left = 5;
169 m_inputToggle.top = H - buttonHeight - 5;
170 m_inputToggle.right = m_inputToggle.left + buttonWidth;
171 m_inputToggle.bottom = m_inputToggle.top + buttonHeight;
172 gCanvasI.drawRect(canvas, &m_inputToggle, m_paintButton);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400173 const char* inputText = m_isTouchCurrentInput ? "Touch" : "Mouse";
174 gCanvasI.drawText(canvas, inputText, strlen(inputText), m_inputToggle.left + 5,
175 m_inputToggle.top - fontMetrics.fTop, m_paintSurface);
176
177 // draw the color selector button
178 m_colorToggle.left = (W/2) - (buttonWidth/2);
179 m_colorToggle.top = H - buttonHeight - 5;
180 m_colorToggle.right = m_colorToggle.left + buttonWidth;
181 m_colorToggle.bottom = m_colorToggle.top + buttonHeight;
182 gCanvasI.drawRect(canvas, &m_colorToggle, m_paintButton);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400183 const char* colorText = getColorText();
184 gCanvasI.drawText(canvas, colorText, strlen(colorText), m_colorToggle.left + 5,
185 m_colorToggle.top - fontMetrics.fTop, m_paintSurface);
186
187 // draw the clear canvas button
188 m_clearSurface.left = W - buttonWidth - 5;
189 m_clearSurface.top = H - buttonHeight - 5;
190 m_clearSurface.right = m_clearSurface.left + buttonWidth;
191 m_clearSurface.bottom = m_clearSurface.top + buttonHeight;
192 gCanvasI.drawRect(canvas, &m_clearSurface, m_paintButton);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400193 const char* clearText = "Clear";
194 gCanvasI.drawText(canvas, clearText, strlen(clearText), m_clearSurface.left + 5,
195 m_clearSurface.top - fontMetrics.fTop, m_paintSurface);
196
197 // draw the drawing surface box (5 px from the edge)
198 m_drawingSurface.left = 5;
199 m_drawingSurface.top = 5;
200 m_drawingSurface.right = W - 5;
201 m_drawingSurface.bottom = m_colorToggle.top - 5;
202 gCanvasI.drawRect(canvas, &m_drawingSurface, m_paintSurface);
203
204 // release the canvas
205 releaseCanvas(canvas);
206}
207
208const char* PaintPlugin::getColorText() {
209
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400210 if (m_activePaintColor == s_blueColor)
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400211 return "Blue";
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400212 else if (m_activePaintColor == s_greenColor)
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400213 return "Green";
214 else
215 return "Red";
216}
217
Derek Sollenberger08581f12009-09-08 18:36:29 -0400218bool PaintPlugin::isFixedSurface() {
219 return true;
220}
221
Derek Sollenbergerb8947ee2009-10-05 14:40:18 -0400222void PaintPlugin::surfaceCreated(jobject surface) {
223 m_surface = surface;
Derek Sollenberger08581f12009-09-08 18:36:29 -0400224 drawCleanPlugin();
225}
226
227void PaintPlugin::surfaceChanged(int format, int width, int height) {
228 // get the plugin's dimensions according to the DOM
229 PluginObject *obj = (PluginObject*) inst()->pdata;
230 const int pW = obj->window->width;
231 const int pH = obj->window->height;
232 // compare to the plugin's surface dimensions
233 if (pW != width || pH != height)
Derek Sollenbergere62ce172009-11-30 11:52:06 -0500234 gLogI.log(kError_ANPLogType,
Derek Sollenberger08581f12009-09-08 18:36:29 -0400235 "----%p Invalid Surface Dimensions (%d,%d):(%d,%d)",
236 inst(), pW, pH, width, height);
237}
238void PaintPlugin::surfaceDestroyed() {
239 JNIEnv* env = NULL;
Derek Sollenbergerb8947ee2009-10-05 14:40:18 -0400240 if (m_surface && gVM->GetEnv((void**) &env, JNI_VERSION_1_4) == JNI_OK) {
Derek Sollenberger08581f12009-09-08 18:36:29 -0400241 env->DeleteGlobalRef(m_surface);
242 m_surface = NULL;
243 }
244}
245
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400246int16 PaintPlugin::handleEvent(const ANPEvent* evt) {
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400247 switch (evt->eventType) {
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400248 case kTouch_ANPEventType: {
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400249 float x = (float) evt->data.touch.x;
250 float y = (float) evt->data.touch.y;
251 if (kDown_ANPTouchAction == evt->data.touch.action && m_isTouchCurrentInput) {
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400252
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400253 ANPRectF* rect = validTouch(evt->data.touch.x, evt->data.touch.y);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400254 if(rect == &m_drawingSurface) {
255 m_isTouchActive = true;
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400256 gPathI.moveTo(m_touchPath, x, y);
257 paintTouch();
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400258 return 1;
259 }
260
261 } else if (kMove_ANPTouchAction == evt->data.touch.action && m_isTouchActive) {
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400262 gPathI.lineTo(m_touchPath, x, y);
263 paintTouch();
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400264 return 1;
265 } else if (kUp_ANPTouchAction == evt->data.touch.action && m_isTouchActive) {
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400266 gPathI.lineTo(m_touchPath, x, y);
267 paintTouch();
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400268 m_isTouchActive = false;
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400269 gPathI.reset(m_touchPath);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400270 return 1;
271 } else if (kCancel_ANPTouchAction == evt->data.touch.action) {
272 m_isTouchActive = false;
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400273 gPathI.reset(m_touchPath);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400274 return 1;
275 }
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400276
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400277 break;
278 }
279 case kMouse_ANPEventType: {
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400280
281 if (m_isTouchActive)
Derek Sollenbergere62ce172009-11-30 11:52:06 -0500282 gLogI.log(kError_ANPLogType, "----%p Received unintended mouse event", inst());
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400283
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400284 if (kDown_ANPMouseAction == evt->data.mouse.action) {
285 ANPRectF* rect = validTouch(evt->data.mouse.x, evt->data.mouse.y);
286 if (rect == &m_drawingSurface)
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400287 paintMouse(evt->data.mouse.x, evt->data.mouse.y);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400288 else if (rect == &m_inputToggle)
289 toggleInputMethod();
290 else if (rect == &m_colorToggle)
291 togglePaintColor();
292 else if (rect == &m_clearSurface)
293 drawCleanPlugin();
294 }
295 return 1;
296 }
297 default:
298 break;
299 }
300 return 0; // unknown or unhandled event
301}
302
303ANPRectF* PaintPlugin::validTouch(int x, int y) {
304
305 //convert to float
306 float fx = (int) x;
307 float fy = (int) y;
308
309 if (fx > m_drawingSurface.left && fx < m_drawingSurface.right && fy > m_drawingSurface.top && fy < m_drawingSurface.bottom)
310 return &m_drawingSurface;
311 else if (fx > m_inputToggle.left && fx < m_inputToggle.right && fy > m_inputToggle.top && fy < m_inputToggle.bottom)
312 return &m_inputToggle;
313 else if (fx > m_colorToggle.left && fx < m_colorToggle.right && fy > m_colorToggle.top && fy < m_colorToggle.bottom)
314 return &m_colorToggle;
315 else if (fx > m_clearSurface.left && fx < m_clearSurface.right && fy > m_clearSurface.top && fy < m_clearSurface.bottom)
316 return &m_clearSurface;
317 else
318 return NULL;
319}
320
321void PaintPlugin::toggleInputMethod() {
322 m_isTouchCurrentInput = !m_isTouchCurrentInput;
323
324 // lock only the input toggle and redraw the canvas
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400325 ANPCanvas* lockedCanvas = getCanvas(&m_inputToggle);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400326 drawCleanPlugin(lockedCanvas);
327}
328
329void PaintPlugin::togglePaintColor() {
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400330 if (m_activePaintColor == s_blueColor)
331 m_activePaintColor = s_redColor;
332 else if (m_activePaintColor == s_greenColor)
333 m_activePaintColor = s_blueColor;
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400334 else
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400335 m_activePaintColor = s_greenColor;
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400336
337 // lock only the color toggle and redraw the canvas
338 ANPCanvas* lockedCanvas = getCanvas(&m_colorToggle);
339 drawCleanPlugin(lockedCanvas);
340}
341
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400342void PaintPlugin::paintMouse(int x, int y) {
343 //TODO do not paint outside the drawing surface
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400344
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400345 //create the paint color
346 ANPPaint* fillPaint = gPaintI.newPaint();
347 gPaintI.setFlags(fillPaint, gPaintI.getFlags(fillPaint) | kAntiAlias_ANPPaintFlag);
348 gPaintI.setStyle(fillPaint, kFill_ANPPaintStyle);
349 gPaintI.setColor(fillPaint, m_activePaintColor);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400350
351 // handle the simple "mouse" paint (draw a point)
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400352 ANPRectF point;
353 point.left = (float) x-3;
354 point.top = (float) y-3;
355 point.right = (float) x+3;
356 point.bottom = (float) y+3;
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400357
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400358 // get a canvas that is only locked around the point and draw it
359 ANPCanvas* canvas = getCanvas(&point);
360 gCanvasI.drawOval(canvas, &point, fillPaint);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400361
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400362 // clean up
363 releaseCanvas(canvas);
364 gPaintI.deletePaint(fillPaint);
365}
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400366
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400367void PaintPlugin::paintTouch() {
368 //TODO do not paint outside the drawing surface
369
370 //create the paint color
371 ANPPaint* strokePaint = gPaintI.newPaint();
372 gPaintI.setFlags(strokePaint, gPaintI.getFlags(strokePaint) | kAntiAlias_ANPPaintFlag);
373 gPaintI.setColor(strokePaint, m_activePaintColor);
374 gPaintI.setStyle(strokePaint, kStroke_ANPPaintStyle);
375 gPaintI.setStrokeWidth(strokePaint, 6.0);
376 gPaintI.setStrokeCap(strokePaint, kRound_ANPPaintCap);
377 gPaintI.setStrokeJoin(strokePaint, kRound_ANPPaintJoin);
378
379 // handle the complex "touch" paint (draw a line)
380 ANPRectF bounds;
381 gPathI.getBounds(m_touchPath, &bounds);
382
383 // get a canvas that is only locked around the point and draw the path
384 ANPCanvas* canvas = getCanvas(&bounds);
385 gCanvasI.drawPath(canvas, m_touchPath, strokePaint);
386
387 // clean up
388 releaseCanvas(canvas);
389 gPaintI.deletePaint(strokePaint);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400390}