Skip to content

Latest commit

 

History

History
380 lines (281 loc) · 8.75 KB

tutorial.md

File metadata and controls

380 lines (281 loc) · 8.75 KB

Pixie tutorial

So you came to the right place, here I will help you out with how with pixie basics and how to use it in your projects.

Are you looking for the API reference? https://treeform.github.io/pixie/pixie.html

Installing pixie.

Nim

nimble install pixie

Then just import it:

import pixie

Python

pip install pixie-python

Then just import it:

import pixie

or

from pixie import *

Drawing paths

You can either load paths from a standard SVG style string or build it up with methods.

var heart1 = newImage(200, 200)
heart1.fill(rgba(255, 255, 255, 255))

heart1.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
  """,
  "#FC427B"
)

heart1.writeFile("images/heart1.png")

Will produce:

example output

But you can also build the path with path commands:

var heart2 = newImage(200, 200)
heart2.fill(rgba(255, 255, 255, 255))

var heartPath = newPath()
heartPath.moveTo(20, 60)
heartPath.ellipticalArcTo(40, 40, 90, false, true, 100, 60)
heartPath.ellipticalArcTo(40, 40, 90, false, true, 180, 60)
heartPath.quadraticCurveTo(180, 120, 100, 180)
heartPath.quadraticCurveTo(20, 120, 20, 60)
heartPath.closePath()

heart2.fillPath(heartPath, "#FC427B")
heart2.writeFile("images/heart2.png")

Both methods will produce the same image:

example output

You can use paths to draw almost anything.

Gradients

var heart3 = newImage(200, 200)
heart3.fill(rgba(255, 255, 255, 255))

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

heart3.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
  """,
  gradientPaint
)

heart3.writeFile("images/heart3.png")

example output

Masking

Pixie supports many different blends and masking modes. You can use masking blends modes to mask out areas of the image. Here we will draw a background "X" then cut the heart path out of it.

var background = newImage(200, 200)

background.fill("#3498db")

background.strokePath(
  """
    M 25, 25
    L 175, 175
    M 25, 175
    L 175, 25
  """,
  paint = "#ecf0f1",
  strokeWidth = 30
)

background.writeFile("images/masking1.png")

You can see this produces crossed lines:

example output

Mask paint is a special type of paint that has blendMod set to MaskBlend. Instead of adding color to the image the MaskBlend will eat color a way:

var maskPaint = newPaint(SolidPaint)
maskPaint.color = color(1, 1, 1, 1)
maskPaint.blendMode = MaskBlend

And now you can just draw the path normally but with the new mask paint:

var mask = newImage(200, 200)
background.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
  """,
  maskPaint
)

background.writeFile("images/masking2.png")

example output

This results in a cutout "X" image.

Drawing text

Pixie has many powerful text features.

You can typeset text in different ways:

var textImage = newImage(240, 70)
textImage.fill("#FFFFFF")
var font = readFont("fonts/Roboto-Regular_1.ttf")
font.size = 40

let text = "Hello world."

textImage.fillText(font.typeset(text, vec2(240, 60)), translate(vec2(10, 10)))
textImage.writeFile("images/text1.png")

example output

Text is layed out in spans and spans are layed out into an arrangement. You can create different spans of text of different fonts and sizes.

var textImage = newImage(200, 200)
let typeface = readTypeface("fonts/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.6, 0.6, 0.6, 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)))
]

textImage.fill("#FFFFFF")
textImage.fillText(typeset(spans, vec2(180, 180)), translate(vec2(10, 10)))
textImage.writeFile("images/text2.png")

example output

Supported features

Currently pixie supports many text features but not all of them:

Description Supported
ASCII English
European Alphabets
Other Alphabets
CJK - Chinese/Japanese/Korean
Cursive Fonts some
Emoji no
RTL - Arabic/Hebrew no
Unicode shaping no
Ligatures no

For websites

Do you have a server that needs to crop or scale images? You can use uploaded file size and image.readImageDimensions() to first make sure its an image you want to load:

let size = readImageDimensions("images/profile.jpeg")
echo size
if size.width > 512 or size.height > 512:
  quit("Image dimensions too big")

Now that we know its not to big to cause memory issues we can read and crop it:

var profile = readImage("images/profile.jpeg")
var smallProfile = profile.resize(64, 64)
smallProfile.writeFile("images/smallProfile.png")

We made a bigger image:

example output

Turn into a 64x64 profile image:

example output

You can also cut people out of a circle for those new "modern profiles images". Simply mask some thing with a circle like this:

var maskPath = newPath()
maskPath.circle(32, 32, 32)
smallProfile.fillPath(maskPath, maskPaint)

Then just write it out:

smallProfile.writeFile("images/circleProfile.png")

example output

Some times you don't have a person's photo and want to just create a colored circle with the letter. First we will draw the background circle:

var letterProfile = newImage(64, 64)
var path = newPath()
path.circle(32, 32, 32)
var paint = newPaint(SolidPaint)
paint.color = parseHtmlColor("#2ecc71")
letterProfile.fillPath(path, paint)

letterProfile.writeFile("images/letterProfile1.png")

That looks like this:

example output

Then we will draw the foreground letter:

var profileFont = readFont("fonts/Roboto-Regular_1.ttf")
profileFont.size = 36
profileFont.paint = "#ecf0f1"
let profileLetters = "Ev"
letterProfile.fillText(profileFont.typeset(
  profileLetters,
  vec2(64, 64),
  hAlign = CenterAlign,
  vAlign = MiddleAlign
))

And this is what you get:

letterProfile.writeFile("images/letterProfile2.png")

example output

For games and openGL

You can load an image and convert it to a texture like this:

import opengl

proc loadTexture() =
  let testTexture = readImage("examples/data/testTexture.png")

  var textureId: uint32
  glGenTextures(1, textureId.addr)
  glBindTexture(GL_TEXTURE_2D, textureId)
  glTexImage2D(
    target = GL_TEXTURE_2D,
    level = 0,
    internalFormat = GL_RGBA8.GLint,
    width = testTexture.width.GLsizei,
    height = testTexture.height.GLsizei,
    border = 0,
    format = GL_RGBA,
    `type` = GL_UNSIGNED_BYTE,
    pixels = cast[pointer](testTexture.data[0].addr)
  )
  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)

example output

See the complete working example: https://github.com/treeform/windy/blob/master/examples/textured_quad.nim

Keep in mind that pixie images are in premultiplied alpha state. We recommend doing all alpha blending in premultiplied alpha for 2d and 3d graphics.