/* * Copyright 2009, The Android Open Source Project * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "PaintPlugin.h" #include <fcntl.h> #include <math.h> #include <string.h> extern NPNetscapeFuncs* browser; extern ANPLogInterfaceV0 gLogI; extern ANPCanvasInterfaceV0 gCanvasI; extern ANPPaintInterfaceV0 gPaintI; extern ANPPathInterfaceV0 gPathI; extern ANPSurfaceInterfaceV0 gSurfaceI; extern ANPTypefaceInterfaceV0 gTypefaceI; /////////////////////////////////////////////////////////////////////////////// PaintPlugin::PaintPlugin(NPP inst) : SurfaceSubPlugin(inst) { m_isTouchActive = false; m_isTouchCurrentInput = true; m_activePaintColor = s_redColor; memset(&m_drawingSurface, 0, sizeof(m_drawingSurface)); memset(&m_inputToggle, 0, sizeof(m_inputToggle)); memset(&m_colorToggle, 0, sizeof(m_colorToggle)); memset(&m_clearSurface, 0, sizeof(m_clearSurface)); // initialize the drawing surface m_surface = NULL; m_vm = NULL; // initialize the path m_touchPath = gPathI.newPath(); if(!m_touchPath) gLogI.log(inst, kError_ANPLogType, "----%p Unable to create the touch path", inst); // initialize the paint colors m_paintSurface = gPaintI.newPaint(); gPaintI.setFlags(m_paintSurface, gPaintI.getFlags(m_paintSurface) | kAntiAlias_ANPPaintFlag); gPaintI.setColor(m_paintSurface, 0xFFC0C0C0); gPaintI.setTextSize(m_paintSurface, 18); m_paintButton = gPaintI.newPaint(); gPaintI.setFlags(m_paintButton, gPaintI.getFlags(m_paintButton) | kAntiAlias_ANPPaintFlag); gPaintI.setColor(m_paintButton, 0xFFA8A8A8); // initialize the typeface (set the colors) ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle); gPaintI.setTypeface(m_paintSurface, tf); gTypefaceI.unref(tf); //register for touch events ANPEventFlags flags = kTouch_ANPEventFlag; NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags); if (err != NPERR_NO_ERROR) { gLogI.log(inst, kError_ANPLogType, "Error selecting input events."); } } PaintPlugin::~PaintPlugin() { gPathI.deletePath(m_touchPath); gPaintI.deletePaint(m_paintSurface); gPaintI.deletePaint(m_paintButton); surfaceDestroyed(); } bool PaintPlugin::supportsDrawingModel(ANPDrawingModel model) { return (model == kSurface_ANPDrawingModel); } ANPCanvas* PaintPlugin::getCanvas(ANPRectI* dirtyRect) { ANPBitmap bitmap; JNIEnv* env = NULL; if (!m_surface || m_vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK || !gSurfaceI.lock(env, m_surface, &bitmap, dirtyRect)) { return NULL; } ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap); // clip the canvas to the dirty rect b/c the surface is only required to // copy a minimum of the dirty rect and may copy more. The clipped canvas // however will never write to pixels outside of the clipped area. if (dirtyRect) { ANPRectF clipR; clipR.left = dirtyRect->left; clipR.top = dirtyRect->top; clipR.right = dirtyRect->right; clipR.bottom = dirtyRect->bottom; gCanvasI.clipRect(canvas, &clipR); } return canvas; } ANPCanvas* PaintPlugin::getCanvas(ANPRectF* dirtyRect) { ANPRectI newRect; newRect.left = (int) dirtyRect->left; newRect.top = (int) dirtyRect->top; newRect.right = (int) dirtyRect->right; newRect.bottom = (int) dirtyRect->bottom; return getCanvas(&newRect); } void PaintPlugin::releaseCanvas(ANPCanvas* canvas) { JNIEnv* env = NULL; if (m_surface && m_vm->GetEnv((void**) &env, JNI_VERSION_1_4) == JNI_OK) { gSurfaceI.unlock(env, m_surface); } gCanvasI.deleteCanvas(canvas); } void PaintPlugin::drawCleanPlugin(ANPCanvas* canvas) { NPP instance = this->inst(); PluginObject *obj = (PluginObject*) instance->pdata; // if no canvas get a locked canvas if (!canvas) canvas = getCanvas(); if (!canvas) return; const float buttonWidth = 60; const float buttonHeight = 30; const int W = obj->window->width; const int H = obj->window->height; // color the plugin canvas gCanvasI.drawColor(canvas, 0xFFCDCDCD); // get font metrics ANPFontMetrics fontMetrics; gPaintI.getFontMetrics(m_paintSurface, &fontMetrics); // draw the input toggle button m_inputToggle.left = 5; m_inputToggle.top = H - buttonHeight - 5; m_inputToggle.right = m_inputToggle.left + buttonWidth; m_inputToggle.bottom = m_inputToggle.top + buttonHeight; gCanvasI.drawRect(canvas, &m_inputToggle, m_paintButton); const char* inputText = m_isTouchCurrentInput ? "Touch" : "Mouse"; gCanvasI.drawText(canvas, inputText, strlen(inputText), m_inputToggle.left + 5, m_inputToggle.top - fontMetrics.fTop, m_paintSurface); // draw the color selector button m_colorToggle.left = (W/2) - (buttonWidth/2); m_colorToggle.top = H - buttonHeight - 5; m_colorToggle.right = m_colorToggle.left + buttonWidth; m_colorToggle.bottom = m_colorToggle.top + buttonHeight; gCanvasI.drawRect(canvas, &m_colorToggle, m_paintButton); const char* colorText = getColorText(); gCanvasI.drawText(canvas, colorText, strlen(colorText), m_colorToggle.left + 5, m_colorToggle.top - fontMetrics.fTop, m_paintSurface); // draw the clear canvas button m_clearSurface.left = W - buttonWidth - 5; m_clearSurface.top = H - buttonHeight - 5; m_clearSurface.right = m_clearSurface.left + buttonWidth; m_clearSurface.bottom = m_clearSurface.top + buttonHeight; gCanvasI.drawRect(canvas, &m_clearSurface, m_paintButton); const char* clearText = "Clear"; gCanvasI.drawText(canvas, clearText, strlen(clearText), m_clearSurface.left + 5, m_clearSurface.top - fontMetrics.fTop, m_paintSurface); // draw the drawing surface box (5 px from the edge) m_drawingSurface.left = 5; m_drawingSurface.top = 5; m_drawingSurface.right = W - 5; m_drawingSurface.bottom = m_colorToggle.top - 5; gCanvasI.drawRect(canvas, &m_drawingSurface, m_paintSurface); // release the canvas releaseCanvas(canvas); } const char* PaintPlugin::getColorText() { if (m_activePaintColor == s_blueColor) return "Blue"; else if (m_activePaintColor == s_greenColor) return "Green"; else return "Red"; } bool PaintPlugin::isFixedSurface() { return true; } void PaintPlugin::surfaceCreated(JNIEnv* env, jobject surface) { env->GetJavaVM(&m_vm); m_surface = env->NewGlobalRef(surface); drawCleanPlugin(); } void PaintPlugin::surfaceChanged(int format, int width, int height) { // get the plugin's dimensions according to the DOM PluginObject *obj = (PluginObject*) inst()->pdata; const int pW = obj->window->width; const int pH = obj->window->height; // compare to the plugin's surface dimensions if (pW != width || pH != height) gLogI.log(inst(), kError_ANPLogType, "----%p Invalid Surface Dimensions (%d,%d):(%d,%d)", inst(), pW, pH, width, height); } void PaintPlugin::surfaceDestroyed() { JNIEnv* env = NULL; if (m_surface && m_vm->GetEnv((void**) &env, JNI_VERSION_1_4) == JNI_OK) { env->DeleteGlobalRef(m_surface); m_surface = NULL; } } int16 PaintPlugin::handleEvent(const ANPEvent* evt) { switch (evt->eventType) { case kTouch_ANPEventType: { float x = (float) evt->data.touch.x; float y = (float) evt->data.touch.y; if (kDown_ANPTouchAction == evt->data.touch.action && m_isTouchCurrentInput) { ANPRectF* rect = validTouch(evt->data.touch.x, evt->data.touch.y); if(rect == &m_drawingSurface) { m_isTouchActive = true; gPathI.moveTo(m_touchPath, x, y); paintTouch(); return 1; } } else if (kMove_ANPTouchAction == evt->data.touch.action && m_isTouchActive) { gPathI.lineTo(m_touchPath, x, y); paintTouch(); return 1; } else if (kUp_ANPTouchAction == evt->data.touch.action && m_isTouchActive) { gPathI.lineTo(m_touchPath, x, y); paintTouch(); m_isTouchActive = false; gPathI.reset(m_touchPath); return 1; } else if (kCancel_ANPTouchAction == evt->data.touch.action) { m_isTouchActive = false; gPathI.reset(m_touchPath); return 1; } break; } case kMouse_ANPEventType: { if (m_isTouchActive) gLogI.log(inst(), kError_ANPLogType, "----%p Received unintended mouse event", inst()); if (kDown_ANPMouseAction == evt->data.mouse.action) { ANPRectF* rect = validTouch(evt->data.mouse.x, evt->data.mouse.y); if (rect == &m_drawingSurface) paintMouse(evt->data.mouse.x, evt->data.mouse.y); else if (rect == &m_inputToggle) toggleInputMethod(); else if (rect == &m_colorToggle) togglePaintColor(); else if (rect == &m_clearSurface) drawCleanPlugin(); } return 1; } default: break; } return 0; // unknown or unhandled event } ANPRectF* PaintPlugin::validTouch(int x, int y) { //convert to float float fx = (int) x; float fy = (int) y; if (fx > m_drawingSurface.left && fx < m_drawingSurface.right && fy > m_drawingSurface.top && fy < m_drawingSurface.bottom) return &m_drawingSurface; else if (fx > m_inputToggle.left && fx < m_inputToggle.right && fy > m_inputToggle.top && fy < m_inputToggle.bottom) return &m_inputToggle; else if (fx > m_colorToggle.left && fx < m_colorToggle.right && fy > m_colorToggle.top && fy < m_colorToggle.bottom) return &m_colorToggle; else if (fx > m_clearSurface.left && fx < m_clearSurface.right && fy > m_clearSurface.top && fy < m_clearSurface.bottom) return &m_clearSurface; else return NULL; } void PaintPlugin::toggleInputMethod() { m_isTouchCurrentInput = !m_isTouchCurrentInput; // lock only the input toggle and redraw the canvas ANPCanvas* lockedCanvas = getCanvas(&m_inputToggle); drawCleanPlugin(lockedCanvas); } void PaintPlugin::togglePaintColor() { if (m_activePaintColor == s_blueColor) m_activePaintColor = s_redColor; else if (m_activePaintColor == s_greenColor) m_activePaintColor = s_blueColor; else m_activePaintColor = s_greenColor; // lock only the color toggle and redraw the canvas ANPCanvas* lockedCanvas = getCanvas(&m_colorToggle); drawCleanPlugin(lockedCanvas); } void PaintPlugin::paintMouse(int x, int y) { //TODO do not paint outside the drawing surface //create the paint color ANPPaint* fillPaint = gPaintI.newPaint(); gPaintI.setFlags(fillPaint, gPaintI.getFlags(fillPaint) | kAntiAlias_ANPPaintFlag); gPaintI.setStyle(fillPaint, kFill_ANPPaintStyle); gPaintI.setColor(fillPaint, m_activePaintColor); // handle the simple "mouse" paint (draw a point) ANPRectF point; point.left = (float) x-3; point.top = (float) y-3; point.right = (float) x+3; point.bottom = (float) y+3; // get a canvas that is only locked around the point and draw it ANPCanvas* canvas = getCanvas(&point); gCanvasI.drawOval(canvas, &point, fillPaint); // clean up releaseCanvas(canvas); gPaintI.deletePaint(fillPaint); } void PaintPlugin::paintTouch() { //TODO do not paint outside the drawing surface //create the paint color ANPPaint* strokePaint = gPaintI.newPaint(); gPaintI.setFlags(strokePaint, gPaintI.getFlags(strokePaint) | kAntiAlias_ANPPaintFlag); gPaintI.setColor(strokePaint, m_activePaintColor); gPaintI.setStyle(strokePaint, kStroke_ANPPaintStyle); gPaintI.setStrokeWidth(strokePaint, 6.0); gPaintI.setStrokeCap(strokePaint, kRound_ANPPaintCap); gPaintI.setStrokeJoin(strokePaint, kRound_ANPPaintJoin); // handle the complex "touch" paint (draw a line) ANPRectF bounds; gPathI.getBounds(m_touchPath, &bounds); // get a canvas that is only locked around the point and draw the path ANPCanvas* canvas = getCanvas(&bounds); gCanvasI.drawPath(canvas, m_touchPath, strokePaint); // clean up releaseCanvas(canvas); gPaintI.deletePaint(strokePaint); }