17Image Operation

17.1Overview

17.2Image Instance

An instance of image class contains image data and provides functions such as reading/writing image files, resizing and rotating.

An image instance can be created by a constructor function image. Calling image function with an argument that specifies a stream containing an image data would read that data. The code below reads a JPEG file and write it in PNG format.

import(jpeg)
import(png)
image('foo.jpg').write('foo.png')

Before image function, you have to import a module that can handle an image type. The following table shows image types and associated module names.

Image Type Module Added Methods to image
BMP bmp bmpread, bmpwrite
JPEG jpeg jpegread, jpegwrite
GIF gif gifread, gifwrite
PNG png pngread, pngwrite
Microsoft Icon msico msicoread, msicowrite
PPM ppm ppmread, ppmwrite
XPM xpm xpmdata, xpmwrite
TIFF tiff tiffread

Importing those modules also add methods to image class like jpeg module adding image#jpegread and image#jpegwrite.

17.3Format-specific Operations

17.4JPEG

EXIF

17.5GIF

Here is a JPEG image file that contains animation frames: cat-picture.jpg.

cat-picture

(Any size of picture would be acceptable if only all the frames have the same size and are aligned at regular invervals.)

The program needs to do the following jobs.

  • Reads a JPEG file as a source image.
  • Reduces number of colors in the image down to 256 so that it suits GIF specification.
  • Creates a GIF content.
  • Divides the source image into frames and adds them to the GIF content.
  • Writes the GIF content to a file.

And here is the script code:

import(jpeg)
import(gif)

delayTime = 12             // interval time in 1/100 seconds
[nx, ny] = [6, 2]          // number to divide a source image
img = image('cat-picture.jpg').reducecolor(`win256)
[w, h] = [img.width / nx, img.height / ny]
i = range(nx * ny)
xs = (i % nx) * w
ys = int(i / nx) * h
imgFrames = img.crop(xs, ys, w, h)
gif.content().addimage(imgFrames, delayTime).write('cat-anim.gif')

It utilizes Implicit Mapping feature to process frame images. If you're interested in what's running in the code, trace the variable imgFrames about how it's created by image#crop() and how it's processed in gif.content#addimage().

cat-picture cat-anim.gif

17.6Cairo

17.6.1Simple Example

Here is a simple example using Cairo.

import(cairo)
import(show)

img = image(`rgba, 300, 300)
img.cairo {|cr|
    cr.scale(img.width, img.height)
    cairo.pattern.create_linear(0, 0, 1, 1) {|pat|
        pat.add_color_stop_rgb(0, 0, 0, 0)
        pat.add_color_stop_rgb(1, 1.0, 1.0, 1.0)
        cr.set_source(pat)
    }
    cr.rectangle(0.1, 0.1, 0.8, 0.8)
    cr.fill()
}
img.show()

17.6.2Render in Exisiting Image

The following is an example that performs reading a JPEG file, drawing something on it with Cairo APIs and writing it out as a JPEG file.

import(jpeg)
import(cairo)
I(filename:string) = path.join(sys.datadir, 'sample/resource', filename)
img = image(I('Winter.jpg'))
img.cairo {|cr|
    repeat (10) {|i|
        [x, y, r] = [128 + 30 * i, 128 + 30 * i, 60 - i * 4]
        pat = cairo.pattern_create_radial(
            x - r / 10, y - r / 6, r / 5, x - r / 6, y - r / 6, r * 1.2)
        pat.add_color_stop_rgba(0, 1, 1, 1, 1)
        pat.add_color_stop_rgba(1, 0, 0, 0, 1)
        cr.set_source(pat)
        cr.arc(x, y, r)
        cr.fill()
    }
}
img.write('result.jpg')

17.6.3Output Animation GIF File Combining Multiple Image Files

You can create a GIF file that has a dynamically produced image. The example below shows how to output an animation GIF file that contains images created by Cairo APIs.

import(cairo)
import(gif)
str = 'Hello'
img = image(`rgba, 64, 64, `white)
gifobj = gif.content()
img.cairo {|cr|
    cr.select_font_face('Georgia', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
    cr.set_font_size(64)
    te = cr.text_extents(str)
    cr.set_source_rgb(0.0, 0.0, 0.0)
    for (x in interval(64, -te.width, 30)) {|i|
        img.fill(`white)
        cr.move_to(x, 50)
        cr.show_text(str)
        gifobj.addimage(img.clone(), 10)
    }
}
gifobj.write('anim2.gif')

17.6.4More Sample Scripts

You can find sample scripts using Cairo on GitHub repository.

17.7OpenGL

17.7.1Sample Script

Gura supports APIs of OpenGL 1.1.

The following example has been ported from one of the samples in http://www.wakayama-u.ac.jp/~tokoi/opengl/libglut.html.

import(glu) {*}
import(opengl) {*}
import(gltester)

vertex = [
    [0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]
    [0, 0, 1], [1, 0, 1], [1, 1, 1], [0, 1, 1]
]

init(w:number, h:number) = {
    glClearColor(1, 1, 1, 1)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glEnable(GL_DEPTH_TEST, GL_CULL_FACE)
    glEnable(GL_LIGHTING, GL_LIGHT0, GL_LIGHT1)
    glCullFace(GL_FRONT)
    glViewport(0, 0, w, h)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(30, w / h, 1, 100)
}

display(degree:number) = {
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
    gluLookAt(3, 4, 5, 0, 0, 0, 0, 1, 0)
    glRotated(degree, 1, 1, 0)
    glMaterialfv(GL_FRONT_AND_BACK,
            GL_AMBIENT_AND_DIFFUSE, [0.8, 0.2, 0.2, 1])
    glBegin(GL_QUADS) {
        glNormal3dv([ 0,  0, -1]), glVertex3dv(vertex[0, 1, 2, 3])
        glNormal3dv([ 1,  0,  0]), glVertex3dv(vertex[1, 5, 6, 2])
        glNormal3dv([ 0,  0,  1]), glVertex3dv(vertex[5, 4, 7, 6])
        glNormal3dv([-1,  0,  0]), glVertex3dv(vertex[4, 0, 3, 7])
        glNormal3dv([ 0, -1,  0]), glVertex3dv(vertex[4, 5, 1, 0])
        glNormal3dv([ 0,  1,  0]), glVertex3dv(vertex[3, 2, 6, 7])
    }
}

degree = 0
[width, height] = [300, 300]
gltester.mainloop(width, height, 0, `idle) {
    `onDraw => function {
        init(width, height)
        display(degree)
    }
    `onKeyPoll => %{
        `left => function { degree += 1 }
        `right => function { degree -= 1 }
    }
}

Execution result.

gl-cube

17.7.2More Sample Scripts

You can find sample scripts using OpenGL on GitHub repository, which have been ported from SGI.