使用C++在andrdoid上画贝塞尔曲线
最近一直在学习在android上做画图板,不是使用android语言画的,使用的是纯C++,有用C++封装的input子系统触摸事件,还有canvas相结合的C++画图板。
效果如下:
好多没有优化,代码粗糙仅供阅读。上C++代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cutils/memory.h> 2 #include <unistd.h> 3 #include <utils/Log.h> 4 #include <binder/IPCThreadState.h> 5 #include <binder/ProcessState.h> 6 #include <binder/IServiceManager.h> 7 8 #include "SkRect.h" 9 #include "SkTypeface.h" 10 #include "Surface.h" 11 #include "SkPaint.h" 12 #include <gui/SurfaceComposerClient.h> 13 #include <gui/ISurfaceComposer.h> 14 #include <ui/DisplayInfo.h> 15 #include <ui/Rect.h> 16 #include <ui/Region.h> 17 #include <android/native_window.h> 18 #include <SkGraphics.h> 19 #include <SkBitmap.h> 20 #include <SkCanvas.h> 21 #include <SkDevice.h> 22 #include <SkStream.h> 23 #include <SkImageDecoder.h> 24 #include <hardware/hwcomposer_defs.h> 25 26 #include <linux/input.h> 27 #include <stdio.h> 28 #include <pthread.h> 29 #include <stdlib.h> 30 #include <sys/types.h> 31 #include <string.h> 32 #include <sys/stat.h> 33 #include <fcntl.h> 34 #include <linux/fb.h> 35 #include <sys/ioctl.h> 36 37 //LCD和触摸框转换比例 38 float xScale;// = 1920.0/32768; 39 float yScale;// = 1080.0/32768; 40 41 int fd ; // dev的文件描述符 42 int fds[2]; //管道 43 44 float x; 45 float y; 46 int pressure; 47 float touchMajor; 48 float touchMinor; 49 int down,up; 50 51 using namespace android; 52 53 static SkBitmap::Config convertPixelFormat(PixelFormat format) { 54 /* note: if PIXEL_FORMAT_RGBX_8888 means that all alpha bytes are 0xFF, then 55 we can map to SkBitmap::kARGB_8888_Config, and optionally call 56 bitmap.setIsOpaque(true) on the resulting SkBitmap (as an accelerator) 57 */ 58 switch (format) { 59 case PIXEL_FORMAT_RGBX_8888: return SkBitmap::kARGB_8888_Config; 60 case PIXEL_FORMAT_RGBA_8888: return SkBitmap::kARGB_8888_Config; 61 case PIXEL_FORMAT_RGBA_4444: return SkBitmap::kARGB_4444_Config; 62 case PIXEL_FORMAT_RGB_565: return SkBitmap::kRGB_565_Config; 63 case PIXEL_FORMAT_A_8: return SkBitmap::kA8_Config; 64 default: return SkBitmap::kNo_Config; 65 } 66 } 67 68 int xScaleyScale(void) 69 { 70 sp<SurfaceComposerClient> surfaceComposerClient = new SurfaceComposerClient(); //创建SurfaceFlinger的本地代理 71 DisplayInfo displayInfo; 72 //获取屏幕的宽高等信息 73 surfaceComposerClient->getDisplayInfo(surfaceComposerClient->getBuiltInDisplay(HWC_DISPLAY_PRIMARY), &displayInfo); 74 xScale = displayInfo.w / 32768.0; 75 yScale = displayInfo.h / 32768.0; 76 77 /* 78 struct fb_var_screeninfo vinfo; 79 // struct fb_fix_screeninfo finfo; 80 81 int fd0 = open("/dev/graphics/fb0", O_RDWR); 82 if(fd0 < 0) 83 { 84 perror("open"); 85 exit(1); 86 } 87 // Get variable screen information 88 if (ioctl(fd0, FBIOGET_VSCREENINFO, &vinfo)) 89 { 90 printf("Error reading variable information.\n"); 91 exit(1); 92 } 93 xScale = vinfo.xres / 32768; 94 yScale = vinfo.yres / 32768; 95 // printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel); 96 close(fd0); 97 */ 98 99 return 0; 100 } 101 102 static int getTouchEventNum() //判断触摸框事件是哪一个event 103 { 104 char name[64]; /* RATS: Use ok, but could be better */ 105 char buf[256] = { 0, }; /* RATS: Use ok */ 106 int fd = 0; 107 int i; 108 for (i = 0; i < 32; i++) 109 { 110 sprintf(name, "/dev/input/event%d", i); 111 if ((fd = open(name, O_RDONLY, 0)) >= 0) 112 { 113 ioctl(fd, EVIOCGNAME(sizeof(buf)), buf); 114 if(strstr(buf, "MTOUC Touch")) 115 { 116 close(fd); 117 return i; 118 } 119 //printf("%s\n", name); 120 //printf("name: %s\n", buf); 121 close(fd); 122 } 123 } 124 return -1; 125 } 126 127 static void* readInput(void *data) 128 { 129 struct input_event inputEvent; 130 while(1) 131 { 132 read(fd, &inputEvent, sizeof(struct input_event)); 133 // 向管道中写数据 134 write(fds[1], &inputEvent, sizeof(struct input_event)); 135 } 136 return NULL; 137 } 138 139 static void* dispatchInput(void *data) 140 { 141 struct input_event inputEvent; 142 int flag = 1; 143 while(1) 144 { 145 //从管道中读取数据 146 read(fds[0], &inputEvent, sizeof(struct input_event)); 147 148 if(inputEvent.type == EV_ABS && inputEvent.code == ABS_X ){ 149 float fv = inputEvent.value * 1.0; 150 x = fv * xScale; 151 continue; 152 } 153 if(inputEvent.type == EV_ABS && inputEvent.code == ABS_Y ){ 154 float fv = inputEvent.value * 1.0; 155 y = fv * yScale; 156 continue; 157 } 158 if(inputEvent.type == EV_KEY && inputEvent.code == BTN_TOUCH ){ 159 pressure = inputEvent.value; 160 if(1 == pressure ) 161 { 162 down = 1; 163 up = 0; 164 } 165 else if(0 == pressure) 166 { 167 up = 1; 168 down = 0; 169 } 170 continue; 171 } 172 //增加flag的判断作用是touchMajor和toushMinor事件在pressure事件之前的比较准确 173 if(inputEvent.type == EV_ABS && inputEvent.code == ABS_MT_TOUCH_MAJOR /*&& flag*/ ){ 174 float fv = inputEvent.value * 1.0; 175 touchMajor = fv * 10; //10 = 0x4000 / 1920 176 continue; 177 } 178 if(inputEvent.type == EV_ABS && inputEvent.code == ABS_MT_TOUCH_MINOR /*&& flag */){ 179 float fv = inputEvent.value * 1.0; 180 touchMinor = fv * 15; //18 15 = 0x4000 / 1080; 181 continue; 182 } 183 } 184 return NULL; 185 } 186 187 int initInput(void) 188 { 189 int num = getTouchEventNum(); 190 if( num == -1) 191 { 192 printf("No Touch Event\n"); 193 return -1; 194 } 195 char name[64]; 196 sprintf(name, "/dev/input/event%d", num); 197 fd = open(name, O_RDWR); 198 if(fd < 0) 199 { 200 //LOGI("Open dev Error"); 201 return fd; 202 } 203 204 xScaleyScale();//初始化xy坐标的比例关系 205 206 //创建无名管道 207 if(-1 == pipe(fds)) 208 { 209 printf("pipe\n"); 210 exit(-1); 211 } 212 213 pthread_t readId, disPatchId; 214 pthread_create(&readId, NULL, readInput, NULL); 215 ///sleep(1); 216 pthread_create(&disPatchId, NULL, dispatchInput, NULL); 217 218 return fd; 219 } 220 221 float getX(void) 222 { 223 return x; 224 } 225 226 float getY(void) 227 { 228 return y; 229 } 230 231 float getW(void) 232 { 233 return touchMajor; 234 } 235 236 float getH(void) 237 { 238 return touchMinor; 239 } 240 241 int getDown(void) 242 { 243 return down; 244 } 245 246 int getUp(void) 247 { 248 return up; 249 } 250 251 typedef struct 252 { 253 float x; 254 float y; 255 } Point2D; 256 /* cp 在此是四个元素的数组: 257 cp[0] 为起点,或上图中的 P0 258 cp[1] 为第一控制点,或上图中的 P1 259 cp[2] 为第二控制点,或上图中的 P2 260 cp[3] 为结束点,或上图中的 P3 261 t 为参数值,0 <= t <= 1 */ 262 Point2D PointOnCubicBezier( Point2D* cp, float t ) 263 { 264 float ax, bx, cx; float ay, by, cy; 265 float tSquared, tCubed; Point2D result; 266 /* 计算多项式系数 */ 267 cx = 3.0 * (cp[1].x - cp[0].x); 268 bx = 3.0 * (cp[2].x - cp[1].x) - cx; 269 ax = cp[3].x - cp[0].x - cx - bx; 270 cy = 3.0 * (cp[1].y - cp[0].y); 271 by = 3.0 * (cp[2].y - cp[1].y) - cy; 272 ay = cp[3].y - cp[0].y - cy - by; 273 /* 计算t位置的点值 */ 274 tSquared = t * t; 275 tCubed = tSquared * t; 276 result.x = (ax * tCubed) + (bx * tSquared) + (cx * t) + cp[0].x; 277 result.y = (ay * tCubed) + (by * tSquared) + (cy * t) + cp[0].y; 278 return result; 279 } 280 /* ComputeBezier 以控制点 cp 所产生的曲线点,填入 Point2D 结构数组。 281 调用方必须分配足够的空间以供输出,<sizeof(Point2D) numberOfPoints> */ 282 void ComputeBezier( Point2D* cp, int numberOfPoints, Point2D* curve ) 283 { 284 float dt; int i; 285 dt = 1.0 / ( numberOfPoints - 1 ); 286 for( i = 0; i < numberOfPoints; i++) 287 curve[i] = PointOnCubicBezier( cp, i*dt ); 288 } 289 290 int main(int argc, char** argv) 291 { 292 // set up the thread-pool 293 //sp<ProcessState> proc(ProcessState::self()); 294 //ProcessState::self()->startThreadPool(); 295 296 initInput(); 297 298 // create a client to surfaceflinger 299 sp<SurfaceComposerClient> client = new SurfaceComposerClient(); //创建SurfaceFlinger的本地代理 300 301 DisplayInfo display; 302 //获取屏幕的宽高等信息 303 client->getDisplayInfo(client->getBuiltInDisplay(HWC_DISPLAY_PRIMARY), &display); 304 char str[80]; 305 sprintf(str,"w=%d,h=%d,xdpi=%f,ydpi=%f,fps=%f,ds=%f,fmt=%d\n", 306 display.w, display.h, display.xdpi, display.ydpi, 307 display.fps, display.density, display.pixelFormatInfo.format); 308 //创建SurfaceControl的本地代理 309 sp<SurfaceControl> surfaceControl = client->createSurface(String8("testsurface"), 310 1920/*240*/, 1080/*160*/, PIXEL_FORMAT_RGBA_8888, 0); 311 312 /*********************************************************************/ 313 SurfaceComposerClient::openGlobalTransaction(); 314 surfaceControl->setLayer(100000); //设置z轴 315 surfaceControl->setSize(display.w, display.h); 316 surfaceControl->setPosition(0, 0); //起始位置 317 SurfaceComposerClient::closeGlobalTransaction(); 318 // 获取Surface本地代理 319 sp<Surface> surface = surfaceControl->getSurface();//获取surface 320 Surface::SurfaceInfo info; 321 322 //创建SurfaceControl的本地代理 323 sp<SurfaceControl> surfaceControl1 = client->createSurface(String8("wenfu"), 324 display.w, display.h, PIXEL_FORMAT_RGBA_8888, 0); 325 SurfaceComposerClient::openGlobalTransaction(); 326 surfaceControl1->setLayer(90000); //设置z轴 327 surfaceControl1->setSize(display.w, display.h); 328 surfaceControl1->setPosition(0, 0); //起始位置 329 SurfaceComposerClient::closeGlobalTransaction(); 330 // 获取Surface本地代理 331 sp<Surface> surface1 = surfaceControl1->getSurface();//获取surface 332 Surface::SurfaceInfo info1; 333 334 ssize_t bpr ; 335 SkBitmap bitmap; 336 SkCanvas canvas ; 337 SkCanvas canvas1; 338 SkBitmap bitmap1; 339 SkPath path; 340 SkPaint paint; //paint可以指定绘图的颜色,文本的大小及对齐方式,编码格式 341 SkPaint paint1; 342 SkRect rect; 343 int sx,sy; 344 int w,h; 345 int flg = 1; 346 int flg2 = 0; 347 int lastx = 0, lasty = 0, ctrx = 0, ctry = 0; 348 int TOUCH_TOLERANCE = 4; //只有当移动的距离大于4px时,才在屏幕上绘图 349 350 SkFILEStream stream("./5.jpg"); 351 SkImageDecoder* codec = SkImageDecoder::Factory(&stream); 352 SkBitmap bmp; 353 if(codec){ 354 stream.rewind(); //将文件指针指向文件开头 355 codec->decode(&stream, &bmp, SkBitmap::kARGB_8888_Config, SkImageDecoder::kDecodePixels_Mode); 356 surface1->lock(&info1); 357 bpr = info1.s * bytesPerPixel(info1.format); 358 bitmap1.setConfig(convertPixelFormat(info1.format), display.w, display.h, bpr); 359 bitmap1.setPixels(info1.bits); 360 //dev = new SkDevice(bitmap); 361 canvas1.setDevice(new SkDevice(bitmap1)); 362 canvas1.drawBitmap(bmp, SkIntToScalar(0), SkIntToScalar(0)); //从0,0点开始画图 363 surface1->unlockAndPost(); 364 }/* 365 surface->lock(&info); 366 bpr = info.s * bytesPerPixel(info.format); 367 android_memset32((uint32_t*)info.bits, 0xFFFFFFFF, bpr*info.h); 368 surface->unlockAndPost(); */ 369 370 int erase = 0; 371 int ew,eh; 372 373 while(1) 374 { 375 if(getDown()) 376 { 377 if(flg) //按下 378 { 379 flg = 0; 380 flg2 = 1; 381 382 if(getW() > 80) 383 { 384 erase = 1; 385 ew = getW(); 386 eh = getH(); 387 } 388 else 389 { 390 path.reset(); 391 //获取坐标 392 ctrx = getX(); 393 ctry = getY(); 394 path.moveTo(ctrx, ctry); 395 lastx = ctrx; 396 lasty = ctry; 397 } 398 } 399 else //移动 400 { 401 ctrx = getX(); 402 ctry = getY(); 403 float dx = abs(ctrx - lastx); 404 float dy = abs(ctry - lasty); 405 //两点之间的距离大于等于4时,生成贝塞尔绘制曲线 406 if (dx >= 3 || dy >= 3) 407 { 408 //设置贝塞尔曲线的操作点为起点和终点的一半 409 float cX = (ctrx + lastx) / 2; 410 float cY = (ctry + lasty) / 2; 411 //二次贝塞尔,实现平滑曲线;previousX, previousY为操作点,cX, cY为终点 412 //path.quadTo(lastx, lasty, cX, cY); 413 path.quadTo(lastx, lasty, cX, cY); 414 } 415 surface->lock(&info); 416 bpr = info.s * bytesPerPixel(info.format); 417 bitmap.setConfig(convertPixelFormat(info.format), display.w, display.h, bpr); 418 bitmap.setPixels(info.bits); 419 paint.setDither(true); //去抖动 420 paint.setAntiAlias(true); //抗锯齿 421 canvas.setDevice(new SkDevice(bitmap)); 422 if(!erase) 423 { 424 paint.setARGB(255, 255, 0, 0); //画笔颜色 425 paint.setStrokeWidth(10); //画笔宽度 426 paint.setStyle(SkPaint::kStroke_Style); //空心 427 canvas.drawPath(path, paint); 428 } 429 else 430 { 431 paint.setARGB(255, 255, 255, 255); //画笔颜色 432 paint.setStyle(SkPaint::kFill_Style); //实心 433 canvas.drawRect(SkRect::MakeXYWH(ctrx - ew/2, ctry - eh/2, ctrx + ew/2,ctry + eh/2), paint); 434 } 435 surface->unlockAndPost(); 436 surface->lock(&info); 437 bpr = info.s * bytesPerPixel(info.format); 438 bitmap.setConfig(convertPixelFormat(info.format), display.w, display.h, bpr); 439 bitmap.setPixels(info.bits); 440 paint.setDither(true); //去抖动 441 paint.setAntiAlias(true); //抗锯齿 442 canvas.setDevice(new SkDevice(bitmap)); 443 if(!erase) 444 { 445 paint.setARGB(255, 255, 0, 0); //画笔颜色 446 paint.setStrokeWidth(10); //画笔宽度 447 paint.setStyle(SkPaint::kStroke_Style); //空心 448 canvas.drawPath(path, paint); 449 } 450 else 451 { 452 paint.setARGB(255, 255, 255, 255); //画笔颜色 453 paint.setStyle(SkPaint::kFill_Style); //实心 454 canvas.drawRect(SkRect::MakeXYWH(ctrx - ew/2, ctry - eh/2, ctrx + ew/2,ctry + eh/2), paint); 455 } 456 surface->unlockAndPost(); 457 458 lastx = ctrx; 459 lasty = ctry; 460 } 461 } 462 else if(getUp()) //抬起 463 { 464 flg = 1; 465 if(flg2) 466 { 467 flg2 = 0; 468 erase = 0; 469 } 470 } 471 } 472 //IPCThreadState::self()->joinThreadPool(); 473 //IPCThreadState::self()->stopProcess(); 474 return 0; 475 }
以上是C++代码,功能结果如上图所示,mk文件:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_C_INCLUDES := $(call include-path-for, corecg graphics)
LOCAL_C_INCLUDES += external/skia/include/core
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include/core
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include/images
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src/images
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src/core
LOCAL_C_INCLUDES += $(INCLUDES)
LOCAL_SRC_FILES:= \
Surface.cpp \
src/core/SkBitmap.cpp \
src/core/SkDevice.cpp \
src/core/SkPaint.cpp \
src/core/SkRect.cpp \
src/core/SkTypeface.cpp \
src/images/SkImageEncoder.cpp \
src/core/SkCanvas.cpp \
main.cpp
LOCAL_SHARED_LIBRARIES:= \
libcutils \
libutils \
libbinder \
libui \
libgui \
libskia \
libandroidfw \
libEGL \
libGLESv1_CM \
libGLESv2
LOCAL_MODULE:= testsurface
LOCAL_STATIC_LIBRARIES := libskiagpu
LOCAL_MODULE_TAGS := eng tests
include $(BUILD_EXECUTABLE)
文件比较多,不过都是在android源码里面有,可以找到。