blob: 80f05e4b8219ec80ac81c4d2e7252de89c9927c5 [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
53 // initialize the drawing surface
Derek Sollenberger08581f12009-09-08 18:36:29 -040054 m_surface = NULL;
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040055
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -040056 // initialize the path
57 m_touchPath = gPathI.newPath();
58 if(!m_touchPath)
Derek Sollenbergere62ce172009-11-30 11:52:06 -050059 gLogI.log(kError_ANPLogType, "----%p Unable to create the touch path", inst);
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -040060
61 // initialize the paint colors
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040062 m_paintSurface = gPaintI.newPaint();
63 gPaintI.setFlags(m_paintSurface, gPaintI.getFlags(m_paintSurface) | kAntiAlias_ANPPaintFlag);
64 gPaintI.setColor(m_paintSurface, 0xFFC0C0C0);
65 gPaintI.setTextSize(m_paintSurface, 18);
66
67 m_paintButton = gPaintI.newPaint();
68 gPaintI.setFlags(m_paintButton, gPaintI.getFlags(m_paintButton) | kAntiAlias_ANPPaintFlag);
69 gPaintI.setColor(m_paintButton, 0xFFA8A8A8);
70
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -040071 // initialize the typeface (set the colors)
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040072 ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
73 gPaintI.setTypeface(m_paintSurface, tf);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040074 gTypefaceI.unref(tf);
75
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040076 //register for touch events
77 ANPEventFlags flags = kTouch_ANPEventFlag;
78 NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags);
79 if (err != NPERR_NO_ERROR) {
Derek Sollenbergere62ce172009-11-30 11:52:06 -050080 gLogI.log(kError_ANPLogType, "Error selecting input events.");
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040081 }
82}
83
84PaintPlugin::~PaintPlugin() {
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -040085 gPathI.deletePath(m_touchPath);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040086 gPaintI.deletePaint(m_paintSurface);
87 gPaintI.deletePaint(m_paintButton);
Derek Sollenberger08581f12009-09-08 18:36:29 -040088 surfaceDestroyed();
Derek Sollenberger4fb83e62009-07-27 16:40:13 -040089}
90
91bool PaintPlugin::supportsDrawingModel(ANPDrawingModel model) {
92 return (model == kSurface_ANPDrawingModel);
93}
94
95ANPCanvas* PaintPlugin::getCanvas(ANPRectI* dirtyRect) {
96
97 ANPBitmap bitmap;
Derek Sollenberger08581f12009-09-08 18:36:29 -040098 JNIEnv* env = NULL;
Derek Sollenbergerb8947ee2009-10-05 14:40:18 -040099 if (!m_surface || gVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK ||
Derek Sollenberger08581f12009-09-08 18:36:29 -0400100 !gSurfaceI.lock(env, m_surface, &bitmap, dirtyRect)) {
101 return NULL;
102 }
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400103
104 ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
105
106 // clip the canvas to the dirty rect b/c the surface is only required to
107 // copy a minimum of the dirty rect and may copy more. The clipped canvas
108 // however will never write to pixels outside of the clipped area.
109 if (dirtyRect) {
110 ANPRectF clipR;
111 clipR.left = dirtyRect->left;
112 clipR.top = dirtyRect->top;
113 clipR.right = dirtyRect->right;
114 clipR.bottom = dirtyRect->bottom;
115 gCanvasI.clipRect(canvas, &clipR);
116 }
117
118 return canvas;
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400119}
120
121ANPCanvas* PaintPlugin::getCanvas(ANPRectF* dirtyRect) {
122
123 ANPRectI newRect;
124 newRect.left = (int) dirtyRect->left;
125 newRect.top = (int) dirtyRect->top;
126 newRect.right = (int) dirtyRect->right;
127 newRect.bottom = (int) dirtyRect->bottom;
128
129 return getCanvas(&newRect);
130}
131
132void PaintPlugin::releaseCanvas(ANPCanvas* canvas) {
Derek Sollenberger08581f12009-09-08 18:36:29 -0400133 JNIEnv* env = NULL;
Derek Sollenbergerb8947ee2009-10-05 14:40:18 -0400134 if (m_surface && gVM->GetEnv((void**) &env, JNI_VERSION_1_4) == JNI_OK) {
Derek Sollenberger08581f12009-09-08 18:36:29 -0400135 gSurfaceI.unlock(env, m_surface);
136 }
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400137 gCanvasI.deleteCanvas(canvas);
138}
139
140void PaintPlugin::drawCleanPlugin(ANPCanvas* canvas) {
141 NPP instance = this->inst();
142 PluginObject *obj = (PluginObject*) instance->pdata;
143
144 // if no canvas get a locked canvas
145 if (!canvas)
146 canvas = getCanvas();
147
148 if (!canvas)
149 return;
150
151 const float buttonWidth = 60;
152 const float buttonHeight = 30;
153 const int W = obj->window->width;
154 const int H = obj->window->height;
155
156 // color the plugin canvas
157 gCanvasI.drawColor(canvas, 0xFFCDCDCD);
158
159 // get font metrics
160 ANPFontMetrics fontMetrics;
161 gPaintI.getFontMetrics(m_paintSurface, &fontMetrics);
162
163 // draw the input toggle button
164 m_inputToggle.left = 5;
165 m_inputToggle.top = H - buttonHeight - 5;
166 m_inputToggle.right = m_inputToggle.left + buttonWidth;
167 m_inputToggle.bottom = m_inputToggle.top + buttonHeight;
168 gCanvasI.drawRect(canvas, &m_inputToggle, m_paintButton);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400169 const char* inputText = m_isTouchCurrentInput ? "Touch" : "Mouse";
170 gCanvasI.drawText(canvas, inputText, strlen(inputText), m_inputToggle.left + 5,
171 m_inputToggle.top - fontMetrics.fTop, m_paintSurface);
172
173 // draw the color selector button
174 m_colorToggle.left = (W/2) - (buttonWidth/2);
175 m_colorToggle.top = H - buttonHeight - 5;
176 m_colorToggle.right = m_colorToggle.left + buttonWidth;
177 m_colorToggle.bottom = m_colorToggle.top + buttonHeight;
178 gCanvasI.drawRect(canvas, &m_colorToggle, m_paintButton);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400179 const char* colorText = getColorText();
180 gCanvasI.drawText(canvas, colorText, strlen(colorText), m_colorToggle.left + 5,
181 m_colorToggle.top - fontMetrics.fTop, m_paintSurface);
182
183 // draw the clear canvas button
184 m_clearSurface.left = W - buttonWidth - 5;
185 m_clearSurface.top = H - buttonHeight - 5;
186 m_clearSurface.right = m_clearSurface.left + buttonWidth;
187 m_clearSurface.bottom = m_clearSurface.top + buttonHeight;
188 gCanvasI.drawRect(canvas, &m_clearSurface, m_paintButton);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400189 const char* clearText = "Clear";
190 gCanvasI.drawText(canvas, clearText, strlen(clearText), m_clearSurface.left + 5,
191 m_clearSurface.top - fontMetrics.fTop, m_paintSurface);
192
193 // draw the drawing surface box (5 px from the edge)
194 m_drawingSurface.left = 5;
195 m_drawingSurface.top = 5;
196 m_drawingSurface.right = W - 5;
197 m_drawingSurface.bottom = m_colorToggle.top - 5;
198 gCanvasI.drawRect(canvas, &m_drawingSurface, m_paintSurface);
199
200 // release the canvas
201 releaseCanvas(canvas);
202}
203
204const char* PaintPlugin::getColorText() {
205
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400206 if (m_activePaintColor == s_blueColor)
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400207 return "Blue";
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400208 else if (m_activePaintColor == s_greenColor)
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400209 return "Green";
210 else
211 return "Red";
212}
213
Derek Sollenberger08581f12009-09-08 18:36:29 -0400214bool PaintPlugin::isFixedSurface() {
215 return true;
216}
217
Derek Sollenbergerb8947ee2009-10-05 14:40:18 -0400218void PaintPlugin::surfaceCreated(jobject surface) {
219 m_surface = surface;
Derek Sollenberger08581f12009-09-08 18:36:29 -0400220 drawCleanPlugin();
221}
222
223void PaintPlugin::surfaceChanged(int format, int width, int height) {
224 // get the plugin's dimensions according to the DOM
225 PluginObject *obj = (PluginObject*) inst()->pdata;
226 const int pW = obj->window->width;
227 const int pH = obj->window->height;
228 // compare to the plugin's surface dimensions
229 if (pW != width || pH != height)
Derek Sollenbergere62ce172009-11-30 11:52:06 -0500230 gLogI.log(kError_ANPLogType,
Derek Sollenberger08581f12009-09-08 18:36:29 -0400231 "----%p Invalid Surface Dimensions (%d,%d):(%d,%d)",
232 inst(), pW, pH, width, height);
233}
234void PaintPlugin::surfaceDestroyed() {
235 JNIEnv* env = NULL;
Derek Sollenbergerb8947ee2009-10-05 14:40:18 -0400236 if (m_surface && gVM->GetEnv((void**) &env, JNI_VERSION_1_4) == JNI_OK) {
Derek Sollenberger08581f12009-09-08 18:36:29 -0400237 env->DeleteGlobalRef(m_surface);
238 m_surface = NULL;
239 }
240}
241
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400242int16 PaintPlugin::handleEvent(const ANPEvent* evt) {
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400243 switch (evt->eventType) {
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400244 case kTouch_ANPEventType: {
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400245 float x = (float) evt->data.touch.x;
246 float y = (float) evt->data.touch.y;
247 if (kDown_ANPTouchAction == evt->data.touch.action && m_isTouchCurrentInput) {
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400248
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400249 ANPRectF* rect = validTouch(evt->data.touch.x, evt->data.touch.y);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400250 if(rect == &m_drawingSurface) {
251 m_isTouchActive = true;
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400252 gPathI.moveTo(m_touchPath, x, y);
253 paintTouch();
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400254 return 1;
255 }
256
257 } else if (kMove_ANPTouchAction == evt->data.touch.action && m_isTouchActive) {
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400258 gPathI.lineTo(m_touchPath, x, y);
259 paintTouch();
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400260 return 1;
261 } else if (kUp_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 m_isTouchActive = false;
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400265 gPathI.reset(m_touchPath);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400266 return 1;
267 } else if (kCancel_ANPTouchAction == evt->data.touch.action) {
268 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 }
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400272
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400273 break;
274 }
275 case kMouse_ANPEventType: {
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400276
277 if (m_isTouchActive)
Derek Sollenbergere62ce172009-11-30 11:52:06 -0500278 gLogI.log(kError_ANPLogType, "----%p Received unintended mouse event", inst());
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400279
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400280 if (kDown_ANPMouseAction == evt->data.mouse.action) {
281 ANPRectF* rect = validTouch(evt->data.mouse.x, evt->data.mouse.y);
282 if (rect == &m_drawingSurface)
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400283 paintMouse(evt->data.mouse.x, evt->data.mouse.y);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400284 else if (rect == &m_inputToggle)
285 toggleInputMethod();
286 else if (rect == &m_colorToggle)
287 togglePaintColor();
288 else if (rect == &m_clearSurface)
289 drawCleanPlugin();
290 }
291 return 1;
292 }
293 default:
294 break;
295 }
296 return 0; // unknown or unhandled event
297}
298
299ANPRectF* PaintPlugin::validTouch(int x, int y) {
300
301 //convert to float
302 float fx = (int) x;
303 float fy = (int) y;
304
305 if (fx > m_drawingSurface.left && fx < m_drawingSurface.right && fy > m_drawingSurface.top && fy < m_drawingSurface.bottom)
306 return &m_drawingSurface;
307 else if (fx > m_inputToggle.left && fx < m_inputToggle.right && fy > m_inputToggle.top && fy < m_inputToggle.bottom)
308 return &m_inputToggle;
309 else if (fx > m_colorToggle.left && fx < m_colorToggle.right && fy > m_colorToggle.top && fy < m_colorToggle.bottom)
310 return &m_colorToggle;
311 else if (fx > m_clearSurface.left && fx < m_clearSurface.right && fy > m_clearSurface.top && fy < m_clearSurface.bottom)
312 return &m_clearSurface;
313 else
314 return NULL;
315}
316
317void PaintPlugin::toggleInputMethod() {
318 m_isTouchCurrentInput = !m_isTouchCurrentInput;
319
320 // lock only the input toggle and redraw the canvas
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400321 ANPCanvas* lockedCanvas = getCanvas(&m_inputToggle);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400322 drawCleanPlugin(lockedCanvas);
323}
324
325void PaintPlugin::togglePaintColor() {
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400326 if (m_activePaintColor == s_blueColor)
327 m_activePaintColor = s_redColor;
328 else if (m_activePaintColor == s_greenColor)
329 m_activePaintColor = s_blueColor;
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400330 else
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400331 m_activePaintColor = s_greenColor;
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400332
333 // lock only the color toggle and redraw the canvas
334 ANPCanvas* lockedCanvas = getCanvas(&m_colorToggle);
335 drawCleanPlugin(lockedCanvas);
336}
337
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400338void PaintPlugin::paintMouse(int x, int y) {
339 //TODO do not paint outside the drawing surface
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400340
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400341 //create the paint color
342 ANPPaint* fillPaint = gPaintI.newPaint();
343 gPaintI.setFlags(fillPaint, gPaintI.getFlags(fillPaint) | kAntiAlias_ANPPaintFlag);
344 gPaintI.setStyle(fillPaint, kFill_ANPPaintStyle);
345 gPaintI.setColor(fillPaint, m_activePaintColor);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400346
347 // handle the simple "mouse" paint (draw a point)
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400348 ANPRectF point;
349 point.left = (float) x-3;
350 point.top = (float) y-3;
351 point.right = (float) x+3;
352 point.bottom = (float) y+3;
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400353
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400354 // get a canvas that is only locked around the point and draw it
355 ANPCanvas* canvas = getCanvas(&point);
356 gCanvasI.drawOval(canvas, &point, fillPaint);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400357
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400358 // clean up
359 releaseCanvas(canvas);
360 gPaintI.deletePaint(fillPaint);
361}
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400362
Derek Sollenbergerb6f5cd22009-07-28 14:09:50 -0400363void PaintPlugin::paintTouch() {
364 //TODO do not paint outside the drawing surface
365
366 //create the paint color
367 ANPPaint* strokePaint = gPaintI.newPaint();
368 gPaintI.setFlags(strokePaint, gPaintI.getFlags(strokePaint) | kAntiAlias_ANPPaintFlag);
369 gPaintI.setColor(strokePaint, m_activePaintColor);
370 gPaintI.setStyle(strokePaint, kStroke_ANPPaintStyle);
371 gPaintI.setStrokeWidth(strokePaint, 6.0);
372 gPaintI.setStrokeCap(strokePaint, kRound_ANPPaintCap);
373 gPaintI.setStrokeJoin(strokePaint, kRound_ANPPaintJoin);
374
375 // handle the complex "touch" paint (draw a line)
376 ANPRectF bounds;
377 gPathI.getBounds(m_touchPath, &bounds);
378
379 // get a canvas that is only locked around the point and draw the path
380 ANPCanvas* canvas = getCanvas(&bounds);
381 gCanvasI.drawPath(canvas, m_touchPath, strokePaint);
382
383 // clean up
384 releaseCanvas(canvas);
385 gPaintI.deletePaint(strokePaint);
Derek Sollenberger4fb83e62009-07-27 16:40:13 -0400386}