Windows下从源码编译Skia一文介绍了Skia的编译,现在我们可以尝试在PPAPI插件中来使用Skia了。

foruok原创,如需转载请关注foruok的微信订阅号“程序视界”联系foruok。

Skia的关键类库

官网https://skia.org/上有文档,可以看。然后下载的源码,可以使用SourceInsight之类的工具来查看。具体不再细说,我这里只提用到的三个关键类:

  • SkPaint
  • SkCanvas
  • SkBitmap

SkCanvas是画布,你可以在它上面画任意的图元,比如矩形、圆型、文字、弧……

SkPaint则是工具箱,聚合了画笔、颜色等各种绘画时要用的配置选项。

SkBitmap代表了一块图像数据。

你调用SkCanvas的方法绘制图元,这些图元对应的数据一定要有个地方保存或显示,用于保存或显示图像数据的那个角色,被称为backends。SkCanvas的backends,有好几种,我们这个简单的示例ppapi_skia只用到了SkBitmap这种后端。

2D绘图类库的基本设计都差不多,我的《Qt on Android核心编程》一书里对Qt绘图有介绍,两厢比对,就会发现设计得差不多。不过有一点不同,QPainter会保存你设置的绘图选项(颜色、画笔等),而SkCanvas则不会,需要自己设计数据结构和逻辑来保存。

ppapi_skia项目

ppapi_skia项目基于ppapi_simple而来,请先参考PPAPI插件的绘图与输入事件处理这篇文章。

不过为了方便调用Skia(C++类库),我这次用了cpp源文件,ppapi_skia.cpp。(吐槽下,VS2013对于C文件的即时提示太弱……)

我还改了ppapi_simple,使用了FlushCompletionCallback来优化绘图流程。

Skia默认编译出来的是静态库,对于一个简单的PPAPI+Skia插件,我不知道需要链接哪些lib,写好了代码,编译,根据undefined reference错误一通狂找,发现要链接下列lib:

skia_core.lib
skia_ports.lib
skia_utils.lib
skia_effects.lib
skia_skgpu.lib
skia_opts.lib
skia_opts_ssse3.lib
skia_opts_sse41.lib
skia_opts_avx.lib
skia_images.lib
skia_sfnt.lib
libetc1.lib
libSkKTX.lib
opengl32.lib

Windows下从源码编译Skia一文提到编译后的lib文件路径是:E:\sources\skia\out\Release。设置到项目的附加库目录列表中。skia还依赖其他一些库文件,在E:\sources\skia\out\Release\obj\gyp目录下,把这个目录添加到附加库目录列表里。

Skia需要OpenGL,我们得链接Opengl32.lib,Windows SDK里有。

skia编译时加了/MD参数,CEF编译时用的MT,如果你用PPAPI的C++接口,就会冲突了。要么改skia,要么改CEF。skia库小,改它吧。我还没找到怎么在生成构建文件时设置,最简单直接的,就是到E:\sources\skia\out\Release\obj\gyp这里,把*.ninja文件里的/MD都修改为/MT。然后重新编译。

如过用C接口,没关系,把PPAPI的代码生成选项里的运行库修改为MD即可。我偷懒,就用C接口,把ppapi_skia项目的运行库选项设置为了MD。

好,关于工程基本就这样了。

源码

分C++源码和HTML源码。

C++代码

So,源码来了,基于C代码改过来,有点乱,不过可以说明用法。

/*
* Copyright (c) 2016 foruok@程序视界. All rights reserved.
* 2016-1-16, edited by foruok.
* 如需转载,请关注微信订阅号“程序视界”,回复foruok获取其联系方式
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
#include <tchar.h>#include "ppapi/c/pp_completion_callback.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_module.h"
#include "ppapi/c/pp_rect.h"
#include "ppapi/c/pp_var.h"
#include "ppapi/c/ppb.h"
#include "ppapi/c/ppb_core.h"
#include "ppapi/c/ppb_graphics_2d.h"
#include "ppapi/c/ppb_image_data.h"
#include "ppapi/c/ppb_instance.h"
#include "ppapi/c/ppb_view.h"
#include "ppapi/c/ppp.h"
#include "ppapi/c/ppp_instance.h"
#include "ppapi/c/ppb_input_event.h"
#include "ppapi/c/ppp_input_event.h"
#include "SkPaint.h"
#include "SkBitmap.h"
#include "SkCanvas.h"PPB_GetInterface g_get_browser_interface = NULL;const PPB_Core* g_core_interface;
const PPB_Graphics2D* g_graphics_2d_interface;
const PPB_ImageData* g_image_data_interface;
const PPB_Instance* g_instance_interface;
const PPB_View* g_view_interface;
const PPB_InputEvent *g_input_interface;
const PPB_MouseInputEvent *g_mouse_interface;/* PPP_Instance implementation -----------------------------------------------*/struct InstanceInfo {PP_Instance pp_instance;struct PP_Size last_size;PP_Resource graphics;PP_Resource image;SkBitmap *bitmap;SkCanvas *canvas;int inFlush;int requestPaint;unsigned int colorIndex;struct InstanceInfo* next;
};/** Linked list of all live instances. */
struct InstanceInfo* all_instances = NULL;/** Returns a refed resource corresponding to the created graphics 2d. */
PP_Resource MakeAndBindGraphics2D(PP_Instance instance,const struct PP_Size* size) {PP_Resource graphics;graphics = g_graphics_2d_interface->Create(instance, size, PP_FALSE);if (!graphics)return 0;if (!g_instance_interface->BindGraphics(instance, graphics)) {g_core_interface->ReleaseResource(graphics);return 0;}return graphics;
}unsigned int g_colors[4] = { 0xFF0000FF, 0xFFFF00FF, 0xFF00FFFF, 0xFFEA00FF };void DoPaint(struct InstanceInfo * instance);void FlushCompletionCallback(void* user_data, int32_t result) {struct InstanceInfo *inst = (struct InstanceInfo*)user_data;inst->inFlush = 0;if (inst->requestPaint){inst->requestPaint = 0;DoPaint(inst);}
}const char szHelloSkia[] = "Hello Skia in PPAPI";
void DoPaint(struct InstanceInfo * instance)
{instance->inFlush = 1;instance->colorIndex++;if (instance->colorIndex >= sizeof(g_colors) / sizeof(g_colors[0])) instance->colorIndex = 0;/* [2] Skia Paint * foruok */instance->canvas->drawColor(g_colors[instance->colorIndex]);SkPaint paint;paint.setColor(SK_ColorWHITE);paint.setAntiAlias(true);paint.setLCDRenderText(true);paint.setStrokeWidth(4);instance->canvas->drawText(szHelloSkia, ARRAYSIZE(szHelloSkia) - 1, 20.0f, 20.0f, paint);paint.setColor(0x80CCCCCC);instance->canvas->drawCircle(150, 130, 100, paint);/* Paint image to graphics 2d. */g_graphics_2d_interface->ReplaceContents(instance->graphics, instance->image);g_graphics_2d_interface->Flush(instance->graphics,PP_MakeCompletionCallback(&FlushCompletionCallback, instance));
}void Repaint(struct InstanceInfo* instance, const struct PP_Size* size) {/* Ensure the graphics 2d is ready. */if (!instance->graphics) {instance->graphics = MakeAndBindGraphics2D(instance->pp_instance, size);if (!instance->graphics)return;}/* Create image data to paint into. */if (!instance->image){instance->image = g_image_data_interface->Create(instance->pp_instance, PP_IMAGEDATAFORMAT_BGRA_PREMUL, size, PP_TRUE);if (!instance->image)return;}/* [1] setup SkCanvas's backends * foruok */if (!instance->bitmap){/* foruok * get image data from PPB interfaces */struct PP_ImageDataDesc image_desc;uint32_t* image_data;g_image_data_interface->Describe(instance->image, &image_desc);image_data = (uint32_t*)g_image_data_interface->Map(instance->image);if (!image_data) {g_core_interface->ReleaseResource(instance->image);return;}/* foruok * bind PPAPI image data to SkBitmap */SkImageInfo ii = SkImageInfo::Make(size->width, size->height, kBGRA_8888_SkColorType, kPremul_SkAlphaType, kLinear_SkColorProfileType);instance->bitmap = new SkBitmap();instance->bitmap->installPixels(ii, image_data, image_desc.stride);if (!instance->canvas){/* foruok * construct SkCanvas with SkBitmap backend */instance->canvas = new SkCanvas(*instance->bitmap);}}if (!instance->inFlush){DoPaint(instance);}else{instance->requestPaint = 1;}
}/** Returns the info for the given instance, or NULL if it's not found. */
struct InstanceInfo* FindInstance(PP_Instance instance) {struct InstanceInfo* cur = all_instances;while (cur) {if (cur->pp_instance == instance)return cur;cur = cur->next;}return NULL;
}PP_Bool Instance_DidCreate(PP_Instance instance,uint32_t argc,const char* argn[],const char* argv[]) {struct InstanceInfo* info =(struct InstanceInfo*)calloc(1, sizeof(struct InstanceInfo));info->pp_instance = instance;/* Insert into linked list of live instances. */info->next = all_instances;all_instances = info;g_input_interface->RequestInputEvents(instance, PP_INPUTEVENT_CLASS_MOUSE);g_input_interface->RequestFilteringInputEvents(instance, PP_INPUTEVENT_CLASS_MOUSE);OutputDebugString(_T("Instance_DidCreate\r\n"));return PP_TRUE;
}void Instance_DidDestroy(PP_Instance instance) {/* Find the matching item in the linked list, delete it, and patch the* links.*/struct InstanceInfo** prev_ptr = &all_instances;struct InstanceInfo* cur = all_instances;while (cur) {if (instance == cur->pp_instance) {*prev_ptr = cur->next;g_core_interface->ReleaseResource(cur->graphics);free(cur);return;}prev_ptr = &cur->next;cur = cur->next;}
}void Instance_DidChangeView(PP_Instance pp_instance,PP_Resource view) {struct PP_Rect position;struct InstanceInfo* info = FindInstance(pp_instance);if (!info)return;if (g_view_interface->GetRect(view, &position) == PP_FALSE)return;if (info->last_size.width != position.size.width ||info->last_size.height != position.size.height) {/* Got a resize, repaint the plugin. */Repaint(info, &position.size);info->last_size.width = position.size.width;info->last_size.height = position.size.height;}OutputDebugString(_T("Instance_DidChangeView\r\n"));
}void Instance_DidChangeFocus(PP_Instance pp_instance, PP_Bool has_focus) {
}PP_Bool Instance_HandleDocumentLoad(PP_Instance pp_instance,PP_Resource pp_url_loader) {return PP_FALSE;
}static PPP_Instance instance_interface = {&Instance_DidCreate,&Instance_DidDestroy,&Instance_DidChangeView,&Instance_DidChangeFocus,&Instance_HandleDocumentLoad
};PP_Bool InputEvent_HandleInputEvent(PP_Instance instance, PP_Resource input_event)
{struct PP_Point pt;TCHAR szLog[512] = { 0 };switch (g_input_interface->GetType(input_event)){case PP_INPUTEVENT_TYPE_MOUSEDOWN:pt = g_mouse_interface->GetPosition(input_event);_stprintf_s(szLog, 512, _T("InputEvent_HandleInputEvent, mouse down at [%d, %d]\r\n"), pt.x, pt.y);OutputDebugString(szLog);break;/*case PP_INPUTEVENT_TYPE_MOUSEUP:OutputDebugString(_T("InputEvent_HandleInputEvent, mouse up\r\n"));break;case PP_INPUTEVENT_TYPE_MOUSEMOVE:OutputDebugString(_T("InputEvent_HandleInputEvent, mouse move\r\n"));break;case PP_INPUTEVENT_TYPE_MOUSEENTER:OutputDebugString(_T("InputEvent_HandleInputEvent, mouse enter\r\n"));break;case PP_INPUTEVENT_TYPE_MOUSELEAVE:OutputDebugString(_T("InputEvent_HandleInputEvent, mouse leave\r\n"));break;*/default:return PP_FALSE;}struct InstanceInfo* info = FindInstance(instance);if (info && info->last_size.width > 0){Repaint(info, &info->last_size);}return PP_TRUE;
}static PPP_InputEvent input_interface = {&InputEvent_HandleInputEvent
};/* Global entrypoints --------------------------------------------------------*/PP_EXPORT int32_t PPP_InitializeModule(PP_Module module,PPB_GetInterface get_browser_interface) {g_get_browser_interface = get_browser_interface;g_core_interface = (const PPB_Core*)get_browser_interface(PPB_CORE_INTERFACE);g_instance_interface = (const PPB_Instance*)get_browser_interface(PPB_INSTANCE_INTERFACE);g_image_data_interface = (const PPB_ImageData*)get_browser_interface(PPB_IMAGEDATA_INTERFACE);g_graphics_2d_interface = (const PPB_Graphics2D*)get_browser_interface(PPB_GRAPHICS_2D_INTERFACE);g_view_interface = (const PPB_View*)get_browser_interface(PPB_VIEW_INTERFACE);g_input_interface = (const PPB_InputEvent*)get_browser_interface(PPB_INPUT_EVENT_INTERFACE);g_mouse_interface = (const PPB_MouseInputEvent*)get_browser_interface(PPB_MOUSE_INPUT_EVENT_INTERFACE);if (!g_core_interface || !g_instance_interface || !g_image_data_interface ||!g_graphics_2d_interface || !g_view_interface ||!g_input_interface || !g_mouse_interface)return -1;OutputDebugString(_T("PPP_InitializeModule\r\n"));return PP_OK;
}PP_EXPORT void PPP_ShutdownModule() {
}PP_EXPORT const void* PPP_GetInterface(const char* interface_name) {if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0){OutputDebugString(_T("PPP_GetInterface, instance_interface\r\n"));return &instance_interface;}else if (strcmp(interface_name, PPP_INPUT_EVENT_INTERFACE) == 0){OutputDebugString(_T("PPP_GetInterface, input_interface\r\n"));return &input_interface;}return NULL;
}

上面代码里我标注了两处与Skia有关的改动,[1]和[2],也加了注释。另外就是使用FlushCompletionCallback来处理绘图和刷新,把ppapi_simple中的一些全局变量修改为与插件实例对象绑定的变量,这样可以支持一个网页里嵌入多个plugin了。

HTML代码

ppapi_skia.html代码如下:

<!DOCTYPE html>
<html><!--Copyright (c) 2016 foruok. All rights reserved.如需转载,请关注微信订阅号“程序视界”,回复foruok获取其联系方式-->
<head><title>PPAPI-SKIA</title>
</head><body><embed id="plugin" type="application/x-ppapi-skia" width="400px" height="300px"></body>
</html>

运行效果

使用下面命令来运行:

cefsimple.exe --ppapi-out-of-process --register-pepper-plugins="E:\sources\CEF\2526\chromium\src\cef\binary_distrib\cef_binary_3.2526.1364.gf6bf57b_windows32\Release\ppapi_skia.dll;application/x-ppapi-skia" --url=file:///E:/sources/CEF/2526/chromium/src/cef/binary_distrib/cef_binary_3.2526.1364.gf6bf57b_windows32/Release/ppapi_skia.html

效果如下:


Ok,这个示例就这么着了。有时间我会用Skia+PPAPI的方式制作一个简单的涂鸦板插件。

其他参考文章:

  • CEF Windows开发环境搭建
  • CEF加载PPAPI插件
  • VS2013编译最简单的PPAPI插件
  • 理解PPAPI的设计
  • PPAPI插件与浏览器的交互过程
  • Windows下从源码编译CEF
  • 编译PPAPI的media_stream_video示例
  • PPAPI插件的绘图与输入事件处理
  • 在PPAPI插件中创建本地窗口
  • PPAPI插件与浏览器的通信
  • Windows下从源码编译Skia

在PPAPI插件中使用Skia绘图相关推荐

  1. PPAPI插件的全屏切换处理

    有时你会想让PPAPI插件全屏(比如播放视频时),这次来看看怎么做. PPAPI和CEF App两侧都要处理. foruok原创,转载请注明出处.欢迎关注foruok的订阅号"程序视界&qu ...

  2. PPAPI插件的绘图与输入事件处理

    在PPAPI插件与浏览器的交互过程一文中学习了PPAPI插件与浏览器的交互流程.渲染逻辑.输入事件的处理逻辑,这次我们改造一下graphics_2d_example示例,加入处理鼠标事件的逻辑,演示一 ...

  3. PPAPI插件与浏览器的交互过程

    上一篇理解了一下PPAPI的设计,并从代码角度理解了一下相关主题,这篇文章关注下面几点: 插件实例对象的创建与使用流程 实例大小的确认 渲染(绘图) 处理输入事件 foruok原创,如需转载请关注fo ...

  4. 新版谷歌Chrome取消对PPAPI插件支持后,浏览器网页打开编辑保存微软Office、金山WPS文档解决方案

    最近陆续看到一些大学发布公告,谷歌Chrome取消了对PPAPI插件支持,导致某些在线Office厂家产品将无法在谷歌Chrome107及以上版本运行,被迫更换360浏览器或者使用低版本Chrome浏 ...

  5. Github 每日精选:可在Java 中绑定 skia 的 2D 图形库Skija;自动对对联系统seq2seq-couplet

    大家好,我是开源菌!天气转凉,有点不想打字,但依然阻挡不了我给大家安利开源项目的冲动.刚刚瞄了一眼今天的榜单,重新上架的 youtube-dl 插件再次登顶第一宝座,好不威风. 1.Skija:可在J ...

  6. 在vscode的jupyter中使用plotly绘图,图片显示不出来

    问题: 在vscode的jupyter中使用plotly绘图时,发现程序无报错,下方也留有一块显示图片的区域,但是图片就是加载不出,显示为空. 解决: 从其他博客了解到,是vscode渲染的问题,下载 ...

  7. PPAPI插件与Node 插件对比

    最近公司项目需要从cef向electron过度,相应的插件也需要做升级,其实ppapi插件在electron中也是可以工作的,只是某些场景效率会差一些. 以下是个人看法,有错误之处请不吝指正. PPA ...

  8. ATS 6.2.1打release版本rpm包时插件中出现undefined symbol的问题追踪

    问题场景 我基于ATS 6.2.1社区版整合进一些插件,发现debug版本一直运行好好的,后来改为release版本(就是configure时不加--enable_debug)时,安装后显示下面的出错 ...

  9. ATS插件中配置文件自动更新思路

    在ATS插件开发过程中,我们经常会需要如下业务需求: 某个插件的配置文件更新了,我们需要让新的配置文件生效,但是我们不想重启ATS.因为作为CDN行业的缓存服务器来说,很大部分缓存是直接使用内存存放的 ...

  10. Cacti 插件中setup.php 文件的编写

    Cacti 插件中setup.php 文件的编写 名词: 初始化函数   预定义函数  cacti 插件存放在 /plugins 目录,由setup.php与cacti 做关联调用; setup.ph ...

最新文章

  1. ARKit从入门到精通-ARKit工作原理及流程介绍
  2. eclipse+maven+jetty环境下修改了文件需要重启才能修改成功
  3. Webform(Linq高级查、分页、组合查询)
  4. 怎么自学python自动化测试-Python移动自动化测试面试 学习 教程
  5. java中的表达式是指_Java 表达式,语句和代码块
  6. 代码回滚:Reset、Checkout、Revert 的选择
  7. 解决eclipse安装maven的问题:Unable to update index for central
  8. Linux之web服务
  9. 颜宁追问4位男科学家:如何平衡事业和家庭?
  10. .xls和.xlsx 有什么区别?
  11. Ruby中对应PHP的hex2bin和bin2hex方法
  12. Cisco.Packet.Tracer思科模拟器练习题
  13. 安卓版微信自带浏览器和IE6浏览器ajax请求abort错误处理
  14. python+opencv修改图像指定像素的值
  15. RZR 丝印RZR Marking RZR 12Pin 芯片,终于找到型号了
  16. JS中国标准时间格式转换
  17. 关于nomogram核心函数的time.inc函数的设定
  18. MCE公司:你所不知道的 MCE--光反应赖氨酸的高难度合成
  19. cae计算机仿真分析技术,cae分析.doc
  20. Eclipse解决SVN版本冲突

热门文章

  1. 《运算放大器权威指南(Op Amps for Everyone)》读书笔记(一)
  2. 【html】表格table与表单form
  3. 【转】移动,电信,中行软开,微软,百度等企业工作纯技术性分析
  4. Oracle10g 详细安装教程
  5. php 显示探针_PHP探针
  6. 权重计算方法一:层次分析法(AHP)
  7. win7下maven安装
  8. Druid创始人Eric Tschetter详解开源实时大数据分析系统Druid
  9. 智慧园区弱电系统集成建设方案
  10. 图灵奖得主Alan Kay如何读书