Giter Club home page Giter Club logo

pixie's Introduction

πŸ‘ πŸ‘ πŸ‘ Check out video about the library: A full-featured 2D graphics library for Nim (NimConf 2021) πŸ‘ πŸ‘ πŸ‘

Pixie - A full-featured 2D graphics library for Nim

Pixie is a 2D graphics library similar to Cairo and Skia written entirely in Nim.

This library is being actively developed and we'd be happy for you to use it.

nimble install pixie

Github Actions

API reference

Pixie Book

Videos

Features:

  • Typesetting and rasterizing text, including styled rich text via spans.
  • Drawing paths, shapes and curves with even-odd and non-zero windings.
  • Pixel-perfect AA quality.
  • Supported file formats are PNG, BMP, JPG, SVG + more in development.
  • Strokes with joins and caps.
  • Shadows, glows and blurs.
  • Complex masking: Subtract, Intersect, Exclude.
  • Complex blends: Darken, Multiply, Color Dodge, Hue, Luminosity... etc.
  • Many operations are SIMD accelerated.

Image file formats

Format Read Write
PNG βœ… βœ…
JPEG βœ…
BMP βœ… βœ…
QOI βœ… βœ…
GIF βœ…
SVG βœ…
PPM βœ… βœ…

Font file formats

Format Read
TTF βœ…
OTF βœ…
SVG βœ…

Joins and caps

Supported Caps:

  • Butt
  • Round
  • Square

Supported Joins:

  • Miter (with miter angle limit)
  • Bevel
  • Round

Blending & masking

Supported Blend Modes:

  • Normal
  • Darken
  • Multiply
  • ColorBurn
  • Lighten
  • Screen
  • Color Dodge
  • Overlay
  • Soft Light
  • Hard Light
  • Difference
  • Exclusion
  • Hue
  • Saturation
  • Color
  • Luminosity

Supported Mask Modes:

  • Mask
  • Overwrite
  • Subtract Mask
  • Intersect Mask
  • Exclude Mask

SVG style paths:

Format Supported Description
M m βœ… move to
L l βœ… line to
H h βœ… horizontal line to
V v βœ… vertical line to
C c S s βœ… cubic curve to
Q q T t βœ… quadratic curve to
A a βœ… arc to
z βœ… close path

Pixie + GPU

To learn how to use Pixie for realtime graphics with GPU, check out Boxy.

Testing

nimble test

Examples

git clone https://github.com/treeform/pixie to run examples.

Text

nim c -r examples/text.nim

var font = readFont("examples/data/Roboto-Regular_1.ttf")
font.size = 20

let text = "Typesetting is the arrangement and composition of text in graphic design and publishing in both digital and traditional medias."

image.fillText(font.typeset(text, vec2(180, 180)), translate(vec2(10, 10)))

example output

Text spans

nim c -r examples/text_spans.nim

let typeface = readTypeface("examples/data/Ubuntu-Regular_1.ttf")

proc newFont(typeface: Typeface, size: float32, color: Color): Font =
  result = newFont(typeface)
  result.size = size
  result.paint.color = color

let spans = @[
  newSpan("verb [with object] ",
    newFont(typeface, 12, color(0.78125, 0.78125, 0.78125, 1))),
  newSpan("strallow\n", newFont(typeface, 36, color(0, 0, 0, 1))),
  newSpan("\nstralΒ·low\n", newFont(typeface, 13, color(0, 0.5, 0.953125, 1))),
  newSpan("\n1. free (something) from restrictive restrictions \"the regulations are intended to strallow changes in public policy\" ",
      newFont(typeface, 14, color(0.3125, 0.3125, 0.3125, 1)))
]

image.fillText(typeset(spans, vec2(180, 180)), translate(vec2(10, 10)))

example output

Square

nim c -r examples/square.nim

let ctx = newContext(image)
ctx.fillStyle = rgba(255, 0, 0, 255)

let
  pos = vec2(50, 50)
  wh = vec2(100, 100)

ctx.fillRect(rect(pos, wh))

example output

Line

nim c -r examples/line.nim

let ctx = newContext(image)
ctx.strokeStyle = "#FF5C00"
ctx.lineWidth = 10

let
  start = vec2(25, 25)
  stop = vec2(175, 175)

ctx.strokeSegment(segment(start, stop))

example output

Rounded rectangle

nim c -r examples/rounded_rectangle.nim

let ctx = newContext(image)
ctx.fillStyle = rgba(0, 255, 0, 255)

let
  pos = vec2(50, 50)
  wh = vec2(100, 100)
  r = 25.0

ctx.fillRoundedRect(rect(pos, wh), r)

example output

Heart

nim c -r examples/heart.nim

image.fillPath(
  """
    M 20 60
    A 40 40 90 0 1 100 60
    A 40 40 90 0 1 180 60
    Q 180 120 100 180
    Q 20 120 20 60
    z
  """,
  parseHtmlColor("#FC427B").rgba
)

example output

Masking

nim c -r examples/masking.nim

let ctx = newContext(lines)
ctx.strokeStyle = "#F8D1DD"
ctx.lineWidth = 30

ctx.strokeSegment(segment(vec2(25, 25), vec2(175, 175)))
ctx.strokeSegment(segment(vec2(25, 175), vec2(175, 25)))

mask.fillPath(
  """
    M 20 60
    A 40 40 90 0 1 100 60
    A 40 40 90 0 1 180 60
    Q 180 120 100 180
    Q 20 120 20 60
    z
  """,
  color(1, 1, 1, 1)
)
lines.draw(mask, blendMode = MaskBlend)
image.draw(lines)

example output

Gradient

nim c -r examples/gradient.nim

let paint = newPaint(RadialGradientPaint)
paint.gradientHandlePositions = @[
  vec2(100, 100),
  vec2(200, 100),
  vec2(100, 200)
]
paint.gradientStops = @[
  ColorStop(color: color(1, 0, 0, 1), position: 0),
  ColorStop(color: color(1, 0, 0, 0.15625), position: 1.0),
]

image.fillPath(
  """
    M 20 60
    A 40 40 90 0 1 100 60
    A 40 40 90 0 1 180 60
    Q 180 120 100 180
    Q 20 120 20 60
    z
  """,
  paint
)

example output

Image tiled

nim c -r examples/image_tiled.nim

let path = newPath()
path.polygon(
  vec2(100, 100),
  70,
  sides = 8
)

let paint = newPaint(TiledImagePaint)
paint.image = readImage("examples/data/mandrill.png")
paint.imageMat = scale(vec2(0.08, 0.08))

image.fillPath(path, paint)

example output

Shadow

nim c -r examples/shadow.nim

let path = newPath()
path.polygon(vec2(100, 100), 70, sides = 8)

let polygonImage = newImage(200, 200)
polygonImage.fillPath(path, rgba(255, 255, 255, 255))

let shadow = polygonImage.shadow(
  offset = vec2(2, 2),
  spread = 2,
  blur = 10,
  color = rgba(0, 0, 0, 200)
)

image.draw(shadow)
image.draw(polygonImage)

example output

Blur

nim c -r examples/blur.nim

let path = newPath()
path.polygon(vec2(100, 100), 70, sides = 6)

let mask = newImage(200, 200)
mask.fillPath(path, color(1, 1, 1, 1))

blur.blur(20)
blur.draw(mask, blendMode = MaskBlend)

image.draw(trees)
image.draw(blur)

example output

Tiger

nim c -r examples/tiger.nim

let tiger = readImage("examples/data/tiger.svg")

image.draw(
  tiger,
  translate(vec2(100, 100)) *
  scale(vec2(0.2, 0.2)) *
  translate(vec2(-450, -450))
)

example output

pixie's People

Contributors

amjadhd avatar anuken avatar beef331 avatar bung87 avatar chancyk avatar codic12 avatar ehmry avatar eyecon avatar guzba avatar jorisbontje avatar khchen avatar nnsee avatar simonkrauter avatar timotheecour avatar treeform avatar zetashift 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

pixie's Issues

Why shadow is smaller than the browser's ?

if you run this html:

<!DOCTYPE html>
<html>
<body>
<div style="margin: 100px 0px 0px 100px;width: 40px; height: 40px; color: white; box-shadow: 0 2px 8px 1px rgba(0, 0, 0, 0.3)"></div>
</body>
</html>

take a screenshot and measure the span of the shadow you'll find it's 60x60
Now:

proc draw =
  let image = newImage(60, 60)

  let path = newPath()
  path.rect(10, 8, 40, 40)

  let r = newImage(60, 60)
  r.fillPath(path, "white")

  let shadow = r.shadow(
    offset = vec2(0, 2),
    spread = 1,
    blur = 8,
    color = color(0, 0, 0, 0.3)
  )

  image.draw(shadow)
  image.draw(r)
  image.writeFile("pixie_shadow.png")

take the image and do the same you'll find it's 56x56 px.
Why ?

fillShapes(mask: Mask, ... does not check for pathWidth == 0

fillShapes(image: Image, ... checks for pathWidth == 0 and returns early when it is which avoids

"index out of bounds, the container is empty" when attempting to access coverages[0].

fillShapes(mask: Mask, ... should perform the same check.

Unexpected Crash on Windows when using Stb

When trying to read a JPG image, the program crashes unexpectedly, on Windows 11.

This is the example code I tried to run:

import pixie
let image = readImage("test.jpg")

Additional Information

Nim Compiler Version 1.6.4 [Windows: amd64]
Pixie version: 3.1.4
Compiled with nim c -d:pixieUseStb test.nim
When I run the program it suddenly crashes with no debug traceback.
It works fine for reading PNG images.

Error: undeclared identifier: 'newImageFill'

Dear treeform and guzba,

pixie/examples/rounded_rectangle.nim(3, 13) Error: undeclared identifier: 'newImageFill'

newImageFill definition has been removed but is still present in examples/ and should be replaced.

Best regards,

Unable to read valid PNG

The following image cannot be read:

invalid

$ file invalid.png 
invalid.png: PNG image data, 96 x 96, 8-bit/color RGBA, non-interlaced
import pixie
let img = readImage("invalid.png")

->

test.nim(2) test
pixie-1.1.3/pixie.nim(38) readImage
pixie-1.1.3/pixie.nim(23) decodeImage
pixie-1.1.3/pixie/fileformats/png.nim(430) decodePng
pixie-1.1.3/pixie/fileformats/png.nim(349) decodePng
Error: unhandled exception: Invalid PNG buffer, unable to load [PixieError]

Pixie version: 1.1.3
Nim Compiler Version 1.4.6 [Linux: amd64]

proc addArc runs indefinitely (at least 12 minutes) for large centre and radius values

This short program never completes. To be precise it runs for at least 12 minutes without completing on my HP Omen 17 inch laptop. It uses very little memory but consumes more than 99% CPU all the time. I extracted the path from a larger program that fails with out of memory in, as far as I can tell, the same area of code.

I instrumented paths.nim and got as far as seeing that the proc that doesn't return is the inner proc addArc inside proc commandsToShapes.

I presume that the path would produce little or nothing inside the boundaries of the image but I'm not sure how to determine that. Is there a short expression or algorithm that can determine whether or not a given path command will produce any visible result? I'm fairly certain that I have used Cairo with similar out of range values and not had to wait long for it to draw. I wasn't using Nim for that though, it was written in Gambas.
I can off course limit the values that my program uses but I would like to have at least a rule of thumb for what sort of limits to apply.

If the arguments to the A command are replaced with the absolute values then the program completes very rapidly.

import pixie, chroma

let
  image = newImage(200, 200)
image.fill(rgba(255, 255, 255, 255))

let  
  pathStr ="""
L -16370.0 -18156.0
A 4100 4100 0 1 0 -19670 -14134
Z
"""

let
  path = parsePath(pathStr)

var
  paint = newPaint(SolidPaint)
paint.color = color(255, 255, 255, 255)

strokePath(image,
           path,
           paint,
           mat3(),
           1,
           ButtCap,
           MiterJoin,
           defaultMiterLimit,
           @[])

Support for Inkscape SVG files

I'm creating an application that requires to load images in SVG format. Most of those images are created by Inkscape and it fails loading them.
Looking at the source code, I think the problem is that Inkscape's SVG files do not have the viewBox attribute.

import std/httpclient
import pixie/fileformats/svg

let
  client = newHttpClient()
  data = client.getContent("https://appimage.github.io/database/SonicLineup/icons/scalable/sonic-lineup-icon.svg")
  image = data.decodeSvg()
/home/cristobal/.../trial.nim(7) trial
/home/cristobal/.nimble/pkgs/pixie-4.1.0/pixie/fileformats/svg.nim(603) decodeSvg
Error: unhandled exception: Unable to load SVG [PixieError]
Error: execution of an external program failed: '/home/cristobal/.../trial '

Error: undeclared identifier: 'newContext'

I'm brand new to Nim, so I'm doubtless doing something simple incorrectly. When I try to run the examples like so

nim c -r realtime_sdl.nim

I get

Error: undeclared identifier: 'newContext'

Nim version:

Nim Compiler Version 1.4.4 [MacOSX: amd64]

ctx.StrokePolygon consumes all memory until killed by OS

I've found a bug related to #388 (I think). This time lineDash is not zero.

import pixie

let image = newImage(200, 200)
image.fill(rgba(255, 255, 255, 255))

let ctx = newContext(image)

ctx.setLineDash(@[2.0.float32])
ctx.strokePolygon(vec2(0.0, 0.0), 0.0, 0)

This has exactly the same behaviour as in #388: consumes memory until killed by the OS. I've looked at the code for context.StrokePolygon and paths.strokePolygon and I can't see why it happens. The code looks right to me; with sides == 0 nothing should be added to the path.

Shadow with negative spread doesn't match figma

A continuation for #372 (comment)

Figma:

  1. create a 47x47px frame
  2. add a 29x29px rectangle at x=y=9
  3. set the corner radii to 6px
  4. set the fill to white and the opacity to 0.001% (it should become 0% when confirmed).

image
6. export the frame

Pixie:

proc drawShadow() =
  let image = newImage(47, 47)

  let r = 6f32
  let path = newPath()
  path.roundedRect(rect(9, 9, 29, 29), r, r, r, r)

  image.fillPath(path, "white")

  image.shadow(
    offset = vec2(0, 0),
    spread = -2,
    blur = 0,
    color = color(0, 0, 0, 0.4)
  ).writeFile("shadow_pixie_negative_spread_no_blur.png")

Figma:
shadow_figma_negative_spread_no_blur

  • 25x25px rect
  • corner radius is 4px (6 - 2):

Pixie:
shadow_pixie_negative_spread_no_blur

  • 25x25px rect
  • corner radius is 6px (unchanged):

Export colortypes

Would it be possible to include exporting colortypes from pixie.nim? I am trying to modify R,G,B,A directly in the Image. Is there a built-in that I missed to do this?

Passing `image[x, y]` to var parameter is compile error

Compiling following code is compile error:

import pixie

var image = newImage(10, 10)
image[0, 0] = rgba(0, 255, 255, 255)
image[0, 1] = rgba(255, 255, 0, 255)
swap(image[0, 0], image[0, 1])
doAssert image[0, 0] == rgba(255, 255, 0, 255)
doAssert image[0, 1] == rgba(0, 255, 255, 255)

Error message:

/tmp/tmp/testpixie/test.nim(11, 5) Error: type mismatch: got <ColorRGBX, ColorRGBX>
but expected one of:
proc swap[T](a, b: var T)
  first type mismatch at position: 1
  required type for a: var T
  but expression 'image[0, 0]' is immutable, not 'var'

expression: swap(image[0, 0], image[0, 1])

Adding proc `[]`(image: var Image, x, y: int): var ColorRGBX allows passing image[x, y] to var parameters.
Following code compiles and runs:

import pixie

proc `[]`(image: var Image, x, y: int): var ColorRGBX {.inline, raises: [].} =
  assert image.inside(x, y)
  image.unsafe[x, y]

var image = newImage(10, 10)
image[0, 0] = rgba(0, 255, 255, 255)
image[0, 1] = rgba(255, 255, 0, 255)
swap(image[0, 0], image[0, 1])
doAssert image[0, 0] == rgba(255, 255, 0, 255)
doAssert image[0, 1] == rgba(0, 255, 255, 255)

Another workaround is copy image[x, y] to temporal variable, pass it to var parameter and copy back to image[x, y].

Can adding proc `[]`(image: var Image, x, y: int): var ColorRGBX to pixie/images.nim cause any problems?

Example with GTK 4

I see examples for sdl, glfw, glut. Is there an option to use this with GTK?

Sample usage for pixie/demo

Nice to have would be example code for using pixie/demo. I was able to launch the app using this code:

import pixie/demo

demo.start()

but am not sure whether it's a bug or incorrect usage as, on macOS Catalina, the app launches, its name is visible in the menubar, then, the screen goes black for a second before returning to normal, and the app exits without error messages.

fillPath fails with unhandled RangeDefect exception

I got fed up with my prototype genetic drawing program throwing exceptions because it gave bad paths to pixie. So I wrote a little program to try to find out what paths don't work. A bit like fuzzing except that I don't know how to do that properly so I opted to simply try every combination of a few numbers. It worked and found some unhandled exceptions but so far no crashes.

Here are a few of the paths that cause unhandled exceptions:

Unhandled error in pixie:
value out of range: -9223372036854775808 notin 0 .. 9223372036854775807
i: 1346a85
pathStr: M -3.402823561222554e+19 -3.402823561222554e+19 L -3.402823561222554e+19 -3.402823561222554e+19 C -3402823424.0 -3402823424.0 -3.402823561222554e+19 -3.402823561222554e+19 -3402.823486328125 -3402.823486328125 A -3402.823486328125 -3402.823486328125 -3402.823486328125 0 0 -3.402823561222554e+19 -3.402823561222554e+19
Unhandled error in pixie:
value out of range: -9223372036854775808 notin 0 .. 9223372036854775807
i: 1346a92
pathStr: M -3.402823561222554e+19 -3.402823561222554e+19 L -3.402823561222554e+19 -3.402823561222554e+19 C -3402823424.0 -3402823424.0 -3.402823561222554e+19 -3.402823561222554e+19 -3402.823486328125 -3402.823486328125 A -3402.823486328125 -3402.823486328125 -3402.823486328125 0 0 -3402.823486328125 -3402.823486328125
Unhandled error in pixie:
value out of range: -9223372036854775808 notin 0 .. 9223372036854775807
i: 1346a93
pathStr: M -3.402823561222554e+19 -3.402823561222554e+19 L -3.402823561222554e+19 -3.402823561222554e+19 C -3402823424.0 -3402823424.0 -3.402823561222554e+19 -3.402823561222554e+19 -3402.823486328125 -3402.823486328125 A -3402.823486328125 -3402.823486328125 -3402.823486328125 0 1 -3.402823561222554e+19 -3.402823561222554e+19
Unhandled error in pixie:
value out of range: -9223372036854775808 notin 0 .. 9223372036854775807
i: 1346aa0
pathStr: M -3.402823561222554e+19 -3.402823561222554e+19 L -3.402823561222554e+19 -3.402823561222554e+19 C -3402823424.0 -3402823424.0 -3.402823561222554e+19 -3.402823561222554e+19 -3402.823486328125 -3402.823486328125 A -3402.823486328125 -3402.823486328125 -3402.823486328125 0 1 -3402.823486328125 -3402.823486328125
Unhandled error in pixie:
value out of range: -9223372036854775808 notin 0 .. 9223372036854775807
i: 1346aa1
pathStr: M -3.402823561222554e+19 -3.402823561222554e+19 L -3.402823561222554e+19 -3.402823561222554e+19 C -3402823424.0 -3402823424.0 -3.402823561222554e+19 -3.402823561222554e+19 -3402.823486328125 -3402.823486328125 A -3402.823486328125 -3402.823486328125 -3402.823486328125 0 0 -3.402823561222554e+19 -3.402823561222554e+19

The program echoes every path that cause an exception that pixie doesn't handle.

Edit: I discovered an embarrassing bug in my code, here is an improved version:

# An attempt at, sort of, fuzzing pixie.

import pixie, chroma

import strformat
import strutils
import bigints
#import std/options


let
  image = newImage(200, 200)
image.fill(rgba(255, 255, 255, 255))

const
  argCount = 18
  maxFloat = 3.4028235e38.float32
  halfMaxFloat = 3.4028235e19.float32
  quarterMaxFloat = 3.4028235e9.float32
  tenthMaxFloat = 3.4028235e3.float32
  thousand = 1000.float32
  ten = 10.float32
  a = [-halfMaxFloat, -quarterMaxFloat, -tenthMaxFloat, -thousand, -ten, 
       0,
       ten, thousand, tenthMaxFloat, quarterMaxFloat, halfMaxFloat]
  base = a.high - a.low + 1
  ai: array[base, int] =  [0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
  m = "1000000000000000000".initBigInt(base)
  #     012345678901234567890

var
  paint = newPaint(SolidPaint)

paint.color = color(255, 255, 255, 255)
  
var
  i = 0.initBigInt()

#i = "10244424".initBigInt(base)

#i = "222220000".initBigInt(base)

while i < m:
  var
    n: array[argCount, int]
    s = i.toString(3).align(argCount, '0')
    
  for j in 0 ..< argCount:
    let
      c = s[j]
    if c in '0'..'9':
       n[j] = int(c) - int('0')
    else:
       n[j] = int(c) - int('a') + 10

      #debugEcho &"n: {n}"
  
  let
    pathStr = &"""M {a[n[0]]} {a[n[1]]} L {a[n[2]]} {a[n[3]]} C {a[n[4]]} {a[n[5]]} {a[n[7]]} {a[n[8]]} {a[n[9]]} {a[n[10]]} A {a[n[11]]} {a[n[12]]} {a[n[13]]} {ai[n[14]]} {ai[n[15]]} {a[n[16]]} {a[n[17]]}"""

  let
     path = parsePath(pathStr)
  try:
    fillPath(image,
             path,
             paint,
             mat3(),
             NonZero)
  except PixieError:
    #echo "Handled error in pixie"
    #echo getCurrentExceptionMsg()
    discard
  except:
    echo "Unhandled error in pixie:"
    echo getCurrentExceptionMsg()
    echo &"i: {i.toString(base)}"
    echo &"pathStr: {pathStr}"
    
  i += 1

echo "Finished"

computeBounds() breaking change in 4.1.0

I’ve been using computeBounds(font: Font, text: string) extensively in previous versions to send a text string along with the Font information to determine in advance what size newImage () should be. That proc has changed in 4.1.0. to only accommodate computeBounds( arrangement: Arrangement, transform = mat3()). I have neither an arrangement nor a transform, and I can’t see a solution for just a font and text string. I’m fairly new to Pixie, so apologies in advance if I’m missing something obvious.

Draw image performance

Hello,
i am new to nim and pixie. I started with the realtime glut example and tried to draw a texture multiple times. But when i just draw a texture 10 times then there remain just a few frames per second. Is this normal behavior or am i doing something wrong ?

## This example show how to have real time pixie using glut API.

import math, opengl, opengl/glu, opengl/glut, pixie

let
  w: int32 = 1024
  h: int32 = 1024

var
  screen = newImage(w, h)
  texture = readImage("texture.png").subImage(64, 128, 64, 64)
  font = readFont("Roboto-Regular.ttf")
  paint = newPaint(PaintKind.pkImage)
  ctx = newContext(screen)
  frameCount = 0

font.paint.color = rgba(255, 0, 0, 255).color
paint.image = texture

proc display() {.cdecl.} =
  ## Called every frame by GLUT
  
  ctx.clearRect(0, 0, w.toFloat, h.toFloat)
  ctx.fillStyle = rgba(255, 0, 0, 255)
  screen.fillText(font.typeset($(frameCount), vec2(100, 50)), translate(vec2(0, 0)))
  for i in 0 ..< 10:
    ctx.fillStyle = paint
    ctx.beginPath()
    ctx.rect(i.toFloat * 64, 50, 50, 50)
    ctx.fill()
    
  # update texture with new pixels from surface
  var dataPtr = ctx.image.data[0].addr
  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, GLsizei w, GLsizei h, GL_RGBA,
      GL_UNSIGNED_BYTE, dataPtr)

  # draw a quad over the whole screen
  glClear(GL_COLOR_BUFFER_BIT)
  glBegin(GL_QUADS)
  glTexCoord2d(0.0, 0.0); glVertex2d(-1.0, +1.0)
  glTexCoord2d(1.0, 0.0); glVertex2d(+1.0, +1.0)
  glTexCoord2d(1.0, 1.0); glVertex2d(+1.0, -1.0)
  glTexCoord2d(0.0, 1.0); glVertex2d(-1.0, -1.0)
  glEnd()
  glutSwapBuffers()

  inc frameCount

  glutPostRedisplay() # ask glut to draw next frame

glutInit()
glutInitDisplayMode(GLUT_DOUBLE)
glutInitWindowSize(w, h)
discard glutCreateWindow("GLUT/Pixie")

glutDisplayFunc(display)
loadExtensions()

# allocate a texture and bind it
var dataPtr = ctx.image.data[0].addr
glTexImage2D(GL_TEXTURE_2D, 0, 3, GLsizei w, GLsizei h, 0, GL_RGBA,
    GL_UNSIGNED_BYTE, dataPtr)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
glEnable(GL_TEXTURE_2D)

glutMainLoop()

I also tried to use ctx.drawImage(texture, i.toFloat * 64, 50, 50, 50) to draw the image but the performance result was the same.

Thank you

Does pixie provide image blitting?

flippy development has moved to pixie. I needed that library's blit functionality.

# blit a rectangular part from one place to another
blit(image, image, rect(0, 0, 100, 100), rect(100, 100, 100, 100))

Does pixie have an equivalent?

Thx!

XLib example

A real-time example using Xlib would be helpful. I'm using Pixie (awesome library!) for my window manager, and struggled with painting Pixie's framebuffer to one on a X window. I'll PR an example soon.

Realtime Examples Fail Compilation

I'm trying to get the realtime examples to work on macOS 10.15.7

Both realtime_sdl.nim and realtime_glfw.nim fail compilation with the following output:

Error: type mismatch: got <ColorRGBX> but expected 'Color = object'

I have installed glfw and sdl:

  • brew install glfw
  • brew install sdl2{,_gfx,_image,_mixer,_net,_ttf}

I have installed pixie and nim bindings:

  • nimble install pixie@#head
  • nimble install staticglfw@#head
  • nimble install sdl2@#head

I have also tried with the non-head versions of pixie and bindings.

  • staticglfw and sdl2 fails the same way as the head versions
  • Non-head pixie fails because it misses newPaint proc

This works:

  • Building the minimal staticglfw example without pixie works like intended.
  • Building other examples like blur.nim, text.nim and gradient.nim works.

What am I missing?

Invalid PNG buffer due to CgBI critical chunk

I'm mostly just documenting a problem I ran into with Xcode when building for iOS. Happy to provide a pull request that generates an error message specific to this issue if you like.

Xcode by default will compress PNGs, I think using a proprietary pngcrush compression variant. The first critical chunk encountered is "CgBI" instead of "IHDR" and the byte length is 4 instead of 13, so the line that raises the error is:

https://github.com/treeform/pixie/blob/master/src/pixie/fileformats/png.nim#L367

The solution is to disable both compression and text metadata removal in Xcode, seen in the image below:

Screen Shot 2022-03-07 at 1 54 17 PM

Here's a reference to the compression I came across:

https://iphonedev.wiki/index.php/CgBI_file_format

And an old issue where I discovered the solution:

https://developer.apple.com/forums/thread/43372

Repository too large

So, Nim need to clone the git repository to install pixie. That includes:

20.9 MiB tests/fileformats
16.6 MiB tests/fonts
2.4 MiB docs/banner.png
1.3 MiB examples/data/*

Which I don't need if I don't want to run tests or use some examples. It's slow to download all these.

fillPath consumes all memory when the path has an arc with out of range arguments

This program runs until killed with out of memory. Of course the arguments to the arc command are far out of range but I would expect a catchable exception instead of an out of memory crash. I suppose it is a similar problem to #403 and #392.

import pixie, chroma
let
  image = newImage(200, 200)
image.fill(rgba(255, 255, 255, 255))

let
  pathStr = """
L 3473901.0 1136732.75 
A 31888.0 31888.0 0 0 1 3493390.25 1076022.375 
L 32563.0 -2081.0"""

var
  paint = newPaint(SolidPaint)

paint.color = color(255, 255, 255, 255)

let
  path = parsePath(pathStr)
  
fillPath(image,
         path,
         paint,
         mat3(),
         NonZero)

Result:

$ ulimit -Sv unlimited; /usr/bin/time -v examples//test_mem6
Command terminated by signal 9
	Command being timed: "examples//test_mem6"
	User time (seconds): 154.55
	System time (seconds): 3.02
	Percent of CPU this job got: 97%
	Elapsed (wall clock) time (h:mm:ss or m:ss): 2:41.19
	Average shared text size (kbytes): 0
	Average unshared data size (kbytes): 0
	Average stack size (kbytes): 0
	Average total size (kbytes): 0
	Maximum resident set size (kbytes): 6226992
	Average resident set size (kbytes): 0
	Major (requiring I/O) page faults: 19
	Minor (reclaiming a frame) page faults: 2272969
	Voluntary context switches: 2120
	Involuntary context switches: 2021
	Swaps: 0
	File system inputs: 4192
	File system outputs: 0
	Socket messages sent: 0
	Socket messages received: 0
	Signals delivered: 0
	Page size (bytes): 4096
	Exit status: 0

Nearest-neighbor scaling

Is there a way to scale images with pixie using a nearest neighbor algorithm?

I believe CanvasRenderingContext2D's imageSmoothingEnabled property "enables" this behavior

Wiki link regarding nearest neighbor

If it's simply unimplemented, I could take a stab at it.


I get the desired effect using magnifyBy2, but this seems to create a whole new image.

My ultimate goal is to be able to scale the context or image (in similar fashion to CanvasTransform.scale in js). There is proc resize*(srcImage: Image, width, height: int): Image but this causes blurring.

Guidance on usage as a C library

I can run the tests successfully with nimble test but I cannot figure out how to build the library with C bindings.

Genny and pixie have both been installed with nimble install. I'm running nimble bindings pixie.nimble from a freshly clone of master, but get the following output:

$ nimble bindings pixie.nimble
  Executing task bindings in ~/clones/pixie/pixie.nimble
Hint: used config file '/etc/nim/nim.cfg' [Conf]
Hint: used config file '/etc/nim/config.nims' [Conf]
.......................................
~/.nimble/pkgs/pixie-2.1.1/pixie/common.nim(21, 14) Warning: use mix instead; lerp is deprecated [Deprecated]
~/.nimble/pkgs/pixie-2.1.1/pixie/common.nim(22, 14) Warning: use mix instead; lerp is deprecated [Deprecated]
~/.nimble/pkgs/pixie-2.1.1/pixie/common.nim(23, 14) Warning: use mix instead; lerp is deprecated [Deprecated]
~/.nimble/pkgs/pixie-2.1.1/pixie/common.nim(24, 14) Warning: use mix instead; lerp is deprecated [Deprecated]
.
~/.nimble/pkgs/pixie-2.1.1/pixie/blends.nim(109, 6) Hint: 'softLight' is declared but not used [XDeclaredButNotUsed]
.....................
~/.nimble/pkgs/pixie-2.1.1/pixie/fileformats/gif.nim(65, 9) Hint: 'colorTableSorted' is declared but not used [XDeclaredButNotUsed]
~/.nimble/pkgs/pixie-2.1.1/pixie/fileformats/gif.nim(66, 9) Hint: 'colorTableSize' is declared but not used [XDeclaredButNotUsed]
~/.nimble/pkgs/pixie-2.1.1/pixie/fileformats/gif.nim(165, 11) Hint: 'extentionType' is declared but not used [XDeclaredButNotUsed]
~/.nimble/pkgs/pixie-2.1.1/pixie/fileformats/gif.nim(31, 5) Hint: 'pixelAspectRatio' is declared but not used [XDeclaredButNotUsed]
~/.nimble/pkgs/pixie-2.1.1/pixie/fileformats/gif.nim(30, 5) Hint: 'bgColorIndex' is declared but not used [XDeclaredButNotUsed]
~/.nimble/pkgs/pixie-2.1.1/pixie/fileformats/gif.nim(28, 5) Hint: 'colorTableSorted' is declared but not used [XDeclaredButNotUsed]
~/.nimble/pkgs/pixie-2.1.1/pixie/fileformats/gif.nim(27, 5) Hint: 'originalDepth' is declared but not used [XDeclaredButNotUsed]
.........
~/clones/pixie/bindings/bindings.nim(46, 13) template/generic instantiation from here
~/clones/pixie/bindings/bindings.nim(47, 6) Error: can raise an unlisted exception: Exception
stack trace: (most recent call last)
/tmp/nimblecache-2590653678/nimscriptapi_3049515398.nim(187, 16)
~/clones/pixie/pixie.nimble(33, 13) bindingsTask
~/clones/pixie/pixie.nimble(22, 10) compile
/usr/lib/nim/system/nimscript.nim(260, 7) exec
/usr/lib/nim/system/nimscript.nim(260, 7) Error: unhandled exception: FAILED: nim c -f  -d:release --app:lib --gc:arc --tlsEmulation:off --out:libpixie.so --outdir:bindings/generated bindings/bindings.nim [OSError]
     Error: Exception raised during nimble script execution

I'm using Nim 1.4.8 [Linux: amd64], and nimble v0.13.1 on arch linux. I have not used nim before, meaning I cannot progress beyond here. Any guidance on how to build this statically would also be appreciated, though less important.

Errors when using paths

On Nim 1.4.8, pixie#head

nim r examples/heart.nim

pixie/src/pixie/paths.nim(1035, 10) Error: attempting to call undeclared routine: 'isNaN'

Maybe we should have the ci run the examples?

Issues with 32bit Architecture (Nintendo 3DS Family)

Hello,

I've been working on getting Pixie to run on the Nintendo 3DS for the last couple of days. After a few changes (and some advice from treeform on Discord) I was able to get it running:

# (Pixie) paths.nim line 1210
    while x < image.width:
      if x + 4 <= coverages.len:
        let peeked = cast[ptr uint32](coverages[x].addr)[]
        if peeked == 0:
          x += 4
          continue
# (zippy) common.nim line 209
func read64*(s: seq[uint8], pos: int): uint64 {.inline.} =
  # when nimvm:
  (s[pos + 0].uint64 shl 0) or
  (s[pos + 1].uint64 shl 8) or
  (s[pos + 2].uint64 shl 16) or
  (s[pos + 3].uint64 shl 24) or
  (s[pos + 4].uint64 shl 32) or
  (s[pos + 5].uint64 shl 40) or
  (s[pos + 6].uint64 shl 48) or
  (s[pos + 7].uint64 shl 56)
  # else:
    # cast[ptr uint64](s[pos].unsafeAddr)[]

func copy64*(dst: var seq[uint8], src: seq[uint8], op, ip: int) {.inline.} =
  # when nimvm:
  for i in 0 .. 7:
    dst[op + i] = src[ip + i]
  # else:
  #   cast[ptr uint64](dst[op].addr)[] = read64(src, ip)

The changes were to avoid getting an "Unaligned" error from the 3DS itself. The 3DS is a 32 bit system, and I was able to fix the errors by avoiding casting to a ptr uint64. For Pixie I changed the loop by amount to be a uint32, and for zippy I simply used the existing nimvm implementation.

Ideally, Pixie would have some way to figure this out at compile time. It could be that when I compiled, I was missing some sort of Nim flag. Here is my nim.cfg for reference:

arm.linux.gcc.path = "/opt/devkitpro/devkitARM/bin"
arm.linux.gcc.exe = "arm-none-eabi-gcc"
arm.linux.gcc.linkerexe = "arm-none-eabi-gcc"
noMain
noLinking
header = "output.h"
cpu = "arm"
os = "linux"
--debugger:native
-d:useMalloc
gc = "arc"
--passC="-specs=3dsx.specs -g -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft -Wl,-Map,3dsfun.map   -L/opt/devkitpro/libctru/lib -lctru -lm"
nimcache="../build"

Note that pretty much all of the passC flags are from devKitPro, the maintainers of the C toolchain for 3DS.

SVG text rendering

decodeSVG does not render text elements. I think Pixie is good at rendering text so I'm willing to implement this, unless something is blocking it that I don't know about.

Issue with image.fillPath with some shapes

I noticed I was able to call fillPath on rectangles that are taller than longer, but not rectangles that are longer than taller. It may just be a misconception on my part.

Here's a runnable example:

import pixie

const
  pointsA = [
    vec2(0, 0),
    vec2(0, 1120),
    vec2(32, 1120),
    vec2(32, 0)
  ]
  pointsB = [
    vec2(0, 0),
    vec2(1120, 0),
    vec2(1120, 32),
    vec2(0, 32)
  ]

proc renderPoints(points: openArray[Vec2]): Image =
  let
    image = newImage(1920, 1080)
    ctx = newContext(image)

  ctx.fillStyle = rgba(255, 0, 0, 255)
  var path: Path
  path.moveTo(points[0])
  for v in points:
    path.lineTo(v)

  ctx.image.fillPath(path, ctx.fillStyle)
  return image

when isMainModule:
  # Works
  let imageA = renderPoints(pointsA)
  imageA.writeFile("shapeA.png")

  # Does not work
  let imageB = renderPoints(pointsB)
  imageB.writeFile("shapeB.png")

You'll notice shapeA.png will have a vertical red rectangle, but shapeB.png appears to be empty.

Realtime GLFW example doesn't work with NimGL's OpenGL and GLFW bindings

I'm relatively new to Nim and OpenGL. I copied your real-time GLFW example, modified some identifiers because I don't use staticglfw but NimGL's OpenGL and GLFW bindings. It threw at this line:

var dataPtr = ctx.image.data[0].addr
glTexImage2D(GL_TEXTURE_2D, 0, 3, GLsizei w, GLsizei h, 0, GL_RGBA,
    GL_UNSIGNED_BYTE, dataPtr)

with traceback: SIGSEGV: Illegal storage access. (Attempt to read from nil?)
To the best of my knowledge dataPtr is not nil.
Is this an issue on either side or am I just being too ignorant of this?

Feature request: Netpbm formats

The FOSS Netpbm project has a few image formats, .pbm, .pgm, .ppm, and .pnm. The formats are supported on most Linux distros alongside macOS and Windows. More reading. Netpbm formats are commonly used where encoding/decoding speed is crucial but image size is not important.

This is a feature request to support these formats, or at least the most widely used .ppm. The format itself is incredibly simple and thus should be simple to implement. For example, take the following dump:

P3           # "P3" means this is a RGB color image in ASCII
3 2          # "3 2" is the width and height of the image in pixels
255          # "255" is the maximum value for each color
# The part above is the header
# The part below is the image data: RGB triplets
255   0   0  # red
  0 255   0  # green
  0   0 255  # blue
255 255   0  # yellow
255 255 255  # white
  0   0   0  # black

The header (file magic, resolution, and maximum value) are ASCII, the actual image data can be bytes (0x00 .. 0xFF) depending on the file magic (type P3 represents ASCII, P6 represents raw bytes). This dump is a textual representation of the following six-pixel image (magnified for demo purposes):
image

The official web page for the PPM specification can be found here.

Create the same image with different dpis

I want to export 3 versions of the following image: @1x, @2x and @3x the dpi, How do I do that ?

import pixie

let image = newImage(62, 62)

let path = newPath()
path.roundedRect(rect(11, 5, 40, 40), 6, 6, 6, 6)

image.fillPath(path, rgba(255, 255, 255, 255))

image.writeFile("overlay_bg.png")

Figma export scale:
image

Core dump while executing ctx.strokeCircle

Found another bug or at least an exception that cannot be caught. The call to ctx.strokeCircle is inside a try block.
I'm not sure if this is a bug in pixie or in nim or both.

I'll try to create a small stand alone program and post it as soon as I can.

I can also try to debug it myself; can you give me a word or two of advice on how to go about that? If I clone the pixie github repo, how do I make my code import from that repo instead of the installed version?

Traceback (most recent call last)
/home/kj/dev/nim/pixie-genes/genetic_picture1.nim(63) genetic_picture1
/home/kj/dev/nim/pixie-genes/genetic_picture1.nim(40) ratchet
/home/kj/dev/nim/pixie-genes/interpreter.nim(563) interpret
/home/kj/dev/nim/pixie-genes/interpreter.nim(382) interpret
/home/kj/.nimble/pkgs/pixie-4.0.1/pixie/contexts.nim(757) strokeCircle
/home/kj/.nimble/pkgs/pixie-4.0.1/pixie/contexts.nim(392) stroke
/home/kj/.nimble/pkgs/pixie-4.0.1/pixie/contexts.nim(158) stroke
/home/kj/.nimble/pkgs/pixie-4.0.1/pixie/paths.nim(1996) strokePath
/home/kj/.nimble/pkgs/pixie-4.0.1/pixie/paths.nim(1610) fillShapes
/home/kj/.nimble/pkgs/pixie-4.0.1/pixie/paths.nim(1126) partitionSegments
/home/kj/.choosenim/toolchains/nim-1.6.2/lib/system/gc.nim(494) newSeq
/home/kj/.choosenim/toolchains/nim-1.6.2/lib/system/gc.nim(486) newObj
/home/kj/.choosenim/toolchains/nim-1.6.2/lib/system/gc_common.nim(423) prepareDealloc
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Segmentation fault (core dumped)
Error: execution of an external program failed: '/home/kj/dev/nim/pixie-genes/genetic_picture1 '
kj@willikins:~/dev/nim/pixie-genes$

Support negative shadow spread value

I want to create a shadow generated by this CSS:

{
  box-shadow: 0 6px 12px -2px rgba(0, 0, 0, 0.4);
}

I get this error:

masks.nim(217)           spread
Error: unhandled exception: Cannot apply negative spread [PixieError]

Pixie SVG does not support clipPath

I was trying to draw one of my SVGs, but got the following error:

Error: unhandled exception: Unsupported SVG tag: clipPath [PixieError]

Simple test image that generates the error:

<?xml version="1.0" encoding="UTF-8"?>
<svg clip-rule="evenodd" fill-rule="evenodd" version="1.1" viewBox="0 0 140 140" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
<rect x="20" y="20" width="100" height="100"/>
<clipPath id="a">
 <rect x="20" y="20" width="100" height="100"/>
</clipPath>
<g clip-path="url(#a)">
 <rect x="40" y="40" width="100" height="100" fill="#f00"/>
</g></svg>

I don't know if the current plan is to use Cairo for SVGs, but I thought I would create an issue anyways.

How hard would it be implementing clipPath given that we already have masks in the drawing library?

Crash when calling image.fill() with SIMD

Calling fill() on an image causes a crash in certain situations. I encountered this issue when loading/rendering in typography.

Stack trace looks something like this:

typography-0.7.2/typography/rasterizer.nim(297) getGlyphImage
typography-0.7.2/typography/rasterizer.nim(292) getGlyphImage
typography-0.7.2/typography/rasterizer.nim(109) getGlyphImage
pixie/src/pixie/images.nim(85) fill

SIGSEGV: Illegal storage access. (Attempt to read from nil?)

This line causes the segfault.
According to StackOverflow, when using _mm_store_si128, "The destination address needs to be 16 byte aligned."

I could very well be wrong, but there doesn't seem to be anything guaranteeing that image data is 16-byte aligned. Switching to _mm_storeu_si128 fixes the problem.

Unicode font support

Hi, is it possible to render unicode strings containing e.g. emojis somehow? I tried loading a typeface containing emojis but they are not rendered at all, I also tried using that typeface as a fallback for another one but that also didn't work. Any help would be appreciated :^)

Program crashes when calling ctx.stroke() even when wrapped in try

I'm using pixie.Context to experiment with some semi-random drawing.
I build strokeStyle and path and then execute ctx.stroke. The program crashes even if I wrap the ctx.stroke call in a try.
I daresay that the strokeStyle is the culprit but it should surely not kill the program.

the snippet of code looks like this:

        echo "GStroke"
        echo "StrokeStyle: ", ctx.strokeStyle.repr
        try:
          ctx.stroke()
        except:
          echo getCurrentExceptionMsg()
          raise

And this is the result:

GStroke
StrokeStyle: ref 0x7fb0d3461590 --> [kind = AngularGradientPaint,
blendMode = NormalBlend,
opacity = 0.2196078449487686,
color = [r = 0.2156862765550613,
g = 0.0,
b = 0.007843137718737125,
a = 0.2196078449487686],
image = ref 0x7fb0d345a050 --> [width = 1,
height = 1,
data = 0x7fb0d345a080@[[r = 0,
g = 0,
b = 0,
a = 0]]],
imageMat = [arr = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]],
gradientHandlePositions = 0x7fb0d345e5d0@[[arr = [0.0, 0.0]], [arr = [0.0, 0.0]], [arr = [0.0, 0.0]]],
gradientStops = 0x7fb0d345e450@[[color = [r = 0.0,
g = 0.0,
b = 0.0,
a = 0.0],
position = 0.0]]]
Killed
Error: execution of an external program failed: ...

I can try to create a stand alone example if necessary.

Edit: I've hardcoded the paint kind to SolidPaint so presumably it is not the strokeStyle that is the problem after all, perhaps it is the path.

paths.nim polygon draws an unecessary lineTo

In proc polygon the code executes path.moveTo(x + size * cos(0.0), y + size * sin(0.0)) to move to the start of the polygon.

The for side in loop enumerates side from 0 to sides. Surely this should be 1 to sides because when side == 0 it will draw a line to the starting point.

Or have I missed something that should be obvious?

proc polygon*(
  path: Path, x, y, size: float32, sides: int
) {.raises: [PixieError].} =
  ## Adds an n-sided regular polygon at (x, y) with the parameter size.
  if sides <= 0:
    raise newException(PixieError, "Invalid polygon sides value")
  path.moveTo(x + size * cos(0.0), y + size * sin(0.0))
  for side in 0 .. sides: <-- Should be: for ide in 1 .. sides:
    path.lineTo(
      x + size * cos(side.float32 * 2.0 * PI / sides.float32),
      y + size * sin(side.float32 * 2.0 * PI / sides.float32)
    )

Memory leak in SDL realtime example

This likes just a mistake on my part, but I'm reaching my wit's end on this one. The realtime_sdl example has a small memory like because it's not cleaning up the texture. The destroy below resolves it, however if I do the same thing in my very, very similar code. It doesn't fix the issue.

var dataPtr = ctx.image.data[0].addr
mainSurface.pixels = dataPtr
mainTexture = render.createTextureFromSurface(mainSurface)
render.copy(mainTexture, nil, nil)
destroy(mainTexture)  # fixes leak
render.present()

My code:

let color = uint8((x / uiState.windowWidth) * 256)
    let screen = newImage(512, 512)
    let screenCtx = newContext(screen)
    screenCtx.font = uiCtx.consoleFont
    screen.fill(rgba(255, 255, 255, 255))
    screen.fillText(
        uiCtx.consoleFont.style(36, rgba(color, color, color, 255)),
        $frameRate
    )
    var surface = createRGBSurface(0, cint 512, cint 512, 32, rmask, gmask, bmask, amask)
    var dataPtr = screenCtx.image.data[0].addr
    surface.pixels = dataPtr
    # dealloc(surface.pixels)
    # destroy(surface)
    var mainTexture = uiCtx.render.createTextureFromSurface(surface)
    uiCtx.render.copy(mainTexture, nil, nil)
    destroy(mainTexture)

It seems to be related to passing the image.data pointer to the Surface. Am I missing something about the propery way to deallocate the image/texture/surface?

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.