Giter Site home page Giter Site logo

brooooooklyn / canvas Goto Github PK

View Code? Open in Web Editor NEW
1.7K 20.0 70.0 54.94 MB

High performance skia binding to Node.js. Zero system dependencies and pure npm packages without any postinstall scripts nor node-gyp.

Home Page: https://vercel.skia.rs

License: MIT License

TypeScript 17.96% Rust 51.54% JavaScript 13.43% C++ 15.27% Shell 0.01% Dockerfile 1.05% HTML 0.73%
skia canvas node-canvas rust napi n-api node-api napi-rs

canvas's Introduction

skr canvas

CI Skia Version install size Downloads

🚀 Help me to become a full-time open-source developer by sponsoring me on Github

Google Skia binding to Node.js via Node-API, 0 System dependencies!

⚠️ This project is in pre-release stage. And there may be some bugs.
For details on planned features and future direction please refer to the Roadmap.

中文文档

Install

yarn add @napi-rs/canvas
npm install @napi-rs/canvas

Support matrix

System requirement

arm64

cortex-a57 or newer CPU architecture on Linux.

All Apple M chips on macOS.

armv7

cortex-a7 or newer CPU architecture.

glibc

Since Skia relies on the glibc 2.18 API, you need to have at least glibc version >= 2.18 on your system.

AWS Lambda usage

To use this library on Lambda you will need to use a Lambda layer.

You can simply attach a lambda layer by getting an ARN from Canvas-Lambda-Layer

Make sure to exclude @napi-rs/canvas while bundling your Lambda.

Usage

const { promises } = require('node:fs')
const { join } = require('node:path')
const { createCanvas, loadImage } = require('@napi-rs/canvas')

const canvas = createCanvas(300, 320)
const ctx = canvas.getContext('2d')

ctx.lineWidth = 10
ctx.strokeStyle = '#03a9f4'
ctx.fillStyle = '#03a9f4'

// Wall
ctx.strokeRect(75, 140, 150, 110)

// Door
ctx.fillRect(130, 190, 40, 60)

// Roof
ctx.beginPath()
ctx.moveTo(50, 140)
ctx.lineTo(150, 60)
ctx.lineTo(250, 140)
ctx.closePath()
ctx.stroke()

async function main() {
  // load images from disk or from a URL
  const catImage = await loadImage('path/to/cat.png')
  const dogImage = await loadImage('https://example.com/path/to/dog.jpg')

  ctx.drawImage(catImage, 0, 0, catImage.width, catImage.height)

  ctx.drawImage(dogImage, canvas.width / 2, canvas.height / 2, dogImage.width, dogImage.height)

  // export canvas as image
  const pngData = await canvas.encode('png') // JPEG, AVIF and WebP are also supported
  // encoding in libuv thread pool, non-blocking
  await promises.writeFile(join(__dirname, 'simple.png'), pngData)
}

main()

Emoji text

const { writeFileSync } = require('fs')
const { join } = require('path')

const { createCanvas, GlobalFonts } = require('@napi-rs/canvas')

GlobalFonts.registerFromPath(join(__dirname, '..', 'fonts', '[email protected]'), 'Apple Emoji')
GlobalFonts.registerFromPath(join(__dirname, '..', '__test__', 'fonts', 'COLRv1.ttf'), 'COLRv1')

console.info(GlobalFonts.families)

const canvas = createCanvas(760, 360)
const ctx = canvas.getContext('2d')

ctx.font = '50px Apple Emoji'
ctx.strokeText('😀😃😄😁😆😅😂🤣☺️😊😊😇', 50, 150)

ctx.font = '100px COLRv1'
ctx.fillText('abc', 50, 300)

const b = canvas.toBuffer('image/png')

writeFileSync(join(__dirname, 'draw-emoji.png'), b)

Performance

See benchmark for benchmark code.

Hardware info:

OS: Windows 10 x86_64
Host: Micro-Star International Co., Ltd. MS-7C35
Kernel: 10.0.19043
Terminal: Windows Terminal
CPU: AMD Ryzen 9 5950X (32) @ 3.400GHz
Memory: 32688MiB
❯ yarn bench

> @napi-rs/[email protected] bench D:\workspace\skia-rs
> node -r @swc-node/register benchmark/bench.ts

Running "Draw house" suite...
Progress: 100%

  skia-canvas:
    26 ops/s, ±0.70%   | slowest, 29.73% slower

  node-canvas:
    30 ops/s, ±6.95%   | 18.92% slower

  @napi-rs/canvas:
    37 ops/s, ±6.30%   | fastest

Finished 3 cases!
  Fastest: @napi-rs/canvas
  Slowest: skia-canvas
Running "Draw gradient" suite...
Progress: 100%

  skia-canvas:
    36 ops/s, ±6.12%   | 14.29% slower

  node-canvas:
    34 ops/s, ±5.60%   | slowest, 19.05% slower

  @napi-rs/canvas:
    42 ops/s, ±0.53%   | fastest

Finished 3 cases!
  Fastest: @napi-rs/canvas
  Slowest: node-canvas

Features

Path2D

new Path2D()
new Path2D(path: Path2D)
// new Path2D('M108.956,403.826c0,0,0.178,3.344-1.276,3.311  c-1.455-0.033-30.507-84.917-66.752-80.957C40.928,326.18,72.326,313.197,108.956,403.826z')
new Path2D(path: string)
export interface DOMMatrix2DInit {
  a: number
  b: number
  c: number
  d: number
  e: number
  f: number
}

export class Path2D {
  constructor(path?: Path2D | string)

  addPath(path: Path2D, transform?: DOMMatrix2DInit): void
  arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean): void
  arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void
  bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void
  closePath(): void
  ellipse(
    x: number,
    y: number,
    radiusX: number,
    radiusY: number,
    rotation: number,
    startAngle: number,
    endAngle: number,
    anticlockwise?: boolean,
  ): void
  lineTo(x: number, y: number): void
  moveTo(x: number, y: number): void
  quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void
  rect(x: number, y: number, w: number, h: number): void

  // PathKit methods
  op(path: Path2D, operation: PathOp): Path2D
  toSVGString(): string
  getFillType(): FillType
  getFillTypeString(): string
  setFillType(type: FillType): void
  simplify(): Path2D
  asWinding(): Path2D
  stroke(stroke?: StrokeOptions): Path2D
  transform(transform: DOMMatrix2DInit): Path2D
  getBounds(): [left: number, top: number, right: number, bottom: number]
  computeTightBounds(): [left: number, top: number, right: number, bottom: number]
  trim(start: number, end: number, isComplement?: boolean): Path2D
  equals(path: Path2D): boolean
}

PathKit

PathKit is a toolset for manipulating Path in Skia, supporting quadratic beziers, cubic beziers and conics. The main features are.

Path Operation

.op(path, PathOp)

const pathOne = new Path2D(
  'M8 50H92C96.4183 50 100 53.5817 100 58V142C100 146.418 96.4183 150 92 150H8C3.58172 150 0 146.418 0 142V58C0 53.5817 3.58172 50 8 50Z',
)
const pathTwo = new Path2D(
  '"M58 0H142C146.418 0 150 3.58172 150 8V92C150 96.4183 146.418 100 142 100H58C53.5817 100 50 96.4183 50 92V8C50 3.58172 53.5817 0 58 0Z',
)

pathOne.op(pathTwo, PathOp.Intersect).toSVGString()
// => "M100 100L58 100C53.5817 100 50 96.4183 50 92L50 50L92 50C96.4183 50 100 53.5817 100 58L100 100Z"
  • Union, subtract the op path from the first path
  • Difference, intersect the two paths
  • ReverseDifference, union (inclusive-or) the two paths
  • Intersect, exclusive-or the two paths
  • XOR, subtract the first path from the op path

boolean-operations

Covert FillType in Path

.asWinding()

You can convert fill-rule="evenodd" to fill-rule="nonzero" in SVG. This is useful for OpenType font-related tools, as fill-rule="nonzero" is only supported in OpenType fonts.

SVG fill-rule

const pathCircle = new Path2D(
  'M24.2979 13.6364H129.394V40.9091H24.2979L14.6278 27.2727L24.2979 13.6364ZM21.9592 0C19.0246 0 16.2716 1.42436 14.571 3.82251L1.67756 22.0043C-0.559186 25.1585 -0.559186 29.387 1.67756 32.5411L14.571 50.7227C16.2716 53.1209 19.0246 54.5455 21.9592 54.5455H70.4673V68.1818H16.073C11.0661 68.1818 7.00728 72.2518 7.00728 77.2727V113.636C7.00728 118.657 11.0661 122.727 16.073 122.727H70.4673V150H84.0658V122.727H128.041C130.975 122.727 133.729 121.303 135.429 118.905L148.323 100.723C150.559 97.5686 150.559 93.3405 148.323 90.1864L135.429 72.0045C133.729 69.6064 130.975 68.1818 128.041 68.1818H84.0658V54.5455H133.927C138.934 54.5455 142.993 50.4755 142.993 45.4545V9.09091C142.993 4.07014 138.934 0 133.927 0H21.9592ZM125.702 109.091H20.6058V81.8182H125.702L135.372 95.4545L125.702 109.091Z',
)
pathCircle.setFillType(FillType.EvenOdd)
pathCircle.asWinding().toSVGString()
// => "M24.2979 13.6364L129.394 13.6364L129.394 40.9091L24.2979 40.9091L14.6278 27.2727L24.2979 13.6364ZM21.9592 0C19.0246 0 16.2716 1.42436 14.571 3.82251L1.67756 22.0043C-0.559186 25.1585 -0.559186 29.387 1.67756 32.5411L14.571 50.7227C16.2716 53.1209 19.0246 54.5455 21.9592 54.5455L70.4673 54.5455L70.4673 68.1818L16.073 68.1818C11.0661 68.1818 7.00728 72.2518 7.00728 77.2727L7.00728 113.636C7.00728 118.657 11.0661 122.727 16.073 122.727L70.4673 122.727L70.4673 150L84.0658 150L84.0658 122.727L128.041 122.727C130.975 122.727 133.729 121.303 135.429 118.905L148.323 100.723C150.559 97.5686 150.559 93.3405 148.323 90.1864L135.429 72.0045C133.729 69.6064 130.975 68.1818 128.041 68.1818L84.0658 68.1818L84.0658 54.5455L133.927 54.5455C138.934 54.5455 142.993 50.4755 142.993 45.4545L142.993 9.09091C142.993 4.07014 138.934 0 133.927 0L21.9592 0ZM125.702 109.091L20.6058 109.091L20.6058 81.8182L125.702 81.8182L135.372 95.4545L125.702 109.091Z"

Simplify Path

.simplify()

Set the path to the same non-overlapping contour as the original path area, which means that it can also remove overlapping paths.

SVG with overlapping paths (Left)

const path =
  'M2.933,89.89 L89.005,3.818 Q90.412,2.411 92.249,1.65 Q94.087,0.889 96.076,0.889 Q98.065,0.889 99.903,1.65 Q101.741,2.411 103.147,3.818 L189.22,89.89 Q190.626,91.296 191.387,93.134 Q192.148,94.972 192.148,96.961 Q192.148,98.95 191.387,100.788 Q190.626,102.625 189.219,104.032 Q187.813,105.439 185.975,106.2 Q184.138,106.961 182.148,106.961 Q180.159,106.961 178.322,106.2 Q176.484,105.439 175.077,104.032 L89.005,17.96 L96.076,10.889 L103.147,17.96 L17.075,104.032 Q15.668,105.439 13.831,106.2 Q11.993,106.961 10.004,106.961 Q8.015,106.961 6.177,106.2 Q4.339,105.439 2.933,104.032 Q1.526,102.625 0.765,100.788 Q0.004,98.95 0.004,96.961 Q0.004,94.972 0.765,93.134 Q1.526,91.296 2.933,89.89 Z'

path.simplify().toSVGString()
// => "M89.005 3.818L2.933 89.89Q1.526 91.296 0.765 93.134Q0.004 94.972 0.004 96.961Q0.004 98.95 0.765 100.788Q1.526 102.625 2.933 104.032Q4.339 105.439 6.177 106.2Q8.015 106.961 10.004 106.961Q11.993 106.961 13.831 106.2Q15.668 105.439 17.075 104.032L96.076 25.031L175.077 104.032Q176.484 105.439 178.322 106.2Q180.159 106.961 182.148 106.961Q184.138 106.961 185.975 106.2Q187.813 105.439 189.219 104.032Q190.626 102.625 191.387 100.788Q192.148 98.95 192.148 96.961Q192.148 94.972 191.387 93.134Q190.626 91.296 189.22 89.89L103.147 3.818Q101.741 2.411 99.903 1.65Q98.065 0.889 96.076 0.889Q94.087 0.889 92.249 1.65Q90.412 2.411 89.005 3.818Z"

The tiger.json was serialized from gojs/samples/tiger

node example/anime-girl.js
SVG PNG

CC-BY-SA 3.0 by Niabot

CC-BY-SA 3.0 by Niabot

Building

Build skia from source

You can build this project from source, the system requirements are here: https://skia.org/docs/user/build

# Clone the code:
$ git clone --recurse-submodules https://github.com/Brooooooklyn/canvas.git
$ cd canvas

# Build Skia:
$ node scripts/build-skia.js

# Install NPM packages, build the Node.js addon:
$ npm install -g yarn
$ yarn install --mode=skip-build # Here are modules that are used for benchmarking and are hard to install, you can skip it by specifying `--mode=skip-build`
$ sudo dnf install clang # https://fedora.pkgs.org/34/fedora-x86_64/clang-12.0.0-0.3.rc1.fc34.x86_64.rpm.html
$ yarn build

# All done! Run test cases or examples now:
$ yarn test
$ node example/tiger.js

Pull pre-build skia binary from GitHub

You can pull skia pre-build binaries if you just care the Rust part:

# Clone the code:
$ git clone --recurse-submodules https://github.com/Brooooooklyn/canvas.git
$ cd canvas

# Download Skia binaries:
# It will pull the binaries match the git hash in `./skia` submodule
$ node scripts/release-skia-binary.mjs --download

# Install NPM packages, build the Node.js addon:
$ npm install -g yarn
$ yarn install --mode=skip-build
$ sudo dnf install clang # https://fedora.pkgs.org/34/fedora-x86_64/clang-12.0.0-0.3.rc1.fc34.x86_64.rpm.html
$ yarn build

# All done! Run test cases or examples now:
$ yarn test
$ node example/tiger.js

canvas's People

Contributors

almeidx avatar brooooooklyn avatar dependabot[bot] avatar domoritz avatar doodlewind avatar harryallen1 avatar jeve-stobs avatar jokalliauer avatar jrc03c avatar meihuanyu avatar meloalright avatar nieyuyao avatar nomagick avatar ragingwind avatar rambo-panda avatar renovate-bot avatar renovate[bot] avatar shivamjoker avatar sohamksuvarna avatar specialcoder avatar styfle avatar superchupudev avatar thisnils avatar twlite avatar wangjue666 avatar yisibl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

canvas's Issues

SVG?

Seems like SkImage does not load SVGs? Do I have to manually parse svg and load or is there other way of doing that?

Add the API to convert SVG text or textPath to path.

For example, I have the following SVG, hoping to convert the text into a <path>, and generate the corresponding SVG string.

resvg can get the correct result: RazrFalcon/resvg#364

<svg height="1024px" width="2000px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <path d="M32,87.344c0,327.678,265.635,593.313,593.313,593.313 M625.313,680.656C827.829,680.656,992,516.485,992,313.969 M992,313.969c0-125.162-101.464-226.625-226.625-226.625 M765.375,87.344c-77.354,0-140.063,62.708-140.063,140.062 M625.313,227.406c0,47.808,38.756,86.563,86.563,86.563 M711.876,313.969c29.546,0,53.499-23.953,53.499-53.5 M765.375,260.47c0-18.261-14.804-33.064-33.064-33.064 M732.311,227.406c-11.286,0-20.435,9.149-20.435,20.435" fill="none" id="id1" stroke-width="1"/>
    <text fill="#000" font-family="Noto Sans" font-size="29" font-weight="bold" id="id2" stroke="none">
        <textPath startOffset="100%" text-anchor="end" href="#id1">abc12345640123456723456789010111213141516171819202122232425262728293031323334353637383940123456789010111213141516171819202122232425262728293031323334353637383940xyz</textPath>
    </text>
</svg>

image

Text Align does not work

ctx.textAlign makes text disappear.

Setting textAlign to center/right/left makes the text disappear, I think this is a bug

Add editorconfig

统一缩进、换行,减少 code review 时的噪音。需要讨论一下各个语言中的缩进规则。

Gif support

Ref: https://docs.rs/gif

// https://docs.rs/gif/0.11.2/gif/enum.ExtensionData.html
const gifCanvas= createGifCanvas(1024, 768, Extensions)

const ctx = gifCanvas.createFrame() // raw canvas rendering context

gifCanvas.encode() // gif buffer Promise<Buffer>

Image API and implementation

For the Image class to be used with drawImage, how to specify the minimal usable API? I've tried the simplest approach that accepts image.src as decoded bitmap buffer, but this is not appropriate, since the buffer itself doesn't contain image size. And if we make the image.width writable from JS, wrong buffer size can easily lead to segfault.

So I propose to hide the decode API and support setting image.src with JPG/PNG file buffer. This requires us to decode bitmap using SkCodec or thiry party libraries like stb_image. For the former approach I'll need to add some glue code in skia-c and sk.rs, is my understanding correct?

Use stricter version control for binary dependencies

I notice that the versions of all optional dependencies start with ^. For example in @napi-rs/[email protected]:

{
  "optionalDependencies": {
    "@napi-rs/canvas-win32-x64-msvc": "^0.1.6",
    "@napi-rs/canvas-darwin-x64": "^0.1.6",
    "@napi-rs/canvas-linux-x64-gnu": "^0.1.6",
    "@napi-rs/canvas-linux-arm-gnueabihf": "^0.1.6",
    "@napi-rs/canvas-linux-x64-musl": "^0.1.6",
    "@napi-rs/canvas-linux-arm64-gnu": "^0.1.6",
    "@napi-rs/canvas-linux-arm64-musl": "^0.1.6",
    "@napi-rs/canvas-darwin-arm64": "^0.1.6",
    "@napi-rs/canvas-android-arm64": "^0.1.6"
  }
}

It will cause problems when user want to install old versions without an old lock file.

When you install v0.1.6, the binary dependencies of v0.1.7 will be used. If the versions of these binary dependencies are in sync with the version of @napi-rs/canvas, we can fixed the version (remove ^).

Otherwise, we can use ~ instead of ^, and increase minor version number when there are some breaking changes. This can ensure that the performance of old versions will not be affected.

fillText crashes on Windows

Following error messages are raised when calling fillText on Windows:

$ node example/text.js
SkIcuLoader: datafile missing: C:\Program Files\nodejs\icudtl.dat
SkIcuLoader: datafile missing: \\?\C:\Users\ewind\Desktop\code\canvas\icudtl.dat
Segmentation fault

Providing icudtl.dat manually can fix this issue. This file contains ICU locales and is specially handled by Skia on Windows. By default the out/Static/icudtl.dat is generated with Skia build, to my understanding this file needs to ship in the same path with the skia.win32-x64-msvc.node.

Test case failed on macOS

My build is based on latest main branch, 3ab6aa4:

➜  canvas git:(3ab6aa4) npm test

> @napi-rs/[email protected] test /Users/ewind/code/canvas
> ava


  draw.ts › createPattern-no-transform-imagedata

  __test__/draw.spec.ts:243

   242:   const imageData = new ImageData(new Uint8ClampedArray(imageMeta.data), imageMeta.width, imageMeta.height)
   243:   const pattern = ctx.createPattern(imageData, 'repeat')                                                   
   244:   ctx.fillStyle = pattern                                                                                  

  Rejected promise returned by test. Reason:

  Error {
    code: 'InvalidArg',
    message: '',
  }

  › CanvasRenderingContext2D.createPattern (index.js:21:21)
  › __test__/draw.spec.ts:243:1

  ─

  1 test failed
  4 tests todo
npm ERR! Test failed.  See above for more details.

My local skia binary is updated with node scripts/build-skia.js, am I missing some other commands?

[Feature request] Support font family name with spaces

For example:

// GlobalFonts.registerFromPath('path/to/font', 'Microsoft YaHei');

ctx.font = '100px "Microsoft YaHei"';
// or
ctx.font = "100px 'Microsoft YaHei'";

By the way, is there a way to remove warning SkIcuLoader: datafile missing: xxx\icudtl.dat.?

Failed to load module with eggjs on Centos7

[egg-scripts] Got error when startup:
[egg-scripts] 2021-10-11 11:58:42,760 ERROR 30307 nodejs.Error: Can not load bindings, file: /app/www/inv/node_modules/@napi-rs/canvas-linux-x64-gnu/skia.linux-x64-gnu.node existed but error occurred while require it: /app/www/mgt/node_modules/@napi-rs/canvas-linux-x64-gnu/skia.linux-x64-gnu.node: undefined symbol: _ZTVNSt7__cxx1115basic_stringbufIcSt11char_traitsIcESaIcEEE
[egg-scripts] file: /app/www/inv-/node_modules/@napi-rs/canvas-linux-x64-musl/skia.linux-x64-musl.node existed but error occurred while require it: /app/www/invnode_modules/@napi-rs/canvas-linux-x64-musl/skia.linux-x64-musl.node: undefined symbol: fstat64
[egg-scripts] Installed packages: [canvas-linux-x64-gnu, canvas-linux-x64-musl]
[egg-scripts] file: /app/www/inv/node_modules/@napi-rs/canvas-linux-x64-musl/skia.linux-x64-musl.node existed but error occurred while require it: /app/www/in/node_modules/@napi-rs/canvas-linux-x64-musl/skia.linux-x64-musl.node: undefined symbol: fstat64
[egg-scripts] Installed packages: [canvas-linux-x64-gnu, canvas-linux-x64-musl]
[egg-scripts]     at loadBinding (/app/www/inv/node_modules/@node-rs/helper/lib/loader.js:50:11)
[egg-scripts]     at Object.<anonymous> (/app/www/inv/node_modules/@napi-rs/canvas/index.js:25:5)
[egg-scripts]     at Module._compile (internal/modules/cjs/loader.js:999:30)
[egg-scripts]     at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
[egg-scripts]     at Module.load (internal/modules/cjs/loader.js:863:32)
[egg-scripts]     at Function.Module._load (internal/modules/cjs/loader.js:708:14)
[egg-scripts]     at Module.require (internal/modules/cjs/loader.js:887:19)
[egg-scripts]     at require (internal/modules/cjs/helpers.js:74:18)
[egg-scripts]     at Object.<anonymous> (/app/www/inv/dist/utils/veri-screenshot.js:3:46)
[egg-scripts]     at Module._compile (internal/modules/cjs/loader.js:999:30)
[egg-scripts]
[egg-scripts] pid: 30307
[egg-scripts] hostname: paas.g1
[egg-scripts]
[egg-scripts] 2021-10-11 11:58:42,761 ERROR 30307 [agent_worker] start error, exiting with code:1
[egg-scripts] 2021-10-11 11:58:42,785 ERROR 30298 nodejs.AgentWorkerDiedError: [master] agent_worker#1:30307 died (code: 0, signal: null)
[egg-scripts]     at Master.onAgentExit (/app/www/inv/node_modules/egg-cluster/lib/master.js:425:17)
[egg-scripts]     at Master.emit (events.js:314:20)
[egg-scripts]     at Messenger.sendToMaster (/app/www/inv/node_modules/egg-cluster/lib/utils/messenger.js:137:17)
[egg-scripts]     at Messenger.send (/app/www/inv/node_modules/egg-cluster/lib/utils/messenger.js:102:12)
[egg-scripts]     at ChildProcess.<anonymous> (/app/www/inv/node_modules/egg-cluster/lib/master.js:289:22)
[egg-scripts]     at Object.onceWrapper (events.js:421:26)
[egg-scripts]     at ChildProcess.emit (events.js:314:20)
[egg-scripts]     at Process.ChildProcess._handle.onexit (internal/child_process.js:276:12)
[egg-scripts] name: "AgentWorkerDiedError"
[egg-scripts] pid: 30298
[egg-scripts] hostname: paas.g1
[egg-scripts]
[egg-scripts] 2021-10-11 11:58:42,785 ERROR 30298 [master] agent_worker#1:30307 start fail, exiting with code:1
[egg-scripts] 2021-10-11 11:58:42,786 ERROR 30298 [master] exit with code:1
[egg-scripts]
[egg-scripts] Start got error, see /home/user/logs/master-stderr.log
[egg-scripts] Or use `--ignore-stderr` to ignore stderr at startup.
Done in 3.90s.

why rust?

I understand this package based on skia and export api for node environment, but why not node c++ extend.

Rust toolchain config looks too aggressive

The rust-toolchain file is now set to nighty, while it may not ensure that all rust components can be successfully installed. According to the rustup components history, rls is missing in builds in recent two days, leading to the rls unavailable issue that prevents VSCode doing IntelliSense. For now I config the rust-toolchain to nightly-2021-03-25 locally to workaround this issue, but I'm wondering if we have enough reason not to use stable rust toolchain?

Performance down after upgrade skia@chrome/m88 to skia@chrome/90

Here are some benchmark samples fould in CI:

chrome/m88 with -fno-rtti

Log from https://github.com/Brooooooklyn/canvas/runs/2104887765

Running "Draw house" suite...
Progress: 50%

  @napi-rs/skia:
    23 ops/s, ±0.16% 
Progress: 100%

  @napi-rs/skia:
    23 ops/s, ±0.16%   | fastest

  node-canvas:
    18 ops/s, ±0.75%   | slowest, 21.74% slower

Finished 2 cases!
  Fastest: @napi-rs/skia
  Slowest: node-canvas
Running "Draw gradient" suite...
Progress: 50%

  @napi-rs/skia:
    23 ops/s, ±0.23% 
Progress: 100%

  @napi-rs/skia:
    23 ops/s, ±0.23%   | fastest

  node-canvas:
    17 ops/s, ±0.20%   | slowest, 26.09% slower

chrome/m88 without -fno-rtti

Log from https://github.com/Brooooooklyn/canvas/runs/1917495451 (in #171 , we remove -fno-rtti flag to make ICU compling passed, but still keptchrome/m88 version)

Benchmarks run many times in this branch, the results was very close to this one below, and @napi-rs/skia is still faster then node-canvas, which backend is cario

 @napi-rs/skia:
    23 ops/s, ±1.27% 
Progress: 100%

  @napi-rs/skia:
    23 ops/s, ±1.27%   | fastest

  node-canvas:
    21 ops/s, ±2.04%   | slowest, 8.7% slower

Finished 2 cases!
  Fastest: @napi-rs/skia
  Slowest: node-canvas
Running "Draw gradient" suite...
Progress: 50%

  @napi-rs/skia:
    22 ops/s, ±1.01% 
Progress: 100%

  @napi-rs/skia:
    22 ops/s, ±1.01%   | fastest

  node-canvas:
    20 ops/s, ±1.40%   | slowest, 9.09% slower

chrome/m90 without -fno-rtti

Log from https://github.com/Brooooooklyn/canvas/runs/2202890170, the first commit which upgrade to m90

 @napi-rs/skia:
    20 ops/s, ±1.42% 
Progress: 100%

  @napi-rs/skia:
    20 ops/s, ±1.42%   | slowest, 4.76% slower

  node-canvas:
    21 ops/s, ±1.30%   | fastest

Finished 2 cases!
  Fastest: node-canvas
  Slowest: @napi-rs/skia
Running "Draw gradient" suite...
Progress: 50%

  @napi-rs/skia:
    19 ops/s, ±1.10% 
Progress: 100%

  @napi-rs/skia:
    19 ops/s, ±1.10%   | slowest, 5% slower

  node-canvas:
    20 ops/s, ±1.68%   | fastest

Finished 2 cases!
  Fastest: node-canvas
  Slowest: @napi-rs/skia

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Awaiting Schedule

These updates are awaiting their schedule. Click on a checkbox to get an update now.

  • chore(deps): lock file maintenance

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Ignored or Blocked

These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.

Detected dependencies

cargo
Cargo.toml
  • anyhow 1
  • base64 0.22
  • cssparser 0.29
  • infer 0.16
  • libavif 0.14
  • napi 2
  • napi-derive 2
  • nom 7
  • num_cpus 1
  • once_cell 1
  • regex 1
  • rgb 0.8
  • serde 1
  • serde_derive 1
  • serde_json 1
  • thiserror 1
  • cc 1
  • napi-build 2
  • mimalloc 0.1
  • mimalloc 0.1
dockerfile
Dockerfile
aarch64.Dockerfile
jammy-armv7.Dockerfile
  • ubuntu 24.04
musl.Dockerfile
  • node 18-alpine
github-actions
.github/actions/setup-rust/action.yaml
  • actions/cache v4
.github/workflows/CI.yaml
  • actions/checkout v4
  • actions/setup-node v4
  • actions/checkout v4
  • actions/setup-node v4
  • docker/setup-qemu-action v3
  • docker/setup-buildx-action v3
  • goto-bus-stop/setup-zig v2
  • ilammy/setup-nasm v1
  • addnab/docker-run-action v3
  • actions/upload-artifact v4
  • actions/checkout v4
  • robinraju/release-downloader v1
  • actions/setup-node v4
  • actions/setup-node v4
  • actions/download-artifact v4
  • actions/upload-artifact v4
  • actions/checkout v4
  • robinraju/release-downloader v1
  • actions/setup-node v4
  • actions/download-artifact v4
  • actions/upload-artifact v4
  • actions/checkout v4
  • robinraju/release-downloader v1
  • actions/setup-node v4
  • actions/download-artifact v4
  • actions/upload-artifact v4
  • actions/checkout v4
  • robinraju/release-downloader v1
  • actions/download-artifact v4
  • docker/setup-qemu-action v3
  • addnab/docker-run-action v3
  • actions/upload-artifact v4
  • actions/checkout v4
  • robinraju/release-downloader v1
  • actions/download-artifact v4
  • docker/setup-qemu-action v3
  • addnab/docker-run-action v3
  • actions/upload-artifact v4
  • actions/checkout v4
  • robinraju/release-downloader v1
  • actions/download-artifact v4
  • docker/setup-qemu-action v3
  • addnab/docker-run-action v3
  • actions/upload-artifact v4
  • actions/checkout v4
  • actions/checkout v4
  • actions/setup-node v4
  • rhysd/github-action-benchmark v1
  • rhysd/github-action-benchmark v1
  • actions/checkout v4
  • actions/setup-node v4
  • actions/download-artifact v4
  • macos 14
.github/workflows/docker.yml
  • actions/checkout v4
  • docker/login-action v3
  • addnab/docker-run-action v3
  • docker/build-push-action v6
.github/workflows/skia.yaml
  • actions/checkout v4
  • actions/setup-node v4
  • actions/setup-python v5
  • docker/login-action v3
  • actions/checkout v4
  • actions/setup-node v4
  • actions/setup-python v5
  • actions/checkout v4
  • actions/setup-node v4
  • docker/login-action v3
  • actions/checkout v4
  • actions/setup-node v4
  • actions/setup-python v5
  • actions/checkout v4
  • actions/setup-node v4
  • docker/login-action v3
  • actions/checkout v4
  • actions/setup-node v4
  • actions/setup-python v5
  • docker/setup-qemu-action v3
  • docker/setup-buildx-action v3
  • actions/checkout v4
  • actions/setup-node v4
  • actions/setup-python v5
npm
npm/android-arm64/package.json
  • node >= 10
npm/darwin-arm64/package.json
  • node >= 10
npm/darwin-x64/package.json
  • node >= 10
npm/linux-arm-gnueabihf/package.json
  • node >= 10
npm/linux-arm64-gnu/package.json
  • node >= 10
npm/linux-arm64-musl/package.json
  • node >= 10
npm/linux-x64-gnu/package.json
  • node >= 10
npm/linux-x64-musl/package.json
  • node >= 10
npm/win32-x64-msvc/package.json
  • node >= 10
package.json
  • @jimp/core ^0.22.10
  • @jimp/custom ^0.22.10
  • @jimp/jpeg ^0.22.10
  • @jimp/png ^0.22.10
  • @napi-rs/cli ^2.18.0
  • @octokit/rest ^21.0.0
  • @swc-node/register ^1.8.0
  • @swc/core ^1.4.0
  • @taplo/cli ^0.7.0
  • @types/lodash ^4.14.202
  • @types/node ^20.11.16
  • @types/semver ^7
  • ava ^6.1.1
  • benny ^3.7.1
  • canvas ^2.11.2
  • canvaskit-wasm ^0.39.1
  • colorette ^2.0.20
  • conventional-changelog-cli ^5.0.0
  • echarts ^5.4.3
  • husky ^9.0.10
  • lint-staged ^15.2.1
  • lodash ^4.17.21
  • npm-run-all2 ^6.1.2
  • oxlint ^0.7.0
  • pinst ^3.0.0
  • png.js ^0.2.1
  • prettier ^3.2.5
  • pretty-bytes ^6.1.1
  • semver ^7.5.4
  • skia-canvas ^1.0.1
  • table ^6.8.1
  • typescript ^5.3.3
  • node >= 10
  • yarn 4.4.0

  • Check this box to trigger a request for Renovate to run again on this repository

Roadmap

0.1. The first usable version

Canvas

  • createCanvas
  • getContext
  • png
  • jpeg
  • svg
  • pdf
  • avif
  • gif
  • webp
  • data
  • toDataURL

CanvasRenderingContext2D

Property

  • canvas
  • fillStyle
  • filter
  • font
  • globalAlpha
  • globalCompositeOperation
  • imageSmoothingEnabled
  • imageSmoothingQuality
  • lineCap
  • lineDashOffset
  • lineJoin
  • lineWidth
  • miterLimit
  • shadowBlur
  • shadowColor
  • shadowOffsetX
  • shadowOffsetY
  • strokeStyle
  • textAlign
  • textBaseline
  • letterSpacing
  • wordSpacing
  • fontStretch
  • fontKerning
  • fontVariantCaps

Methods

  • arc
  • arcTo
  • beginPath
  • bezierCurveTo
  • clearRect
  • clip
  • closePath
  • createImageData
  • createLinearGradient
  • createPattern
  • createRadialGradient
  • drawImage
  • ellipse
  • fill
  • fillRect
  • fillText
  • getContextAttributes
  • getImageData
  • getLineDash
  • getTransform
  • isPointInPath
  • isPointInStroke
  • lineTo
  • measureText
  • moveTo
  • putImageData
  • quadraticCurveTo
  • rect
  • resetTransform
  • restore
  • rotate
  • save
  • scale
  • setLineDash
  • setTransform
  • stroke
  • strokeRect
  • strokeText
  • transform
  • translate

Path2D

  • Constructor
  • addPath
  • closePath
  • moveTo
  • lineTo
  • bezierCurveTo
  • quadraticCurveTo
  • arc
  • arcTo
  • ellipse
  • rect

Text rendering

  • maxWidth
  • font weight
  • font variant
  • font style

ImageData

Constructor

  • width/height
  • Uint8ClampedArray/width
  • Uint8ClampedArray/width/height

Property

  • data
  • width
  • height

Image

Property

  • width
  • height
  • alt
  • complete
  • crossOrigin
  • currentSrc
  • decoding
  • loading
  • naturalWidth
  • naturalHeight

Methods

  • decode

0.2. Testing, code cleanup and performance

0.3. API stable, documents

1.0

Error: /lib64/libc.so.6: version `GLIBC_2.18' not found

There is an issue when I require it:

Welcome to Node.js v14.18.1.
Type ".help" for more information.
> const { createCanvas } = require('@napi-rs/canvas')
Uncaught:
Error: /lib64/libc.so.6: version `GLIBC_2.18' not found (required by /home/opc/node_modules/@napi-rs/canvas-linux-x64-gnu/skia.linux-x64-gnu.node)
    at Object.Module._extensions..node (internal/modules/cjs/loader.js:1144:18)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:12)
    at Module.require (internal/modules/cjs/loader.js:974:19)
    at require (internal/modules/cjs/helpers.js:93:18) {
  code: 'ERR_DLOPEN_FAILED'
}
>

OS: Oracle Linux 7.2

"Segmentation fault" in worker thread on ubuntu

When running the library inside a worker thread on ubuntu (I had no problems running the exact same code on windows, same library and nodejs version), the program exits with the message "Segmentation fault". The image gets rendered and transferred to the main thread correctly, but as soon as the worker exits, the main thread will terminate too.

Example code:

// index.js
const { Worker } = require('worker_threads');
const { promises: fs } = require('fs');

const worker = new Worker('./worker.js');
worker.on('message', async (m) => {
  await fs.writeFile('./out.png', m);
});
// worker.js
const { parentPort } = require('worker_threads');
const { createCanvas } = require('@napi-rs/canvas');

const canvas = createCanvas(1000, 1000);
const ctx = canvas.getContext('2d');

ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);

const pngBuffer = canvas.toBuffer('image/png');
parentPort.postMessage(pngBuffer);

Output:

# node index.js
Segmentation fault

Versions:
NodeJS: v16.11.1
@napi-rs/canvas: 0.1.13
Ubuntu: 18.04.5 LTS

TextMetrics support plan

For the measureText API in roadmap, here are all the required properties to support:

  • actualBoundingBoxAscent
  • actualBoundingBoxDescent
  • actualBoundingBoxLeft
  • actualBoundingBoxRight
  • alphabeticBaseline
  • emHeightAscent
  • emHeightDescent
  • fontBoundingBoxAscent
  • fontBoundingBoxDescent
  • hangingBaseline
  • ideographicBaseline
  • width

I may look into #125 and file a new PR implementing TextMetrics soon. @Brooooooklyn please let me know if you have other timeframe about this part.

Glibc compatible issue

We are compiling skia-rs in ubuntu-latest image, which means native addon's is linking against with higher versions GLIBC than most users have.

objdump -p skia.linux-x64-gnu.node

skia.linux-x64-gnu.node:     file format elf64-x86-64

Program Header:
    LOAD off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**12
         filesz 0x000000000002e9a0 memsz 0x000000000002e9a0 flags r--
    LOAD off    0x000000000002f000 vaddr 0x000000000002f000 paddr 0x000000000002f000 align 2**12
         filesz 0x0000000000349821 memsz 0x0000000000349821 flags r-x
    LOAD off    0x0000000000379000 vaddr 0x0000000000379000 paddr 0x0000000000379000 align 2**12
         filesz 0x00000000000aafec memsz 0x00000000000aafec flags r--
    LOAD off    0x00000000004244c0 vaddr 0x00000000004254c0 paddr 0x00000000004254c0 align 2**12
         filesz 0x0000000000020360 memsz 0x000000000022fe18 flags rw-
 DYNAMIC off    0x000000000043af20 vaddr 0x000000000043bf20 paddr 0x000000000043bf20 align 2**3
         filesz 0x0000000000000240 memsz 0x0000000000000240 flags rw-
    NOTE off    0x0000000000000270 vaddr 0x0000000000000270 paddr 0x0000000000000270 align 2**2
         filesz 0x0000000000000024 memsz 0x0000000000000024 flags r--
     TLS off    0x00000000004244c0 vaddr 0x00000000004254c0 paddr 0x00000000004254c0 align 2**5
         filesz 0x0000000000001800 memsz 0x00000000000018d8 flags r--
EH_FRAME off    0x00000000003bdf58 vaddr 0x00000000003bdf58 paddr 0x00000000003bdf58 align 2**2
         filesz 0x000000000000ee34 memsz 0x000000000000ee34 flags r--
   STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
         filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
   RELRO off    0x00000000004244c0 vaddr 0x00000000004254c0 paddr 0x00000000004254c0 align 2**0
         filesz 0x0000000000017b40 memsz 0x0000000000017b40 flags r--

Dynamic Section:
  NEEDED               libfontconfig.so.1
  NEEDED               libpthread.so.0
  NEEDED               libdl.so.2
  NEEDED               libgcc_s.so.1
  NEEDED               libc.so.6
  NEEDED               ld-linux-x86-64.so.2
  NEEDED               libm.so.6
  INIT                 0x000000000002f000
  FINI                 0x0000000000378814
  INIT_ARRAY           0x0000000000426cc0
  INIT_ARRAYSZ         0x0000000000000018
  FINI_ARRAY           0x0000000000426cd8
  FINI_ARRAYSZ         0x0000000000000008
  GNU_HASH             0x0000000000000298
  STRTAB               0x0000000000001a78
  SYMTAB               0x00000000000002c0
  STRSZ                0x00000000000012c8
  SYMENT               0x0000000000000018
  PLTGOT               0x000000000043c160
  PLTRELSZ             0x0000000000000f90
  PLTREL               0x0000000000000007
  JMPREL               0x000000000002da10
  RELA                 0x00000000000030e0
  RELASZ               0x000000000002a930
  RELAENT              0x0000000000000018
  FLAGS                0x0000000000000008
  FLAGS_1              0x0000000000000001
  VERNEED              0x0000000000002f40
  VERNEEDNUM           0x0000000000000006
  VERSYM               0x0000000000002d40
  RELACOUNT            0x0000000000001b94

Version References:
  required from libdl.so.2:
    0x09691a75 0x00 15 GLIBC_2.2.5
  required from ld-linux-x86-64.so.2:
    0x0d696913 0x00 13 GLIBC_2.3
  required from libgcc_s.so.1:
    0x09276060 0x00 17 GCC_4.2.0
    0x0b792653 0x00 14 GCC_3.3
    0x0b792650 0x00 06 GCC_3.0
  required from libm.so.6:
    0x06969189 0x00 08 GLIBC_2.29
    0x09691a75 0x00 07 GLIBC_2.2.5
    0x06969187 0x00 05 GLIBC_2.27
  required from libpthread.so.0:
    0x09691973 0x00 19 GLIBC_2.3.3
    0x06969192 0x00 11 GLIBC_2.12
    0x09691972 0x00 04 GLIBC_2.3.2
    0x09691a75 0x00 03 GLIBC_2.2.5
  required from libc.so.6:
    0x06969194 0x00 21 GLIBC_2.14
    0x09691974 0x00 20 GLIBC_2.3.4
    0x0d696914 0x00 18 GLIBC_2.4
    0x0d696916 0x00 16 GLIBC_2.6
    0x06969198 0x00 12 GLIBC_2.18
    0x09691972 0x00 10 GLIBC_2.3.2
    0x06969197 0x00 09 GLIBC_2.17
    0x09691a75 0x00 02 GLIBC_2.2.5

WebAssembly

WebAssembly could be built via wasm-bindgen.

fillText not working

Hello, thanks for creating this library! It's much less painful to use than node-canvas because it doesn't work on new Node.js versions...

At the moment I'm facing a little issue with canvas. Even though fillText has been added to canvas already, it doesn't seem to be working for me.

This is the code:

import { createCanvas } from '@napi-rs/canvas'
import fs from 'fs/promises'
import path from 'path'

const canvas = createCanvas(1000, 1000)

const ctx = canvas.getContext('2d')

ctx.fillStyle = 'red'
ctx.font = '30px Noto Serif'
ctx.textAlign = 'center'
ctx.fillText('Hello World', 500, 500)

export function generateImage(title: string) {
  try {
    return canvas.encode('png')
  } catch (e) {
    console.log('Failed to encode image: ' + e.message)
    throw e
  }
}

and this is what I get:

test

System info

  • Node.js 16.5.0
  • Linux 5.10.49-1-MANJARO
  • @napi-rs/canvas 0.0.12

Additional context

skia-canvas seems to work fine

Get the width and height from the viewBox

When the read SVG file does not have width/height, the width and height should be obtained from the viewBox.

<svg viewBox="0 0 300 300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <rect fill="#2498EE" x="0" y="0" width="300" height="300"></rect>
    </g>
</svg>

Of course, I think the final solution still needs to get the BBox of SVG. The width and height of the generated picture can be set safely and freely through BBox.

Support scaling SVG

Currently, when drawing a small SVG on a larger canvas, it will become blurred, which loses the advantage of SVG as a vector format.

Fundamentally, I hope to get SVG BBox( .getBBox()), which will make skr-canvas operation SVG very flexible.

This can solve the problem: Automattic/node-canvas#1555

Local fresh build produces empty image output

Hi, I'm trying to build and run this project locally, the libskia.a and the skia.node addon can be successfully built, but for the simple and tiger examples, I can only get transparent images as output.

Commands I used for building:

$ git clone --recurse-submodules https://github.com/Brooooooklyn/skia-rs.git
$ cd skia-rs
$ node scripts/build-skia.js
$ yarn install
$ npm run build

And commands for testing:

$ npm test # success
$ node examples/simple.js # blank image
$ node examples/tiger.js # blank image

It's worth to note that during yarn install, the canvas dependency installation fails on the final node-pre-gyp build stage (both on WSL2 and macOS 10.15). I believe other dependencies are installed correctly.

simple.png:

simple

tiger.png:

tiger

I've tried on WSL2 and macOS and got the same output result, wondering if I'm missing some configurations :(

Unable to set transform

ctx.transform throws error when setting transform. This transform config comes from MDN:

const { createCanvas } = require('../index')
const canvas = createCanvas(1024, 768)
const ctx = canvas.getContext('2d')

ctx.transform(1, 0, 1.7, 1, 0, 0)
ctx.fillStyle = 'gray'
ctx.fillRect(40, 40, 50, 20)
ctx.fillRect(40, 90, 50, 20)

GlobalFonts.families contains the font file path

This helps us to debug and understand the specific path of the font in the system.

Like resvg --list-fonts flag output(Mac):

/Users/username/Library/Fonts/HarmonyOS_Sans_Thin.ttf: 'HarmonyOS Sans', 0, Normal, 250, Normal
/Users/username/Library/Fonts/SourceHanSansSC-Normal.otf: 'Source Han Sans SC Normal', 0, Normal, 350, Normal

font string parsing issue

Test case:

ctx.font = '50px'

For the Rust Font struct, this will set font.size to 50 and font.family to "px" (instead of "sans-serif" by default).

textAlign is not working

SKRSContext2D#textAlign is not working correctly. "right" and "center" seem to position the text relative to the right border of the canvas (examples below). "left" made the text completely disappear.

Example 1:

ctx.textAlign = 'center';
ctx.fillText('CENTERED TEXT', canvas.width / 2, canvas.height / 2);

Output:

Example 2:

ctx.textAlign = 'right';
ctx.fillText('CENTERED RIGHT TEXT', canvas.width / 2, canvas.height / 2);

Output:

Versions:
NodeJS: v16.11.1
@napi-rs/canvas: v0.1.13

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.