canvas是skia的核心部分,skia的逻都是围绕skcanvas对象来组织管理的,通过skcanvas可以指定不同的渲染上下文、draw call(绘制命令)、以及绘制状态管理(如绘图矩阵、操作栈等)

skcanvas的状态

skcanvasskpaint共同提供了sksurface和skbasedevice上绘制的状态,skcanvas保存了所有操作的堆栈,通过 saverestore这两个方法来管理skcanvas的操作状态信息。

Backend

skcanvas是可以指定在特定的像素上进行绘制,为其提供这种能力主要是不同画布来提供的(sksurface),skcanvas本身也是一个基类,画布主要可以分为两种,Raster Surface 和 GPU Suface。
Raster Surface生成的canvas可以将绘制生成在cpu的内存上(如在不同的图片绘制)
CPU Surface 生成的canvas 可以内部通过opengl、webgl、vulkan来进行绘制(目前skia可以在webassembly技术在web上使用)
skia提供的backends(理解为不同的后端渲染设备),有以下几种:
raster cpu渲染(如将结果绘制在不同的图片上,可以理解为只要绘制在CPU的内存上,都可以使用这种方式)
gpu 绘制在GPU上,可以创建opengl、vulkan的绘制
skPdf 将结果绘制在PDF文档上
skPicture将结果绘制在显示列表中(类似于内存结构,下次绘制可以用来加速)
skSvg将结果绘制在Svg文档上。
skXPS将结果绘制在XPS文档上.

Raster Backend

Raster是通过指定一块内存,canvas会将draw calls绘制在一块CPU的内存上,这个内存一般来说是一个栅格图片.下面的代码有三种实现方式(指定图片、指定内存、指定窗口句柄Hwnd)来实现绘制。

#include "SkData.h"
#include "SkImage.h"
#include "SkStream.h"
#include "SkSurface.h"
//内部创建内存,将结果保存在一张图片中;
void raster(int width, int height,void (*draw)(SkCanvas*),const char* path) {sk_sp<SkSurface> rasterSurface =SkSurface::MakeRasterN32Premul(width, height);SkCanvas* rasterCanvas = rasterSurface->getCanvas();draw(rasterCanvas);sk_sp<SkImage> img(rasterSurface->makeImageSnapshot());if (!img) { return; }sk_sp<SkData> png(img->encode());if (!png) { return; }SkFILEWStream out(path);(void)out.write(png->data(), png->size());
}//指定一片内存,将结果绘制在这个内存中void raster(char* pixel,int width,int height,void (*draw)(SkCanvas*))
{const SkImageInfo image_info = SkImageInfo::Make(data->width(), data->height(),kN32_SkColorType, SkAlphaType::kPremul_SkAlphaType);//获取每一行的字节大小;const int bytes_per_line = sizeof(uint32_t)*width;auto canvas = SkCanvas::MakeRasterDirect(image_info, pixel, bytes_per_line);draw(canvas)//绘制回调;
}//windows 下绑定窗口句柄的CPU绘制
#include <windows.h>SkAutoMalloc  fSurfaceMemory;
void renderWindows(Hwnd hwnd)
{//获取窗口大小;RECT rect;GetClientRect(fWnd, &rect);int w=rect.right-rect.left;int h=rect.bottom-rect.top;const size_t bmpSize = sizeof(BITMAPINFOHEADER) + w * h * sizeof(uint32_t);//创建一片内存与窗口的内存大小相同;fSurfaceMemory.reset(bmpSize);BITMAPINFO* bmpInfo = reinterpret_cast<BITMAPINFO*>(fSurfaceMemory.get());ZeroMemory(bmpInfo, sizeof(BITMAPINFO));bmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);bmpInfo->bmiHeader.biWidth = w;bmpInfo->bmiHeader.biHeight = -h; // negative means top-down bitmap. Skia draws top-down.bmpInfo->bmiHeader.biPlanes = 1;bmpInfo->bmiHeader.biBitCount = 32;bmpInfo->bmiHeader.biCompression = BI_RGB;void* pixels = bmpInfo->bmiColors;SkImageInfo info = SkImageInfo::Make(w, h, fDisplayParams.fColorType, kPremul_SkAlphaType, fDisplayParams.fColorSpace);//将窗口的内存位置给到内存图片中.fBackbufferSurface = SkSurface::MakeRasterDirect(info, pixels, sizeof(uint32_t) * w);
}
//将结果同步到hwnd上;
void swapBuffers() {BITMAPINFO* bmpInfo = reinterpret_cast<BITMAPINFO*>(fSurfaceMemory.get());HDC dc = GetDC(fWnd);RECT rect;GetClientRect(fWnd, &rect);int w=rect.right-rect.left;int h=rect.bottom-rect.top;StretchDIBits(dc, 0, 0, w, h, 0, 0, w, h, bmpInfo->bmiColors, bmpInfo,DIB_RGB_COLORS, SRCCOPY);ReleaseDC(fWnd, dc);
}

GPU Surface

必须有一个GrContext对象来管理gpu context,skia自身没有直接通过窗口创建上下文的方法,不过有一个示例的库(tools/sk_app)可以创建不同平台、Surface窗口的方法。

#include "GrContext.h"
#include "gl/GrGLInterface.h"
#include "SkData.h"
#include "SkImage.h"
#include "SkStream.h"
#include "SkSurface.h"void gl_example(int width, int height, void (*draw)(SkCanvas*), const char* path) {// You've already created your OpenGL context and bound it.const GrGLInterface* interface = nullptr;// Leaving interface as null makes Skia extract pointers to OpenGL functions for the current// context in a platform-specific way. Alternatively, you may create your own GrGLInterface and// initialize it however you like to attach to an alternate OpenGL implementation or intercept// Skia's OpenGL calls.sk_sp<GrContext> context = GrContext::MakeGL(interface);SkImageInfo info = SkImageInfo:: MakeN32Premul(width, height);sk_sp<SkSurface> gpuSurface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info));if (!gpuSurface) {SkDebugf("SkSurface::MakeRenderTarget returned null\n");return;}SkCanvas* gpuCanvas = gpuSurface->getCanvas();draw(gpuCanvas);sk_sp<SkImage> img(gpuSurface->makeImageSnapshot());if (!img) { return; }sk_sp<SkData> png(img->encode());if (!png) { return; }SkFILEWStream out(path);(void)out.write(png->data(), png->size());
}

SkPicture

使用显示列表的方式有点类似opengl的 displaylist,即将绘制的结果存储起来,下次绘制的时候只需要调用drawcall绘制指令即可,不需要重新准备数据。只要不改变数据,画布的放大缩小等不需要重新更新数据绘制。

//使用SkPictureRecorder做记录
void picture(int width, int height  void (*draw)(SkCanvas*))
{SkPictureRecorder recorder;auto recording_canvas = recorder.beginRecording(SkIntToScalar(width),SkIntToScalar(height));//调用普通的绘制方法即可;draw(recording_canvas);//将记录存储在canvas中auto picture = recorder.finishRecordingAsPicture();
}//使用回放;
void drawPicture(sk_sp<SkPicture> picture)
{sk_sp<SkSurface> rasterSurface =SkSurface::MakeRasterN32Premul(width, height);SkCanvas* raster_canvas = rasterSurface->getCanvas();//使用playback绘制picture->playback(&raster_canvas);
}

SkSvg
通过canvas的方式输出Svg矢量图形,与绘制达到一样的效果(同样支持各种裁剪)

//将绘制结果保存到Svg文件中;
void drawSvg(int width,int height,void (*draw)(SkCanvas*),const char* path)
{const auto wStream = std::make_shared<SkFILEWStream>(path);auto canvas = SkSVGCanvas::Make(SkRect::MakeWH(width, height), wStream.get());draw(canvas);wStream->flush();
}//读写svg文件并进行绘制(skia需要静态编译才能编译sksvg模块)
void drawSvg(skCavas* canvas,double x,double y,double width,double height)
{SkFILEStream text_stream(image_file.toLocal8Bit());SkTDArray<char> inData;inData.setCount((int)text_stream.getLength());size_t inLen = inData.count();text_stream.read(inData.begin(), inLen);text_stream.close();const auto svg_stream = SkMemoryStream::MakeDirect(inData.begin(), inLen);if(svg_stream == nullptr){return { nullptr };}auto svg_dom = SkSVGDOM::Builder().make(*svg_stream);if(svg_dom == nullptr){return ;}canvas->save();double widthRatio = 1;double heightRatio = 1;const double w=svg_dom->getRoot()->getWidth().value();const double h= svg_dom->getRoot()->getHeight().value();if(rect.isValid()){widthRatio = width /w ;heightRatio = rect.height() / h;}canvas->translate(x,y);canvas->scale(widthRatio, heightRatio);svg_dom->render(canvas);canvas->restore();}

SkPDF 输出PDF矢量图形,PDF可以分页打印,也可以通过skPDFOjbect来扩展实现自定义信息的填写。SKPDF与SKXPS的使用方式基本一致;

void drawPDF(const char* pdf_name)
{const auto wStream = std::make_shared<SkFILEWStream>(byte_array.constData());SkPDF::Metadata metadata;sk_sp<SkDocument> pdf_doc = SkPDF::MakeDocument(wStream.get(), metadata);//auto pdf_canvas =pdf_doc->beginPage(width, height);//与正常方法一样调用绘制;skPaint _paint;pdf_canvas->drawLine(0,0,100,100,_paint);pdf_doc->endPage();wstream->flush();wstream->close();
}

skia 之canvas相关推荐

  1. 跨平台Web Canvas渲染引擎架构的设计与思考(内含实现方案)

    这篇文章主要从技术视角介绍下跨平台WebCanvas的架构设计以及一些关键模块的实现方案(以Android为主),限于作者水平,有不准确的地方欢迎指正或者讨论. 设计目标 标准化:Web Canvas ...

  2. 跨平台Web Canvas渲染引擎架构的设计与思考

    简介: 这篇文章主要从技术视角介绍下跨平台WebCanvas的架构设计以及一些关键模块的实现方案(以Android为主),限于作者水平,有不准确的地方欢迎指正或者讨论. 设计目标 标准化:Web Ca ...

  3. chromium的图形和skia(Graphics and skia)

    Chrome uses Skia for nearly all graphics operations, including text rendering. GDI is for the most p ...

  4. android view交替动画,Android View原理(View树遍历,View重绘,View动画)

    一.屏幕绘图基础 Android中的GUI系统是客户端和服务端配合的窗口系统,即后台运行了一个绘制服务,每个应用程序都是该服务端的一个客户端,当客户端需要绘制时,首先请求服务端创建一个窗口,然后在窗口 ...

  5. 从零开始学Android架构(一)——什么是设计模式?

    前言 不少人会觉得架构师是一个高大上的岗位,只有技术顶尖的人才能胜任,但其实它并没有这么高大上,大部分的架构师,都只是开发经验非常丰富,并且热爱学习,善于知识迁移和总结.应用的架构是一件非常成熟,有非 ...

  6. 图解Android - Android GUI 系统 (2) - 窗口管理 (View, Canvas, Window Manager)

    Android 的窗口管理系统 (View, Canvas, WindowManager) 在图解Android - Zygote 和 System Server 启动分析一 文里,我们已经知道And ...

  7. android对skia的封装,Skia引擎API整理介绍(skia in Android 2.3 trunk)

    序 通过google, baidu,我们都很难搜索到关于skia引擎方面的文档,skia的官方wiki(http://code.google.com/p/skia) 也只有寥寥无几的文字简单介绍了Sk ...

  8. android自定义游戏闯关图,Android自定义View(四) -- Canvas

    本文计划根据HenCoder系列文章进行学习,所以代码风格及博文素材可能会摘自其中. 1 范围裁切 范围裁切有两个方法: clipRect() 和 clipPath().裁切方法之后的绘制代码,都会被 ...

  9. hwui opengl VS skia opengl VS skia vulkan?

    之前讨论过skia codec部分在o,p上的变化,比如增加了heif解码等. 其实skia在android o,p的变化不只这些. 印象最深刻的还是渲染部分 从o开始hwui渲染支持skia ope ...

最新文章

  1. BeeHive模块注册
  2. 【Android 逆向】ART 脱壳 ( dex2oat 脱壳 | aosp 中搜索 dex2oat 源码 | dex2oat.cc#main 主函数源码 )
  3. java中取得上下文路径的方法
  4. 通过lseek产生空洞文件
  5. dos攻击命令_Kali Linux系列之拒绝服务攻击(DOS)实战(上)
  6. 在gluster中配置distributed 卷
  7. 网络爬虫中URLConnection的使用[以科学网为例]
  8. ARM三个寄存器 : 堆栈指针SP(R13)、连接寄存器LR(R14)和程序计数器PC(R15)
  9. 【SpringMVC笔记】Ajax 入门(jQuery.ajax)
  10. Pikachu实验过程1(函数报错的信息)
  11. mysql计算同比和环比的区别_【面试真题】Mysql实现计算同比、环比
  12. 单模光纤VS多模光纤
  13. QSPI FLASH与SD卡同时支持fatfs文件系统
  14. convert 函数的使用
  15. 2022跨年烟花代码(三)HTML5点击页面烟花绽放特效
  16. python脚本案例-python+adb命令实现自动刷视频脚本案例
  17. springboot slf4j log4j2 动态创建日志的方法
  18. iphone拍照的历史顽固问题-鬼影
  19. Django--Models
  20. [WPF实践之路] 目录导航

热门文章

  1. OpenSSL 心脏滴血漏洞(CVE-2014-0160)漏洞讲解(小白可懂,简单详细)
  2. 数据分析常用的软件及工具
  3. java poi导出excel 设置单元格式为百分比现实
  4. python--视频拆帧 帧合成视频流
  5. html做一个京东搜索功能,连续动作:自动搜索关键词采集信息—以京东为例
  6. 基于RFID技术下的室内人员定位考勤系统,室内来访定位-新导智能
  7. 新视野大学英语(第三版)第一册课后习题答案(完整版)
  8. 拼多多首次盈利,陈磊做了什么?
  9. 莫生气---经典打油诗
  10. 如何实现IM即时通信系统(一)