version 1.1.0, October 2005
by Niklas Frykholm (niklas@frykholm.se)
http://www.frykholm.se/rita.html
Rita is a paint program for MacOS X that attempts to be limitless, flexible and easy to use. Unlike traditional paint programs Rita does not restrict you to a particular image size or resolution. Instead, it uses an infinite canvas. You can make the image as big as you like and zoom in as much as you want without ever encountering any big blocky pixels.
Zooming in Rita
Rita accomplishes this by not storing image pixels. Instead, it stores the brush strokes that make up the image. This means that the image can be recreated at any resolution without loosing precision.
Rita's main focus is freehand drawing using a digitizer, such as a Wacom board -- not photo editing or vector graphics. Of course you can use Rita with a mouse as well, but it is not as fun. If you like to draw on your computer, I can really recommend investing in a digitizer.
Rita is distributed as shareware. If you use it and like it, you should purchase it. The price is only $20 and your support will help fund future development of Rita.
If you have ideas, feature requests or bug reports, you can send them to rita@frykholm.se.
Unlike a lot of other shareware, Rita is not time limited or crippled. You can try out all features at your own pace. Also, you only need to pay for Rita once. The license is valid for all computers in your household and for all future versions of Rita. I try to be a nice guy.
There are several ways of paying. Use whichever is most convenient to you:
The online Kagi store accepts credit card and check payments.
PayPal accepts PayPal and credit card payments.
PlusGirot is a good option if you are located in Sweden. My postgiro account number is: 731126-8515. 20$ is 150 SEK.
Cash is always an option. Contact me to get an up-to-date postal address for sending cash.
In addition to cash, I am also fond of gifts and surprises. Please don't send me foreign (non-Swedish) checks in the mail. The Swedish banks won't cash them.
For a company with many computers, contact me to discuss site license pricing.
One more thing. You can trust that your Rita files will not be abandoned. If I am unable to continue developing Rita, I will release all code as open source so that someone else can pick up the torch.
The basics of Rita are real simple -- just draw with your mouse or digitizer. Select tools and colors from the menus.
The sections below will explain some of the more subtle aspects of drawing with Rita.
All drawing in Rita is done with the pen tool. The properties of the pen is controlled through the Tools menu or the Tool Properties... panel.
The most important pen property is the pen type. Different types of pens are used to draw different things. The Brush draws brush strokes. The Eraser erases stuff. The Line Drawer draws lines. Etc. The different pen types are described in the next section.
Four additional properties can be set for every pen: color, size, opacity and transfer mode. In addition, some pen types have additional properties which are available from the drop down menu at the bottom of the properties panel.
The size of a pen determines the thickness of the lines it draws. The size is independent of the zoom level. No matter how much you zoom in or out the pen remains the same size on the screen.
The opacity parameter determines to what extent what you draw affects the picture. A lower setting creates more transparent strokes.
The transfer mode specifies the pixel operation that should be used to merge the pixels drawn by the pen with the pictures already on the canvas.
The available options are:
Normal
Strokes are drawn on top of the background.
Erase
Strokes erase pixels from the background.
Multiply
Stroke pixels are multiplied with the background. This creates a darkening effect.
Colorize
Strokes colorize white or colored areas of the background, but do not erase black line art.
Shadow
Strokes create a shadow effect on the background. The opacity value determines the strength of the effect.
Hilight
Strokes create a hilight effect on the background.
Darker
Strokes are drawn on top of the background, but only where the stroke color is darker than the background.
Lighter
Strokes are drawn on top of the background, but only where the stroke color is lighter than the background.
Option-clicking with the pen tool allows you to pick colors from the image.
The Pen Type sub menu and the Tool Properties dialog box let you select among many different pen types. Below is a short description of the different pens.
Brush
The normal drawing tool, similar to a Photoshop brush.
Eraser
Similar to the brush, but erases content instead of drawing a particular color.
Line
Draws straight lines. Hold down shift to restrict the angle.
Rect
Draws a rectangle outline. Hold down shift to draw a square.
Filled Rect
Draws a filled rect. Hold down shift to draw a square.
Oval
Draws an oval outline. Hold down shift to draw a circle.
Filled Oval
Draws a filled oval. Hold down shift to draw a circle.
Polygon
Draws a polygon. Use the custom parameters to control the number of sides and whether to draw a filled polygon or an outline.
Arrow
Draws a straight line arrow. Hold down shift to restrict the angle.
Table
Draws a table. The custom parameters control the number of rows and columns.
Panels
Draws a number of rectangular panels with spacing in between -- similar to the panels in a comic strip.
Grid
Draws a grid of squares.
Hex Grid
Draws a grid of hexagons.
Freehand curve
Lets you sketch a curve and then draws the curve that matches the sketch as closely as possible.
Freehand arrow
As freehand curve, but the curve is ended with an arrow.
Freehand shape
Lets you sketch a complicated shape which is then simplified to a smoother curve. The custom parameters let you control how much the path is smoothed.
Freehand oval
Lets you sketch an oval. If you hold down shift you sketch a circle.
Colorizer
A special pen for colorizing and re-colorizing black and white drawings.
Shadow
A special pen for drawing shadows.
Highlight
A special pen for drawing hilights.
Darker
A pen that only draws where it makes the image darker.
Lighter
A pen that only draws where it makes the image lighter.
Scatter
A pen that draws scattered dots.
Hatches
A pen that draws scattered hatches.
The Zoom Tool, Rotate Tool and Move Tool can be used to control the view. Clicking the zoom tool zooms in, option-clicking zooms out. Dragging with the rotation tool rotates the view. Dragging with the move tool moves the view.
Most of the time, however, you only wish to make a single adjustment to the view (zoom or pan) and then continue to draw. In those situations it is a bit of a hassle to switch to a different tool.
You can use the shortcut commands Zoom to Rect, Rotate and Move in the View menu for such occasions. These commands temporarily switch to the corresponding tool and switch back once you have performed the adjustment.
The Zoom to Full command is useful for getting a complete overview of your artwork.
The Fill Tool is the flood fill (or paint-bucket) tool known from Photoshop and many other drawing programs.
Since Rita does not work with pixels like other paint programs, the fill tool does not fill pixel by pixel. Instead, it creates a vector graphics object that attempts to simulate the effect of a pixel fill operation. The process is not completely perfect and may occasionally miss a pixel or two.
The Fill Tool Properties panel controls the properties of the fill tool. Transfer mode and opacity work the same way for the fill tool as for pens. Tolerance controls how dissimilar neighboring pixels are allowed to be before the flood fill stops.
Expand expands the filled region by the specified number of pixels along all edges. If you get white pixels around the filled area because the fill algorithm is less than perfect, you can sometimes solve this by expanding the filled region with one or two pixels.
The strokes you have drawn can be moved, scaled and resized just like objects in a vector drawing program. The Arrow Tool is used to manipulated strokes. Click or drag with the tool to select individual strokes or groups of strokes. Drag the strokes to move them. Command-drag the strokes to rotate and resize them.
When you drag to select strokes, you normally only select the strokes which are fully enclosed by the dragged rect. You can option-drag to select all the strokes touched by the drag rectangle instead.
There are a number of menu options that affect selected strokes.
Cut, Copy, Paste, Clear and Duplicate can be used to copy and paste strokes.
Merge merges two or more separate strokes to a single object. Merged strokes can only have a single color, so if the objects have different colors the result will probably not be what you expect.
The Arrange sub menu can be used to change the order in which strokes are drawn.
Rita can import bitmaps such as PNG-files, GIF-files or JPEG-files. You can import bitmap data into Rita by pasting, by drag-and-drop or by the Place menu command.
Currently, Rita cannot import vector data.
Rita can export data to bitmaps for use on web pages and in other programs. The simplest way of exporting bitmap data is perhaps to use the Copy to Bitmap command in the Edit menu. This will create a bitmap of the current window at the screen resolution and save it to the clipboard. You can then paste the image into other applications. Copy to Bitmap 2x is similar, but copies the image at twice the screen resolution. Copy to Bitmap 3x uses three times the screen resolution.
To have greater control you can use the Export Bitmap... option. The dialog box lets you export the image as PNG, JPEG, TIFF or GIF.
Note that when you export to bitmap data, you always export the content of the window, not the entire image. (Because the entire image can potentially be infinitely large.) Make sure to frame the object you want to export nicely in the window before exporting.
Printing in Rita works as in most other programs, but there is one small subtlety. If possible -- Rita will try to generate a vector PDF and use that for printing. If this is not possible, Rita will convert the image to a bitmap and print the bitmap.
Having a vector PDF is sometimes preferable -- the file is smaller, printing is quicker and the resulting PDF works well at all resolutions. You can ensure that you get a vector PDF by only using transfer mode Normal and by only drawing strokes at opacity 100 %. (Note that this for example means that you cannot use the Eraser pen.) Other transfer modes than Normal and other opacities than 100 % cannot be accurately represented by vector PDFs and will force generation of a bitmap PDF.
Basic drawing is speedy in Rita -- you are unlikely to notice any significant delays. Unfortunately, because of the way Rita is built, some operations are inherently slow. By understanding which operations are slow and why, you can reduce such slowdowns to a minimum which will make working with Rita a much more pleasant experience.
The basis for understanding slowdowns is to remember that Rita does not store the image as a bitmap at a particular resolution. Instead, it stores the individual strokes that make up the image. However, on the screen, the image is drawn as a bitmap. If this bitmap has to be redrawn, Rita must loop through all the strokes in the image and redraw them. This is by necessity a slow procedure. The time it takes depends on three factors:
The number of strokes in the image.
The complexity of the strokes (i.e. how big the strokes are, if they are drawn with transparency, etc).
The size of the bitmap that is generated.
As you work with an image, adding more and more strokes, the image will take longer and longer to redraw.
During normal drawing, Rita does not have to redraw its bitmap, so everything will run at a reasonable speed. However, some operations force Rita to redraw the bitmap:
Changing the view. (Zooming, rotating or moving the view.)
Resizing the document window.
Manipulating strokes. (Moving, scaling, merging or arranging them.)
Undoing operations.
Exporting to bitmap.
Printing.
If you avoid these operations as much as possible (avoiding them completely is of course not possible) you will get reasonable response times.
All pens in Rita are defined by Lua scripts. If you have a little bit of programming skill you can customize Rita by creating your own pens. This section will document in more detail how to write your own pens. For more information on the Lua programming language, see
Pens are stored in the Contents/Resources/Pens directory inside the Rita.app application directory. Every .lua file in that directory that defines a global name variable will be treated as a pen definition by lua. (You can put library .lua files in this directory. As long as they don't define a name variable, they won't be interpreted as pen definitions.)
A pen definition consists of a number of global variables and (possibly) a couple of callback functions. You can look at the existing pen definition files in Contents/Resources/Pens to see some examples.
The global variables interpreted by Rita are (some sample values are shown for each variable):
name = "Brush"
The name of the pen, as shown inside Rita. The name specified in the .lua file can be localized to different languages using the Lua.strings localization file.
key = "option-p"
Shortcut key for activating this pen. The shortcut can be written as: "p", "shift-p", "option-p", "command-shift-p", etc. Make sure your shortcut does not conflict with other existing shortcuts. If you don't define this variable, the pen won't have a shortcut.
submenu = "Special Pens"
The submenu that this pen should be put under. The value you specify here is translated through the Lua.strings file.
If you don't specify a submenu, the pen will be put in the root menu.
menu_position = 101
The pens in the Pen Type menu are sorted in ascending order by the value of their menu_position variables. Submenus are sorted by the menu_position of a random menu element. So to ensure that a certain submenu appears before another submenu, all the pens in the first menu should have a menu_position lower than the menu_position of all pens in the second menu.
mode = "darker"
The transfer mode that should be the default for the pen. (The user can change transfer mode with the pen properties panel.) The possible values for this parameter are:
- "normal"
- "erase"
- "darker"
- "lighter"
- "colorize"
- "shadow"
- "highlight"
- "multiply" If no mode is specified, "normal" is used.
color = "ffff00"
The default color of the pen. The color is specified with a six digit hex number, just as in HTML, so "ffff00" means yellow.
If you don't specify a color the default color is black.
opacity = 1.0
The default opacity of the pen. (1.0 means 100 %.) If you don't specify an opacity, the opacity is 1.0.
radius = 10
The default radius of the pen. If you don't specify a radius, the default radius is 5.
custom_pen = false
If custom_pen is false the pen will use the normal drawing method of the brush and eraser tools. Basically -- you draw with the mouse and Rita paints what you have drawn.
If custom_pen is true you can completely define what the pen should draw and how it should work by providing three callback functions: pen_down, pen_up and pen_move. In this way you can create line drawers, polygon drawers or anything else that you can think of. More information on how to write the callbacks is given in the next section.
If you don't specify the custom_pen variable, it defaults to false.
parameters = {"length", "width"}
This variable defines a vector of custom parameters for the pen. The custom parameters are available to the user in the drop down menu in the Pen Properties... panel. The names of the parameters are translated by the Lua.strings file.
Custom parameters are always integers. If you need a boolean value, I suggest you use an integer ranging from 0 to 1. If you need a float value, I suggest you divide your floating point range into a suitable number of intervals and translate it to an integer range.
A custom parameter has a current value, a minimum value and a maximum value. These are specified as global variables. The current value variable has the same name as the parameter. The min and max variables have _min and _max appended, i.e.:
parameters = {"length", "width"} length = 10; length_min = 5; length_max = 30 width = 20; width_min = 5; width_max = 30
When the user changes the parameter in the Pen Properties... dialog box, the current value variable will change. So if you use that variable in your program, you always get the value that has been set by the user.
If custom_pen is set to true, no automatic drawing is done for the pen. Instead your program will receive callbacks when the user uses the pen. Inside these callbacks you do all the drawing of the pen.
pen_down (at, radius)
This callback is called whenever the user puts down the pen. at is the coordinate where the user put down the pen. It is a table of the form {x = x-coordinate, y = y-coordinate}. radius is the current radius of the pen. It is the radius set in the Pen Properties... panel multiplied by the current pressure of the pen (if the user is using a pressure sensitive device).
pen_move (at, radius)
This callback is called whenever the user moves the pen.
pen_up ()
This callback is called whenever the user lifts the pen. It is typically used to finalize the stroke.
Rita makes the following functions available to the callback functions:
table.xxx, string.xxx, math.xxx
The standard Lua functions in the base, table, string and math libraries are available. The io and debug functions are not available for safety reasons.
bezier_path () : path
Creates a new bezier path and returns it. Rita uses bezier paths for all its drawing. If you want to learn more about bezier paths I\ suggest you look at the documentation for the NSBezierPath class in the Cocoa documentation.
move_to_point (path, point)
Moves the current position in the bezier path path to the specified point. The point is specified as a table {x = ..., y = ...}.
line_to_point (path, point)
Draws a line in the bezier path from the current position to the point.
curve_to_point (path, target_point, control_point_1, control_point_2)
Draws a curve in the bezier path from the current point to the target_point with control points controlpoint1 and controlpoint2 controlling the curvature.
append_bezier_path_with_oval_in_rect (path, rect)
Appends the bezier path with an oval that fits in the rect. The rectangle is specified as a table {x = ..., y = ..., w = ..., h = ...}, where w and h are the width and height of the rectangle.
append_bezier_path_with_rect (path, rect)
Appends the bezier path with the rectangle rect.
close_path (path)
Closes the current active sub-path in the bezier path by joining the current point with the first point.
set_line_width (path, width)
Sets the line width that will be used for the bezier path when it is stroked.
retain (path)
When you create a path with the bezier_path function, that path is assumed to exist only for the duration of the current callback. If you want to save the path in a global variable and reuse it in a later callback (for example, if you want to incrementally build the path during the pen_move calls) you must call retain on it to make sure it is not deleted. If you have called retain on a path you must call release when you are done with it to return the memory to the system.
You can call retain and release multiple times. The functions follow their normal Objective-C semantics. If you're not familiar with retain and release from Objective-C it may be best to abstain from using them.
release (path)
Releases a path that has previously been retained. The number of calls to release should match the number of calls to retain.
set_color (color)
Sets the color to be used for drawing with the stroke and fill calls. The color is specified as a table {red = ..., green = ..., blue = ..., alpha = ...}. The value of each component should be between 0 and 1. The color components are assumed to be 0 if not specified. The alpha component is assumed to be 1 if not specified.
add_stroke (path)
Adds a stroked bezier path to the drawing. Added strokes are always affected by the current pen settings for color, opacity and transfer mode.
add_fill (path)
Adds a filled bezier path to the drawing.
stroke (path)
Draws the bezier path with a temporary stroke. Temporary strokes are not added to the drawing. Instead, they are erased as soon as the pen is moved. Temporary strokes are used for example in the line drawer to preview what the line will look like.
fill (path)
Fills the bezier path with a temporary fill.
preview_stroke (path)
Draws the bezier path with a preview stroke. A preview stroke is like a temporary stroke, but instead of just lasting until the pen is moved a preview strokes lasts until a stroke is added to the image. Preview strokes are used for example by the freehand tools to let the user see the approximate shape she has drawn until it is replaced by the real shape.
preview_stroke (path)
Fills the bezier path with a preview fill.
set_pen_color (color)
Sets the color of the pen.
get_pen_color () : color
Returns the pen color.
set_pen_opacity (opacity)
Sets the pen opacity.
get_pen_opacity () : opacity
Returns the pen opacity.
set_pen_radius (radius)
Sets the pen radius.
get_pen_radius () : radius
Returns the pen radius.
set_pen_transfer_mode (transfer_mode)
Sets the pen transfer mode. transfer_mode should be one of the transfer mode strings.
get_pen_transfer_mode () : transfer_mode
Gets the pen transfer mode.
pressure () : pressure
Returns the current pressure of the pen as a value between 0 and 1. If the user is not using a pressure sensitive device the value will always be 1.
scale () : scale
Returns the current scale of the user's view. If the user is zoomed in 10 times the value is 10. If you want to draw a line that is one pixel thick no matter how much the user has zoomed in you can draw a line with a width of 1.0/scale(). (However, most of the time, what you really want to do is to use the radius from the pen callback functions.)
caps_lock_down () : bool
Returns true if the caps lock key is currently held down. You can use this to modify the pen behavior based on the pressed modifier keys.
shift_down () : bool
control_down () : bool
option_down () : bool
command_down () : bool
Similar to capslockdown. Note that the option key triggers the color picker, so you probably don't want to use it to modify pen behavior. The function is provided for completeness.
If you look at the pen definitions in the Contents/Resources/Pens folder, most of them make use of the lib file lib.lua. This is a library file that provides some often reused common functionality:
If you don't fully understand the code in lib.lua, the pen definition files can be a bit hard to read. To get started with a simpler example, look at the file sample.lua. It contains a sample pen that doesn't use the lib.lua file. You can use it as a basis for your own pen experiments.
The code from sample.lua is reproduced here:
--------------------------------------------------------------------------------
-- File description
--------------------------------------------------------------------------------
-- This is a sample pen definition file. This pen draws a line with tick
-- marks. The number of tick marks along the line can be configured with a
-- parameter. You can use this file as a basis for your own pen definitions.
--
-- To make this example as simple to understand as possible, it does not make
-- use of "lib.lua" or any other library file. The code is completely self-
-- contained.
--
-- Niklas Frykholm, 28 Aug 2005
--------------------------------------------------------------------------------
-- Pen Variables
--------------------------------------------------------------------------------
-- The name of the pen. This is also the variable that makes Rita recognize
-- this file as a pen definition file. Currently, the variable is commented
-- out, so this pen will not be seen inside Rita. To make it possible to use
-- this pen inside Rita, uncomment the line below:
-- name = "Sample"
-- The key used to activate this pen
key="ctrl-s"
-- The position of this pen in the menu. Currently, pens in the root menu are
-- numbered 1, 2. Pens in the first submenu are numbered 101, 102, 103,
-- pens in the second submenu 201, 202, 203, etc.
menu_position = 601
-- The sub menu that this pen should appear in
submenu = "My own pens"
-- This tells Rita, that we want to implement our own drawing routines by
-- intercepting the pen_down, pen_move and pen_up events.
custom_pen = true
-- Lists all the custom parameters used by this pen. Currently we only use
-- one, the number of ticks along the line.
parameters = {"ticks"}
-- By default, we draw ten tick marks. We allow anything between 1 and 100 marks.
ticks = 10; ticks_min = 1; ticks_max = 100
--------------------------------------------------------------------------------
-- Pen Callback Functions
--------------------------------------------------------------------------------
-- We use this global variable to keep track of the start position for the line.
start = {x=0, y=0}
-- We use this global variable to keep track of the end position for the line.
stop = {x=0, y=0}
-- We use this global variable to keep track of the line width we should use for
-- the line.
line_width = 1
-- This function makes a path to be drawn (a stroke with tick marks) given
-- a starting {x,y}-point and a final {x,y}-point. It is used by all the
-- callback functions below.
function make_path(start, stop, width)
-- make the path
local path = bezier_path()
set_line_width(path, width)
-- draw the main stroke
move_to_point(path, start)
line_to_point(path, stop)
-- find the direction to draw the tick marks in, we want them to be orthogonal
-- to the main stroke and 3 times as long as the width of the main stroke
local stroke = {x = stop.x - start.x, y = stop.y - start.y}
local stroke_len = math.sqrt(stroke.x*stroke.x + stroke.y*stroke.y)
if stroke_len == 0 then
return path
end
local tick = {x = stroke.y / stroke_len * 3 * width, y = -stroke.x / stroke_len * 3 * width}
-- Loop ticks+1 times to draw the tick marks
for i=0,ticks do
local t = i/ticks
local p = {x = start.x + t*stroke.x, y = start.y + t*stroke.y}
move_to_point(path, {x = p.x - tick.x, y = p.y - tick.y})
line_to_point(path, {x = p.x + tick.x, y = p.y + tick.y})
end
return path
end
-- Callback for when pen is down, save the start point in the start global variable
function pen_down(p,r)
start = p
line_width = r
end
-- When pen is moved, preview the result by temporary stroking the path
function pen_move(p,r)
stop = p
line_width = r
stroke(make_path(start, stop, r))
end
-- When pen is lifted, add the stroke to the drawing
function pen_up()
add_stroke(make_path(start, stop, line_width))
end
Best wishes and good luck with using Rita.
Niklas Frykholm
niklas@frykholm.se
© 2012, Niklas Frykholm, administrator@frykholm.se