Wednesday, June 3, 2015

First Drawings

The best way to understand the future structure of our test application is to view a small class diagramm:


It describes our Render classes. We use Renderer as abstract base class which contains a Color buffer and the size of our buffer (width * height). From Renderer we derive one drawing class (RendererSimipleDrawing) and two additional abstract classes (Renderer2D and Renderer3D). We ignore the latter ones for this section and continue with some other important stuff for this post.

Update: Mathtools


We add a function pixelIndex(width, row, col) which returns an index of a 1-dimensional array according to row, column and width of a color buffer.

Color


This class contains color informations in RGB format (you could also add an alpha channel, but there is no actual need for that). Color channels can be combined and calculated similar to our Vector classes, with the difference that there are no dot and cross products.

red, green and blue color channels consists values between 0.0 and 1.0. We only have to consider that certain user inputs might be outside of our color scale (in constructor and setter).

There are 3 possibilities to change color:
  • setColor(float r, float g, float b)
  • setColor(float color[3])
  • setColor(int colorcode)
where floats consists values between 0.0f and 1.0f and colorcode is written in 0xRRGGBB format.

IMPORTANT: DON'T FORGET 0x BEFORE WRITING COLOR CODES

Last but not least getter methods can return colors as float values AND as 8Bit color values. This is very important on creating Images in PPM.

Image


The Image class is similar to Renderer class, with the difference that we can't "render" anything. It needs width, height and a color buffer as input, We might still change color of a single pixel of it with at(row, column). Normally we use a coordinate system where x goes left right and y goes from bottom up. But an image uses x left right and y top down.


The most important part of Image is its save method to create an Portable Pixmap (PPM) file. The structure looks as following:

P6 <width> <height> <max color> <RRGGBB><RRGGBB><RRGGBB>....

P6, width, height and max color have to be written in ASCII code, while the color for each pixel is in binary code. Example of the head line of a PPM file (above: Ascii, below: binary in hexdecimal code)

P6 640 480 255 <colors>
50 36 20 36 34 30 20 34 38 30 20 32 35 35 20 <colors>

  • magic number P6 = 0x5036
  • space = 0x20
  • width 640 = 0x363430
  • space = 0x20
  • height 480 = 0x343830
  • space = 0x20
  • max color 255 = 0x323535
  • space = 0x20
afterwards binary color codes are displayed (i.e. yellow = 0xFFFF00). That's why we need Color::getRed8B(), Color::getGreen8B() and Color::getBlue8B().

Painter


Finally, the class where we can draw something. This class has as attributes:
  • renderer (to draw onto)
  • line color
  • fill color
  • fill (bool)
and methods
  • setLineColor(...)
  • setFillColor(...) (sets fill attribute to true)
  • setFiller(true or false)
  • drawLine(...)
  • drawRect(...)
  • drawEllipse(...) (also to draw circles)
All drawing methods are based on Bresenham's algorithm to draw lines (even fillings xD) and border of ellipses. Because we don't use transformations (yet), we can simply consider that all fillable objects (rectangles and ellipses) are parallel to x- and y-axis. Except for using a loop which goes from one border (width and height) to another we simply draw a line of the filling color.

A bit tricky is filling out an ellipse/circle: I put in an additional variable lastDy to ask if dy was already used for filling. If not, draw a line from left to write of the current row.

Renderer (abstract)


Abstract class from which we derive other classes having the same important attributes:
  • color buffer
  • width
  • height
and our most important methods
  • initBuffer() (preparing our buffer)
  • setBackgroundColor(...) (3 different possibilities)
  • colorPixel(x, y, color) to color a pixel (x for column and y for row)
  • getColor(x, y) to get and edit the color of pixel x/y
  • createImage() puts color buffer into Image class
  • render() abstract method we need to override later. needed for drawing

RendererSimpleDrawing (derived from Renderer)


This class is derived from class Renderer. There is nothing special about this class, except that we override render() so that we can draw something. We use Painter to draw on a Renderer.

The current render function creates the following picture:




Summary

We added quite a lot of new classes in our application and created the fundamentals for future algorithms. In the next issue we will discuss 2D transformations more clearly. Therefore we create a 2D objects which contains lots of points and lines to be drawn. Except for Painter::drawLine(...) we won't be able to use the other drawing methods like before. Even ellipses will consist of multiple lines, forming an "infinitygon".

Like always, you can find everything in my Repository. Code was compiled and tested with
  • Visual Studio 2013 (Windows 8.1)
  • GNU 4.9.2 (Ubuntu 12.04)
See you in my next post.

No comments:

Post a Comment