1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.egg.paint 18 19 import java.util.LinkedList 20 21 import android.view.MotionEvent 22 23 class SpotFilter(internal var mBufSize: Int, posDecay: Float, pressureDecay: Float, internal var mPlotter: Plotter) { 24 val spots = LinkedList<MotionEvent.PointerCoords>() // newest at front 25 val tmpSpot = MotionEvent.PointerCoords() 26 var lastTool = MotionEvent.TOOL_TYPE_UNKNOWN 27 28 val posDecay: Float 29 val pressureDecay: Float 30 31 interface Plotter { plotnull32 fun plot(s: MotionEvent.PointerCoords) 33 } 34 35 init { 36 this.posDecay = if (posDecay in 0f..1f) posDecay else 1f 37 this.pressureDecay = if (pressureDecay in 0f..1f) pressureDecay else 1f 38 } 39 filterIntonull40 fun filterInto(out: MotionEvent.PointerCoords, tool: Int): MotionEvent.PointerCoords { 41 lastTool = tool 42 43 var wi = 1f // weight for ith component (position) 44 var w = 0f // total weight 45 var wi_press = 1f // weight for ith component (pressure) 46 var w_press = 0f // total weight (pressure) 47 48 var x = 0f 49 var y = 0f 50 var pressure = 0f 51 var size = 0f 52 for (pi in spots) { 53 x += pi.x * wi 54 y += pi.y * wi 55 56 pressure += pi.pressure * wi_press 57 size += pi.size * wi_press 58 59 w += wi 60 wi *= posDecay // exponential backoff 61 62 w_press += wi_press 63 wi_press *= pressureDecay 64 65 if (PRECISE_STYLUS_INPUT && tool == MotionEvent.TOOL_TYPE_STYLUS) { 66 // just take the newest one, no need to average 67 break 68 } 69 } 70 71 out.x = x / w 72 out.y = y / w 73 out.pressure = pressure / w_press 74 out.size = size / w_press 75 return out 76 } 77 addInternalnull78 protected fun addInternal(c: MotionEvent.PointerCoords, tool: Int) { 79 val coord = 80 if (spots.size == mBufSize) { 81 spots.removeLast() 82 } else { 83 MotionEvent.PointerCoords() 84 } 85 coord.copyFrom(c) 86 87 spots.add(0, coord) 88 89 filterInto(tmpSpot, tool) 90 mPlotter.plot(tmpSpot) 91 } 92 addnull93 fun add(cv: List<MotionEvent.PointerCoords>, tool: Int) { 94 for (c in cv) { 95 addInternal(c, tool) 96 } 97 } 98 addnull99 fun add(evt: MotionEvent) { 100 val tool = evt.getToolType(0) 101 for (i in 0 until evt.historySize) { 102 evt.getHistoricalPointerCoords(0, i, tmpSpot) 103 addInternal(tmpSpot, tool) 104 } 105 evt.getPointerCoords(0, tmpSpot) 106 addInternal(tmpSpot, tool) 107 } 108 finishnull109 fun finish() { 110 while (spots.size > 0) { 111 filterInto(tmpSpot, lastTool) 112 spots.removeLast() 113 mPlotter.plot(tmpSpot) 114 } 115 116 spots.clear() 117 } 118 119 companion object { 120 var PRECISE_STYLUS_INPUT = true 121 } 122 } 123 124 125