Sie sind auf Seite 1von 171

Lecture Notes for Computer Games Programming 1

Jonathan G. Campbell
Department of Computing,
Letterkenny Institute of Technology,
Co. Donegal, Ireland.

email: jonathan dot campbell (at) gmail.com, jonathan.campbell@lyit.ie


URL:http://www.jgcampbell.com/bscgp1/
Report No: jc/08/0002/r
Revision 2.4 (recompiled with tags and links)
Revision 2.3 (very minor alterations since 2.2)

24th February 2008


Contents

1 Introduction 1
1.1 Purpose . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 The Course | Games Programming 1 . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2.1 Module Aims . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2.2 Module Learning Outcomes . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2.3 Syllabus Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2.4 Assessment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Module Policies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3.1 Plagiarism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3.2 Reading List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.4 Lectures, Learning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.5 Suggested Reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.5.1 Mathematics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.5.2 Computer Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.5.3 Games Programming APIs . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5.4 Physics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.5.5 General Books on Games . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2 What is Computer Programming all about? 1
2.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2.2 Simple Model of a Computer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2.3 Computer Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.4 Assembly Code versus Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.4.1 Simple Program { add two integers . . . . . . . . . . . . . . . . . . . . . 7
2.4.2 If-then . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.4.3 If-then-else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.4.4 Repetition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.5 Back to the calculator program . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.6 Object-oriented Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.6.1 Java versions of Student and ClassList . . . . . . . . . . . . . . . . . . . 11
2.7 Operating Systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3 Overview of Games Programming 1
3.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
3.2 Noughts and Crosses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
3.3 More on object-oriented programming . . . . . . . . . . . . . . . . . . . . . . . . 3
3.4 Back to games . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
3.5 Some Detail on Games Programming . . . . . . . . . . . . . . . . . . . . . . . . 4
3.6 The Programming Language | Java . . . . . . . . . . . . . . . . . . . . . . . . 6

0{1
4 Simple Procedural Programming 1
4.1 Sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
4.2 Repetition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
4.2.1 Sequence of Repetitions . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
4.2.2 Repetitions of Repetitions | Nested Loop . . . . . . . . . . . . . . . . . 6
4.2.3 Data Pattern = Program Pattern . . . . . . . . . . . . . . . . . . . . . . 7
4.3 Procedures | Methods, Subprograms, Functions . . . . . . . . . . . . . . . . . . 8
4.3.1 Procedures Without Parameters . . . . . . . . . . . . . . . . . . . . . . . 8
4.3.2 Subprograms with Parameters . . . . . . . . . . . . . . . . . . . . . . . . 9
4.4 Selection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
4.5 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
5 Threads 1
5.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
5.2 An Example of the Advantages of Multitasking . . . . . . . . . . . . . . . . . . . 2
5.3 Brief Case Study { MS-DOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
5.3.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
5.3.2 History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
5.3.3 Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
5.3.4 Processes in MS-DOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
5.4 Processes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
5.4.1 Process Life cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
5.4.2 Process Control Block (PCB) . . . . . . . . . . . . . . . . . . . . . . . . 9
5.4.3 Context Switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
5.5 The Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
5.5.1 Kernel Privileges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
5.6 Thread Examples in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
6 Images, Displays, Animation, Colour 1
6.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
6.2 3D Graphics, Images, Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
6.3 Digital Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
6.3.1 Continuous/Analogue versus Discrete/Digital . . . . . . . . . . . . . . . . 1
6.3.2 Analogue to Digital Converters and Digital to Analogue Converters . . . . 3
6.3.3 Images and Digital Images . . . . . . . . . . . . . . . . . . . . . . . . . . 3
6.3.4 Anti-aliasing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
6.3.5 Opacity, alpha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
6.4 Displays and Factors Associated with them . . . . . . . . . . . . . . . . . . . . . 9
6.4.1 Display Hardware | CRTs, LCDs, Plasmas . . . . . . . . . . . . . . . . . 9
6.4.2 Flicker and Tearing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
6.5 Visual Perception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
6.6 A Model of a General Imaging System . . . . . . . . . . . . . . . . . . . . . . . . 14
6.6.1 Light and re ection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
6.6.2 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
6.6.3 Uneven Illumination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
6.6.4 Uneven Sensor Response . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
6.6.5 Di use and Specular Re ection . . . . . . . . . . . . . . . . . . . . . . . 16
6.7 Colour . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
6.7.1 Electromagnetic Waves and the Electromagnetic Spectrum . . . . . . . . 17

0{2
6.7.2 The Visible Spectrum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
6.7.3 Sensors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
6.7.4 Spectral Selectivity and Colour . . . . . . . . . . . . . . . . . . . . . . . . 21
6.7.5 Spectral Responsivity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
6.7.6 Colour Display . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
6.7.7 Additive Colour . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
6.7.8 Colour Re ectance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
6.7.9 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
6.8 Cameras and Photographic Film . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
6.9 More on Colour Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
7 Introduction to 2D Graphics 1
7.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
7.2 JFrame Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
7.3 Drawing Shapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
7.4 Y-axis pointing down? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
7.5 Drawing with Di erent Shapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
7.6 Colour . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
8 More 2D Graphics 1
8.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
8.2 The Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
8.3 Some Graphics Using the Framework . . . . . . . . . . . . . . . . . . . . . . . . . 6
8.4 Shape and General Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
8.5 Drawing Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
9 Vectors and Vector transformations 1
9.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
9.2 Vector Addition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
9.3 Vector Rotation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
9.3.1 Homogeneous Coordinates . . . . . . . . . . . . . . . . . . . . . . . . . . 7
9.4 Rotating Shapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
9.5 Scaling Shapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
9.6 Transformation using java.awt.AneTransform . . . . . . . . . . . . . . . . . . . 13
10 Programming Audio 1
10.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
10.2 Audio Data and Sound in Computers . . . . . . . . . . . . . . . . . . . . . . . . . 1
10.2.1 Sound . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
10.2.2 Loudspeakers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
10.2.3 Microphones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
10.2.4 Analogue versus Digital . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
10.2.5 Electronic (or Electro-acoustic) Music . . . . . . . . . . . . . . . . . . . . 3
10.2.6 Electronic Instruments . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
10.2.7 Magnetic Recording . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
10.2.8 Data Compression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
10.3 Java Sound API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
10.3.1 Sampled Audio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
10.3.2 MIDI | Musical Instrument Digital Interface . . . . . . . . . . . . . . . . 6
10.3.3 Java Sound Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

0{3
10.4 Programs Using Java Sound API . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
11 Brief Notes on the 2D Platform Game 1
11.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
11.2 Selected parts of the code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

0{4
Chapter 1

Introduction

1.1 Purpose
This document provides course notes for the module Games Programming 1 at Letterkenny Insti-
tute of Technology.

1.2 The Course | Games Programming 1


1.2.1 Module Aims
To introduce the programming of graphics and animated graphics with application to computer
games and computer based entertainment.

1.2.2 Module Learning Outcomes


A student who successfully completes this module will be able to:

1. Describe the computer representation of digital images and the hardware and software used
for their display;
2. Outline the basic principles of computer graphics and animated graphics;
3. Describe in both qualitative and mathematical or programming language terms the computer
representation of 2D graphics and the hardware and software used for their reproduction;
4. Interpret and describe the operation of computer programs which implement techniques based
on learning outcomes 1 to 3;
5. Construct modi cations (including design and coding of program fragments) to demonstra-
tion programs (learning outcome 4) to alter their behaviour appreciably;
6. Interpret, describe and construct minor modi cations to the computer program implementa-
tion of a simple two dimensional tile and sprite based game.

1{1
1.2.3 Syllabus Content
Section A - Digital Representation of Images and Video (10%)
Sampling. Digitisation. Light; sensing of images; basics of cameras. Image displays. Animation.
Examples and software demonstrations.
Section B - Applications of Mathematics in 2D Computer Graphics (30%)
Trigonometry. Vectors. Matrix mathematics. Vector (linear) transformations. Ane transforma-
tions. Homogeneous coordinates. Practical work using a high-level language API and a matrix
calculator such as Matlab or Octave.
Section C. Brief Introduction to Mathematics for 3D Computer Graphics (5%)
3D Homogeneous coordinates. 3D Ane transformations. Modelling and viewing transformations.
Section D. Introduction to 2D Computer Graphics (30%)
Representation of graphics on a computer. Images; raster; vector. Characteristics (simple) of
graphics software (API view). 2D graphics. Computer program and mathematical representation
of common objects: points, lines, rectangles, circles. Vector versus raster. Programming via an
appropriate API. Inclusion of image data in graphics. Window to viewport transformation.
Section E. Creating a 2D Game (25%)
Tile-based platform games and their implementation; tiles, sprites. Implementation of velocity,
acceleration, gravity, collision detection, parallax scrolling.

1.2.4 Assessment
Marks Breakdown: Continuous Assessment (CA), 60%, Final Written Examination, 40%.
Continuous assessment will comprise four components.

1.3 Module Policies


Late assignments
Late assignments without documented extenuating circumstances will lose 10% of marks per day
late. Those seven days late will receive zero marks. If you have extenuating circumstances, you
should contact me as soon as possible; in particular, be careful about wasting time on completing
an assignment only to hand it in well after the deadline; whether you have valid reasons or not I
cannot give any marks for work handed in after outline answers have been handed out or discussed
in class.

1{2
1.3.1 Plagiarism
Answers should be your own work. When you take information from books, the web, notes or other
sources, do not copy verbatim | convert the ideas into your own words. Also, you should give a
reference for any source used.
Plagiarised work will receive zero marks and will be reported to the college authorities. The
student handbook gives a clear statement on the penalties for plagiarism. See also the Computing
Department Guidelines on this subject.

1.3.2 Reading List


We will use:
These notes.
David Brackeen, Bret Barker and Laurence Vanhelsuwe, Developing Games in Java, New Riders
Publishing (Pearson Education), 2004. ISBN: 1-5927-3005-1.
Jonathan Campbell, Notes on Mathematics for 2D and 3D Graphics, Letterkenny Institute of
Technology, edition of 17th December 2007.
For other books see section 1.5.

1.4 Lectures, Learning . . .


Learning is not a passive process (like watching TV); it is an active process. Thus, I strongly
encourage you to:

 Take notes during lectures, rather than just following along with these notes. Make sure to
bring extra notepaper | these notes will quickly become cluttered if you try to superimpose
signi cant additional notes on them; on the other hand, listening carefully and participating
may be more important than taking notes;
 Do the exercises (I mean unassessed exercises). They, or problems similar to them, crop up
in exams;
 Participate in the classes and practicals tutorials { by participate I mean attempt the exercises
and enter into discussion if the presented solution is di erent from yours; ask questions about
things that remain unclear to you. Don't forget, there are no stupid questions, just people
too stupid to ask them;
 Form study groups; if you, individually, have a problem with the course, it's your problem,
but if it becomes clear that a good number of students have the same problem, then it can
become the lecturer's problem;
 Read books and websites and magazine on the subject;

1{3
 Spend a decent amount of time on the assignments and practicals | they are designed to
help you learn;
 To be honest, learning to learn should probably be your major objective; if you work in the
computer industry for the next forty years, you're going to have to do a lot of learning new
things.

All that aside, I think this is a great opportunity to learn programming through doing things that
you enjoy.

1.5 Suggested Reading


For Games Programming 1, the module text is David Brackeen's Developing Games in Java
(Brackeen, Barker & Vanhelsuwe 2004). You should buy the book or otherwise ensure that you
have access to it. If you look at Amazon.com or Amazon.co.uk, you'll see that Brackeen's book is
highly regarded.
Regarding the web, by the time the module starts, I'll have a website ready at:
http://www.jgcampbell.com/bscgp1; that will have my notes and other module materials. And
loads of links to relevant websites; but don't forget, anybody can publish on the web | anybody;
so try to be a little discriminating.
The web is not the only place where you will nd rubbish. Games programming is a hot topic
at present and there are a lot of books being rushed out: by people who have little idea; poorly
written; poorly edited (alive with typos and other bugs); containing software which does not compile
or execute; . . . . You have been warned, caveat emptor. (As I write in December 2007, the situation
regarding the quality of books has improved greatly.)

In a third-level course, it is essential that you read around the courses. Here I make some sugges-
tions for year 1 and later.

1.5.1 Mathematics
Video games programming involves mathematics, in most cases a lot of mathematics. I'm not
saying you should leave now if you dislike mathematics, but, it's always a possibility . . .
Brackeen's book has a reasonable introduction to the mathematics of 2D and 3D graphics.
(Vince 2001) provides a very nice summary.
(vanVerth & Bishop 2004) is probably the best all round games mathematics book.
I will be handing out a set of notes on the relevant mathematics (Campbell 2007b).

1{4
1.5.2 Computer Graphics
In Year 2, we will use OpenGL, see (Shreiner, Woo, Neider & Davis 2006) (there's an earlier version
on the web that is still quite useful | see my website). My notes on Computer Graphics Using
OpenGL (Campbell 2007a) may be useful.

1.5.3 Games Programming APIs


When you get to C++, Simple DirectMedia Layer (SDL) is a nice easy to learn games program-
ming API; SDL is nicely introduced in (Penton 2003). If you know anyone who wants to learn
programming on their own, via games programming, Andy Harris's Games Programming, The L
Line, The Express Line to Learning looks good; it uses the programming language Python and the
game API Pygame; Pygame is SDL for Python.

1.5.4 Physics
The best two books on game physics are (Millington 2007) and (Palmer 2005)
See also (Eberly 2004) (but dicult); (Bourg 2002) (easier). There's plenty of introductory physics
in Brackeen's book.
Feynman's Lectures on Physics are well regarded: (R. Feynman & Sands 1965a); (R. Feynman
& Sands 1965b); (R. Feynman & Sands 1965c). For light relief, Cartoon Physics: (Gonick &
Hu man 1990)

1.5.5 General Books on Games


Must haves for serious games programmers: (McSha rey 2005), (Llopis 2003), (O'Luanaigh
2006).

1{5
Bibliography

Akenine-Moeller, T. & Haines, E. (2002). Real-time Rendering, 2nd edn, Natick, MA: A.K. Peters.
Angel, E. (2005). Interactive Computer Graphics: a top-down approach using OpenGL, 4th edn,
Addison Wesley.
Astle, D. & Hawkins, K. (2004). Beginning OpenGL Game Programming, Premier Press / Thomp-
son Course Technology.
Barrass, R. (1995). Students Must Write, 2nd ed., Routledge.
Bourg, D. M. (2002). Physics for Game Developers, O'Reilly.
Brackeen, D., Barker, B. & Vanhelsuwe, L. (2004). Developing Games in Java, New Riders
Publishing (Pearson Education). ISBN: 1-5927-3005-1.
Budd, T. (1999a). C++ for Java Programmers, Addison Wesley.
Budd, T. (1999b). Understanding Object-oriented Programming with Java (updated for Java 2),
Addison-Wesley.
Buss, S. R. (2003). 3-D Computer Graphics: a mathematical introduction with OpenGL, Cam-
bridge University Press.
Campbell, J. (2007a). Lecture Notes on Computer Graphics using
OpenGL, Technical report, Letterkenny Institute of Technology. URL.
http://www.jgcampbell.com/graphics1/notes/graphics.pdf.
Campbell, J. (2007b). Notes on Mathematics for 2D and 3D Graph-
ics, Technical report, Letterkenny Institute of Technology. URL.
http://www.jgcampbell.com/bscgp1/notes/grmaths.pdf.
Castleman, K. (1996). Digital Image Processing, Prentice Hall.
Clingman, D., Kendall, S. & Mesdagh, S. (2004). Practical Java Game Programming, Charles
River Media.
Cormen, T., Leiserson, C. E., Rivest, R. & Stein, C. (2001). Introduction to Algorithms, 2nd edn,
MIT Press / McGraw Hill.
Dawson, M. (2004). Beginning C++ Game Programming, Thompson Course Technology. 1435,
pds 15.27, ISBN: 1-59200-206-6.
Dodge, C. & Jerse, T. (1997). Computer Music: synthesis, composition, and performance, New
York: Schirmer Books.

1{1
Eberly, D. H. (2004). Game Physics, Morgan Kaufmann.
Eberly, D. H. (2005). The 3D Game Engine Architecture: Engineering Real-time Applications with
Wild Magic, Morgan Kaufmann. ISBN: 0-12-229064-X.
Eberly, D. H. (2007). 3D Game Engine Design: a practical approach to real-time computer
graphics, 2nd edn, Morgan Kaufmann. ISBN: 0-12-229063-1.
Eckel, B. (2006). Thinking in Java, 4th edn, Prentice Hall.
(Editor), M. D. (2000a). Game Programming Gems, Charles River Media.
(Editor), M. D. (2001). Game Programming Gems 2, Charles River Media.
Editor), O. A. R. B. D. S. (2000b). OpenGL Reference Manual: The Ocial Reference Document
to OpenGL, Version 1.2, Addison Wesley.
et al, P. S. (2005). Fundamentals of Computer Graphics, 2nd edn, A.K. Peters. ISBN: 1-56881-
269-8.
Farin, G. & Hansford, D. (2005). Practical Linear Algebra: A Geometry Toolbox, A.K. Peters.
Flanagan, D. (2005). Java in a Nutshell, 5th edn, O'Reilly and Associates.
Foley, J., van Dam, A., Feiner, S. & Hughes, J. (1990). Computer Graphics: principles and
practice, 2nd edn, Addison Wesley. ISBN:0-201-12110-7.
Foley, J., van Dam, A., Feiner, S., Hughes, J. & Phillips, R. (1994). Introduction to Computer
Graphics, Addison Wesley. ISBN:0-201-60921-5.
Forsyth, D. & Ponce, J. (2002). Computer Vision: a modern approach, Prentice Hall.
Freeman, E. & Freeman, E. (2005). Head First Design Pat-
terns, O'Reilly and Associates. ISBN: 0596007124; see URL:
http://www.wickedlysmart.com/HeadFirst/HeadFirstDesignPatterns/.
Fries, B. (2005). Digital Audio Essentials: A Comprehensive Guide to Creating, Recording, Editing,
and Sharing Music and Other Audio, O'Reilly.
Gonick, L. & Hu man, A. (1990). The Cartoon Guide to Physics, Harper Perennial.
Gonzalez, R. & Woods, R. (2002). Digital Image Processing, 2nd edn, Prentice Hall.
Harris, A. (2007). Games Programming, The L Line, The Express Line to Learning, Wiley. ISBN:
978-0-470-06822-9.
Hartley, R. & Zisserman, A. (2004). Multiple View Geometry in Computer Vision, 2nd edn,
Cambridge University Press.
Hatcher, E. & Loughran, S. (2003). Java Development with Ant, Manning.
Hill(Jr), F. (2001). Computer Graphics Using OpenGL, 2nd edn, Prentice Hall.
Horstmann, C. (2005). Java Concepts, 4th edn, Wiley.
Horstmann, C. S. & Cornell, G. (2004a). Core Java 2 Volume 1, 7th edn, Prentice Hall.

1{2
Horstmann, C. S. & Cornell, G. (2004b). Core Java 2 Volume 2, 7th edn, Prentice Hall.
Koenig, A. & Moo, B. (2000). Accelerated C++, Addison-Wesley.
LaMothe, A. (2002). Tricks of the Windows Game Programming Gurus: Fundamentals of 2D and
3D Game Programming, 2nd edn, SAMS.
LaMothe, A. (2003). Tricks of the 3D Game Programming Gurus: Advanced 3D Graphics and
Rasterization, SAMS.
Lengyel, E. (2004). Mathematics for 3d Game Programming and Computer Graphics, 2nd edn,
Charles River Media.
Llopis, N. (2003). C++ for Game Programmers, Charles River Media.
McSha rey, M. (2005). Game Programming Complete, 2nd edn, Paraglyph Press.
Millington, I. (2007). Game Physics Engine Development, Morgan Kaufmann. ISBN: 0-12-369471-
X.
Murdock, K. (2004). 3ds Max 6 Bible, Wiley.
Norman, D. (1993). Things that make us smart, Perseus Books.
Norman, D. (1998). The Invisible Computer, MIT Press.
O'Luanaigh, P. (2006). Game Design Complete, Paraglyph Press. ISBN: 1-93309700-0.
Palmer, G. (2005). Physics for Games Programmers, Apress. ISBN: 1-59059-472-X.
Parker, J. (2005). Start Your Engines: developing driving and racing games, Paraglyph Press.
Penton, R. (2003). Data Structures for Games Programmers, Premier Press / Thompson Course
Technology. ISBN: 1-931841-94-2.
Pohlmann, K. C. (2000). Principles of Digital Audio, 4th edn, McGraw-Hill.
Publications, S. T. (1996). Read Me First!: A Style Guide for the Computer Industry, Prentice
hall.
R. Boulanger, e. (2000). The Csound Book, MIT Press.
R. Feynman, R. L. & Sands, M. (1965a). The Feynman Lectures on Physics, vol. 1, Addison-
Wesley.
R. Feynman, R. L. & Sands, M. (1965b). The Feynman Lectures on Physics, vol. 2, Addison-
Wesley.
R. Feynman, R. L. & Sands, M. (1965c). The Feynman Lectures on Physics, vol. 3, Addison-
Wesley.
Roads, C. (1996). The Computer Music Tutorial, MIT Press.
Rollings, A. & Morris, D. (2004). Game Architecture and Design, 2nd edn, New Riders Publishing
(Pearson Education). ISBN: 1-7357-1363-4.
Rost, R. J. (2004). OpenGL Shading Language, Addison Wesley.

1{3
Sanger, G. (2003). The Fat Man on Game Audio: Tasty Morsels of Sonic Goodness, New Riders
Publishing (Pearson Education).
Schneider, P. J. & Eberly, D. H. (2003). Geometric Tools for Computer Graphics, Morgan Kauf-
mann.
Selman, D. (2002). Java 3D Programming, Manning.
Shreiner, D., Woo, M., Neider, J. & Davis, T. (2006). OpenGL Programming Guide: The
Ocial Guide to Learning OpenGL, Version 2 (The Red Book), 5th edn, Addison Wes-
ley. ISBN: 0-321-33573-2. Note that there is a very usable web version of Edition 1.1 at:
http://www.glprogramming.com/red/.
Sierra, K. & Bates, B. (2005). Head First Java, O'Reilly and Associates. ISBN: 0596009208.
Steiglitz, K. (1996). A Digital Signal Processing Primer - with applications to digital audio and
computer music, Addison-Wesley.
Tufte, E. (1990). Envisioning Information, Graphics Press.
Tufte, E. (1997). Visual Explanations, Graphics Press.
vanVerth, J. M. & Bishop, L. M. (2004). Essential mathematics for games and interactive appli-
cations, Morgan Kaufmann. ISBN: 155860863X.
Vince, J. (2001). Essential Mathematics For Computer Graphics Fast, Springer-Verlag. ISBN:
1852333804.
Watt, A. (2000). 3D Computer graphics, 3rd edn, Addison-Wesley.
Watt, A. & Policarpo, F. (2001). 3D Games, Vol.1, Addison-Wesley.
Wright, R. & Lipchak, B. (2004). OpenGL Superbible, 3rd edn, SAMS. ISBN: 0-672-32601-9.
Xiang, Z. & Plastock, R. (2000). Computer Graphics (Schaum's Outlines), 2nd edn, McGraw-Hill.

1{4
Chapter 2

What is Computer Programming all about?

2.1 Introduction
This course is about programming of computer games. In fact it is more speci cally about video
game programming, i.e. computer games in which the player sees a video representation of the
game world. Other sorts of games will occasionally be mentioned.
Since most games, and de nitely video games, involve sound output, we will also cover programming
that involves audio data and its output.
So, we'd better say something about what is a computer program and something about what we
would expect a computer game program to do. And before that we'd better build up a simple
model of a computer and then say something about the operating system.

2.2 Simple Model of a Computer


A very simple model of a computer is shown in Figure 2.1. At its simplest, the Central Processing
Unit (CPU) is a glori ed calculator, the memory stores the results of calculations | it is a set
of labelled cells | the label is called the address, each cell contains a number | data. The
bus is simply a pipeline along which data and addresses can ow. (I'm sure you all have used a
calculator which has numbered memories | where you can type memory-store-3 and later retrieve
with memory-recall-3.
Both data and addresses are stored as numbers, though data numbers may be interpreted otherwise,
e.g. as text characters. As well as containing data, the memory also contains the program | in
the form of numeric instructions. Ultimately, everything is reduced to numbers.
What is a computer program? A program is a sequence of instructions for the computer to obey.
Take, for example, the Java instruction:

z = x + y; /* add variables x and y, result in z*/

This says: (i) take the number in the cell labelled x, (ii) take the number in the cell labelled y, (iii)
add them, (iv) put the result in the cell labelled z.

2{1
Figure 2.1: Overview of a Computer: CPU and Memory

In a programming language, cells are called variables, variable names are easy to remember names
for memory cells: e.g. x at memory cell 0, y at 1, z at 2; therefore in Figure 2.1 the value in x is
22, in y, 33, and in z, 0.

P1. Read the contents of memory cell corresponding to x.


P2. Read the contents of memory cell corresponding to y.
P3. Do the addition,
P4. Write the result in the memory cell corresponding to z.

Four or ve machine instructions per high-level language instruction is typical.


Where do the machine instructions P1 . . . P4 come from?
The answer to these is that the machine instructions are just another form of data, and the
controller in the CPU must fetch them from memory, and execute them; thus, there are two sorts
of data owing along the bus - actual numeric values, and machine instructions; the controller is
in a continuous cycle:

 Fetch P1.
 Execute P1.
 Fetch P2 . . . Execute P2.
 . . . and keep on until it reaches a halt instruction.

This is the famous fetch-execute cycle that you'll hear a lot about in the Computer Architecture
module.
A CPU and memory aren't much use without a keyboard and screen, and a disk drive, i.e. a useful
computer is actually a computer system.
Figure 2.2 shows the organisation of a simple computer system with one CPU, memory and three
input-output (I/O) devices { peripherals: a disk and a keyboard & screen. As always, the compo-
nents are connected by a system bus { this is the same as the bus mentioned above; communication
with peripherals can be done in a manner similar to memory reading/writing.

2{2
Figure 2.2: Organisation of a Simple Computer System

Figure 2.3 shows main memory; it is essentially a collection of labelled cells (labels = addresses),
each of which, typically, can store eight binary digits | bits (BInary digiTs). Each bit is like a
sub-cell that can be `0' or `1'. Bits have much in common with light-bulbs: they are either on |
value 1, or o | value 0.
A collection of eight bits is called a byte.
Each cell has an address. Addresses run from 0 to N-1, where N is the size of the memory; on a
typical PC these days, N is 64 Mega(bytes). Mega = `million' | actually, 1048576 = 220 .

Figure 2.3: Computer Memory, bytes labelled 0 to N - 1

Numeric values Each bit is a single binary value: you can count only from 0 to 1 with it; however,
a collection of eight bits, properly interpreted, allows you to count from 0 up to 255; and 16 bits, 0
. . . 65535 (64K - 1). The byte shown in address 0 of Figure 2.3 contains binary 0001 0110 which
is decimal (base-10) 22, hexadecimal (base-16) 0x16.
Essentially, Figure 2.2 conforms to the so-called von Neumann computer architecture, which has
ve basic parts:

1. The arithmetic-logic unit (ALU).


2. The control unit. The ALU and the control unit comprise the CPU.

2{3
3. The memory.
4. Input equipment.
5. Output equipment.

2.3 Computer Programs


As we have said earlier, a computer program is a list of instructions for the computer. There are
other ways of viewing programs, especially when programming in a high-level and object-oriented
language like Java. But if we want to consider the native language of the machine (numerically
coded machine language), this is de nitely true.
As list of instructions a computer programs is rather like a cookery recipes, see Figure 2.4 below.

2{4
Figure 2.4: Computer programs have much in common with recipes (recipes from B. Nilsson, The
Penguin Cookery Book)

2{5
One signi cant di erence between instructions for computers and instructions for cooks is the
computer instructions must be absolutely precise and correct | to the nest detail; computers
know nothing other than what is in their programs, whereas humans can often ll in gaps and
correct obvious errors.
So, if had a (very dull and uninteresting) computer program which added numbers, we could write a
pseudo-code program as follows. (Pseudo-code is English-like instructions that are easily translated
to computer readable instructions.)

1. Initialise;
2. Prompt user to type two numbers;
3. Read in the two numbers and store them in memory, one in a cell called x, the other in y;
4. Add x and y and store the result in z; (as we have done before);
5. Write the value in z to the screen;
6. Go to instruction 2 (and repeat).

Apart from pseudo-code, another way of writing an outline of a program, that is without going
into the detail of the exact program, is a ow chart, see Figure 2.5. Here the blocks indicate
do-something and the arrows show where to go to next. I mention ow charts not because they
are all that much use these days for general programming, but because they do occasionally crop
up in games programming books.

Figure 2.5: Flowchart expression of a program.


We are not going to bother with the details of how to read keyboards and how to write to screens.
In fact, they are one of the tasks that a computer program can ask the operating system to do for
it, see section 2.7.

2{6
This is all dreadfully dull; but we are not as far from a game program as you might think.
Let us take a glimpse at two types programming language: a high level language like Java; and a
low level language that the computer itself uses.

2.4 Assembly Code versus Java


We write programs for a mythical computer called the Mac-1A. The instruction set is given in
Figure 2.6. We can write in binary machine code, using the left hand side of Figure 2.6; but, that
is even more tedious and error prone than what we are about to do | because of the distraction of
having to translate into a numeric code. It is better to use the so called mnemonics. The assembly
code can then be assembled to machine code; this is done using a program called an assembler
program.
----------------------------------------------------------
Binary Mnemonic Name Action(s)
------ -------- ---- ---------
----------------------------------------------------------
0000XXXXXXXXXXXX LODD Load Direct AC<-M[X]
0001XXXXXXXXXXXX STOD Store Direct M[X]<-AC
0010XXXXXXXXXXXX ADDD Add Direct AC<-AC+M[X]
0011XXXXXXXXXXXX SUBD Subtract Direct AC<-AC-M[X]
0100XXXXXXXXXXXX JPOS Jump on pos. IF AC>= 0 : PC<-X
0101XXXXXXXXXXXX JZER Jump on zero IF AC= 0 : PC<-X
0110XXXXXXXXXXXX JUMP Jump uncond. PC <- X
0111CCCCCCCCCCCC LOCO Load constant AC <- C
----------------------------------------------------------

Figure 2.6: Mac-1A Instruction Set


Assembly language is a language in which there is one-to-one relationship between symbolic instruc-
tions and machine instructions; we will come across pseudo-instructions that are really instructions
to the assembler program, but these will be obvious.

2.4.1 Simple Program { add two integers


Program. Add the integer in a1 to the integer in a0 and put the result in a2, i.e.
a2 = a0 + a1;
In assembly code:

Start: LODD a0 /get a0 into accum.


ADDD a1 /accum. gets a0+a1
STOD a2 /store result in a2

There are four elds in an assembly instruction:

2{7
Label. Optional, only necessary if you need to jump to that instruction; can also be useful as
comment, e.g. above. Always followed by ':', otherwise assembler will try to interpret it as
an instruction.
Instruction mnemonic.
Operand I.e. address of thing to su er the operation. Either:
 Symbolic name, e.g. above.
 Symbolic label, or numeric address, to jump to.
 Constant { for immediate addressing.
Comment Comments are even more important for assembly language programs than the are for
high-level language programs. Make your code maintainable by someone else!

2.4.2 If-then
High-level language

if(a0==a1) a2 = a2 + 1;
else next instructions...

Assembly language

If: LODD a0
SUBD a1
JZER Then
JUMP Next
Then: LOCO 1 /load constant 1
ADDD a2
STOD a2
EndThen: JUMP Next /Unnecessary
Next: ...

2.4.3 If-then-else
High-level language

if(a0==a1) a2 = a2 + 1;
else a2 = a2 + 2;
//next instructions...

2{8
Assembly language

If: LODD a0
SUBD a1
JZER Then
Else: LOCO 2
ADDD a2
STOD a2
JUMP Next
Then: LOCO 1
ADDD a2
STOD a2
EndThen: JUMP Next /Unnecessary
Next: ...

2.4.4 Repetition
High-level language

//sum the first n-1 integers


int sum=0;
int i=0;
while(i < n){
sum= sum + i;
i++;
}

Assembly Language

init: LOCO 0
STOD sum
STOD i
loop: LODD i
SUBD n
JPOS loopend / if i-n>=0,
/ same as: if i>=n
/ same as: if NOT i<n
/otherwise continue with body of loop
loopbody:
LODD sum /load sum into accumulator
ADDD i /add i to it
STOD sum / and don't forget to store the result!
incr: LOCO 1
ADDD i
STOD i
JUMP loop
loopend: ...

2{9
Exercise. Draw a owchart representing the program above.
It is straightforward to de ne deterministic loops, for example,

for(i = 0; i<n; i++){


sum= sum + i;
}

in terms of do-while. Likewise do-until { the continuation test is merely carried out at the end
of the loop, instead of at the beginning for do-while.

2.5 Back to the calculator program


In order to lead nicely in to the next part, section 2.6, please note that our little calculator program
(section 2.3) has a program part: (1. to 6.) and a data part: data (x, y and z).
Now I want you to imagine a slightly modi ed version of this little computer program executing in
a little calculator-like machine. There is an input button; when you press that the little calculator
displays a message "Give me the two numbers"; you type the two numbers; the little calculator
reads them and stores them internally. There is a compute button; when you press that the
calculator computes the sum of whatever it has in x and y and stores the result internally in z.
There is a display button. When that is pressed whatever is stored in z is displayed.
The little program is now split into a program part that has separate parts: (input, compute, and
display) and a data part. In fact, it is an object | in the sense of object-oriented. Since the little
calculator still has a keyboard, memory, a CPU and a display, deep down nothing has changed. But
our way of thinking about the program (as a machine that performs tasks and has some memory)
is a lot closer humanly understandable that the program 1. . . . 6., especially when all the detail is
added.

2.6 Object-oriented Programming


In object-oriented programming, we organise our program into objects.
The point about object-oriented programming is that when a program gets very large, as in, say,
a program that handles the registration of students and storing of their records during their time
in the college.
If we can organise the program into objects, for example: Student, ClassList, etc. the whole thing
becomes easier to work on. For a start, you can allocate someone the task of writing the Student
object, and they don't have to worry about other detail.
The objects are quite separate and perform quite speci c tasks and store quite speci c data (e.g.
related only to Student). Separate the objects may be, but they they cannot be totally isolated.
ClassList will need to include Student objects.

2{10
Information is passed between objects via messages | at least that is what the information passing
is called in object-oriented programming.
In fact, message passing boils down to nothing more than an object X pressing certain buttons on
an object Y and/or passing data to object Y.
That's probably confusing you. Think of it as follows.
Instead of a computer, let us have a room with two administrative sta in it. For each new student
who comes to the top of a queue, clerk A takes out a blank Student form like Figure 2.7, gets the
details from the student and adds them to the form. The clerk has done the equivalent of creating
a Student object | the administrative system's model of a student. The example here is grossly
simpli ed, for obvious reasons.
The point is that by organising our programs into objects the programs become easier for program-
mers to think about; it is easier to split the programming task amongst programmers; programs
are easier to modify and it is easier to x errors in them. But not changes inside the computer,
our object-oriented programs are still translated into the same bunches of instructions and data as
we saw in section 2.5
Student Record Form

Last Name:_______________________________________________

First Name:______________________________________________

Student Id.:___________________ Credits:_______

Figure 2.7: Student record form

Clerk A now passes the completed form to clerk B, who places it into a box- le. The box- le is a
ClassList object.

2.6.1 Java versions of Student and ClassList


Now we show Java versions of Student and ClassList and a program which uses them. These are
real working programs, and you'll get a chance to work with them; but note that I've chopped out
a lot of details in order to get the programs on a page. Please note that at this stage you do not
need to understand these programs, just get the gist.

2{11
import java.util.*;
public class Student{
private String firstName;
private String lastName;
private String id;
private int credits;

/**
* Create a new student with a given name and ID number.
*/
public Student(String lastN, String firstN, String studentId){
lastName = lastN; firstName = firstN;
id = studentId;
credits = 0;
}

public Student(String str){


StringTokenizer tok = new StringTokenizer(str, " ,");
lastName = tok.nextToken(); firstName = tok.nextToken();
id = tok.nextToken();
}

public void changeName(String newLast, String newFirst) {


lastName = newLast; firstName = newFirst;
}

public String getStudentId() {


return id;
}

/**
* Add some credit points to the student's accumulated credits.
*/
public void addCredits(int newCredPoints){
credits = credits + newCredPoints;
}

public void print() {


System.out.println(this.toString());
}

public String toString(){


return lastName + ", " + firstName + ", " + id;
}
}

Figure 2.8: Java version of Student

2{12
import java.io.*; import java.util.*;
public class ClassList{
private List<Student> theList = new ArrayList<Student>();
/**
* Create a new empty class list
*/
public ClassList(){
theList = new ArrayList<Student>();
}

public void addStudent(Student s){


theList.add(s);
}

public int size(){


return theList.size();
}

public void print(){


System.out.println("The class size is: " + theList.size());
System.out.println("The class list is:");
for(Student s : theList){
System.out.println(s);
}
}

public void saveToFile(String filename) throws java.io.IOException {


PrintWriter pout = new PrintWriter(new BufferedWriter(
new FileWriter(filename) ) );
for(Student s : theList){
pout.println(s);
}
pout.println("end"); pout.close();
}

public void readFromFile(String filename) throws java.io.IOException {


BufferedReader in = new BufferedReader(new FileReader(filename));
String line; boolean finished = false;

while(!finished){
line= in.readLine();
if(line.equalsIgnoreCase("end") ){
finished= true; break;
}
Student s = new Student(line); addStudent(s);
}
in.close();
}
}

Figure 2.9: Java2{13


version of ClassList
import java.io.*; import java.util.*;

public class ClassListT1


{
public ClassListT1() throws java.io.IOException
{
String lastns[]= {"Bloggs", "Ahern", "Coughlan", "Carr","end"};
String firstns[]= {"Joe", "Bertie", "Mary", "Maeve"};
int currentId= 2005000001;
String last, first, id;
Student s;
boolean finished = false;
ClassList cl = new ClassList();

int i = 0;
while(!finished){
last= lastns[i];
if(last.equalsIgnoreCase("end") ){
finished= true;
break;
}
first= firstns[i];
id= "" + currentId;
currentId++;

s= new Student(last, first, id);


s.print();
cl.addStudent(s);
i++;
}
cl.print();

System.out.println("Saving to file ...");


cl.saveToFile("classlist.txt");

System.out.println("Reading from file ...");

ClassList cl2 = new ClassList();


cl2.readFromFile("classlist.txt");
cl2.print();

System.exit(0);
}

public static void main(String[] args) throws java.io.IOException


{
new ClassListT1();
}
}

Figure 2.10: Java program which


2{14 uses ClassList and Student
If you want to seem some game classes (e.g. Sprite, Player), jump ahead to Chapter 11.1.
Finally, before we get to games, we need to discuss operating systems.

2.7 Operating Systems


These days, if you are buying a computer system, operating system software is just as important
as the hardware.

An Operating System is a Program An operating system is a program. When you switch on a


computer (a computer system), the hardware ensures that the operating system is read into the
memory (a process called booting | bootstrapping) and that control is transferred to the OS (i.e.
the OS starts executing).

The Operating System Executes all the Time | well, sort of I am tempted to say that, from
then on, the operating system (program) runs all the time until the the computer is switched o .
However, we all know that only one program can be running on a single processor at any time; and
most computers operate with just one processor. What about the applications we want to run |
our word processor, or spreadsheet, our games . . . See Chapter 5.

The Operating System is in charge of the computer We can revise that statement to say that
the operating system is in charge of the computer all of the time. To show how a simple program
(the operating system) can be in charge, or manages, the computer, yet relinquish the processor
to other programs, is one of the major objectives of this course. Note: I use the term in charge
because I want to reserve the term control; when we say transfer of control (to a program), we
mean that the instructions of this program begin to execute on the processor { and the operating
system (and other programs), for the moment, slide into the background.

The Operating System as a platform for executing software Another useful way of thinking
about the operating system is that it provides a platform or environment (a virtual machine) upon
which to run your application software and provides an interface with which you the user interact
with the machine. In this view, the operating system provides a layer that separates the application
software and the user from the hardware.

The Operating System provides services When an operating system is viewed as a platform
for executing software (and for interfacing with humans), we can see that it provides services. For
example: reading from, and saving to disks; sending data to a printer, etc.

2{15
Layers in the Operating System Figure 2.11 shows a layered view of an operating system and
the things it inter-operates with. The layers are:

Figure 2.11: Layers in an Operating System.

1. (not really a layer) The user, who interacts with . . .


2. the shell or an application program;
3. the kernel | as the name suggests, the engine room of the OS;
4. device drivers | software for talking to speci c hardware devices;
5. nally, the hardware.

In this diagram, arrows mean ows of data and the blocks represent layers. Note carefully the
di erent meanings compared to ow charts.
This diagram is an important one for games programmers. Previously, games programmers liked
to write their programs (application programs) to talk directly to the hardware, i.e. bypassing the
OS and using the programmers' own drivers. Modern operating systems don't allow this | for
many good reasons.
Q. Why might a games programmer want to bypass the OS | especially if it meant duplicating
services/software already provided by the OS? Hint: video games . . . speed.
Q. Why is it a good idea to keep device drivers in as low a layer as possible? Think of graphics cards
(or network interface cars for that matter). And then think of games (application programs). Would
it be a good idea for an application (program) to have to contain a driver for every conceivable
graphics card (or network card)? Or would it be nicer if the application could just ask the OS to

2{16
display that image, or play that sound, for it? If applications were to supply the drivers, think of
how many di erent applications would need to contain a particular driver? Duplication is bad; and
triplication worse! And multiplication even worse than that | especially if you have no calculator
:-)

The Operating System as police or government When modern operating systems (Windows
2000, XP, UNIX, Linux) go to the trouble of providing services they also force application programs
to avail of these services. For example, unlike on MS-DOS and early versions of Windows (3.1
and to an extent 95 and 98), application programs are barred from directly accessing hardware like
disks, network cards, etc.; they must use the appropriate operating system service. Since accessing
hardware directly is dicult and some application programs are written by programmers who are
less skilled than system programmers, directly accessing hardware was a common source of errors
and frequently resulted in the systems crashing.
Operating systems that adopt this more modern approach are therefore more reliable | there
are fewer occurrences of the blue screen of death. (The blue screen of death refers to the blue
screen that used to appear on early versions of Windows when the OS crashed and needed to be
rebooted.)
In this sense, the operating system can be seen as a police force or government. And just like
governments require you to pay taxes (for the services they claim to provide) the operating system
demands a certain amount of CPU time for itself.

2{17
Chapter 3

Overview of Games Programming

3.1 Introduction
Before, see section 2.5, we talked about a very boring little program that read two numbers, added
them, and displayed the result. We showed a ow chart of it, and we hinted that we were not too
far away in structure from a games program.

3.2 Noughts and Crosses


Think about the game noughts and crosses (Tic-tac-toe for our American cousins); Figure 3.1
shows the rst two steps in the progress of a game and, for automation, a cell numbering scheme.
Cell numbering scheme

| | o | | 0 |1 |2
--+--+-- --+--+-- --+--+--
|x | |x | 3 |4 |5
--+--+-- --+--+-- --+--+--
| | | | 6 |7 |8

(a) (b) (c)

Figure 3.1: Noughts and crosses


Now you could imagine a program which records the game (between two human players) as follows:

1. Initialise | create variables, initialise them to start state;


2. Prompt user for a cell number and x or o;
3a. Read number (n) and character (c);
3b. Check n valid and available;

3{1
3c. Check play in proper turn: c = x, o, x, o . . .
3d. If something wrong, go back to 2;
4a. Store c in cell n
4b. Check for: (i) a win, (ii) the cells are all occupied and hence the game is a draw; otherwise
(iii) continue;
5. Display the current state of the game;
6. Unless game ended, go to instruction 2 (and repeat).

This is shown in a owchart, Figure 3.2.

Figure 3.2: Flowchart of a noughts-and-crosses game.


If the involvement of a computer program here bothers you, in class I can revise the game such
that I'm the computer and I'm displaying things on the board. (I'm aware that we are only a week
or two into the course and that some people may still have done very little programming.)

3{2
It doesn't require much modi cation to make this program act also as one of the players | all you
need is the addition of a step 3e. which makes the computer's move. Incidentally, this step would
use what is called game AI | game Arti cial Intelligence. Hardly worthy of the term intelligence
but such algorithms are sometimes digni ed with the name AI.
This is still boring, but we have identi ed many of the components and characteristics of computer
games. We have identi ed: (i) user input; (ii) internal representation; (iii) handling of user input,
including rules of the game; (iv) display. Another comment worth making is that the program
above is expressed in a procedural or imperative manner, i.e. as opposed to object-oriented.

3.3 More on object-oriented programming


One way to make our noughts-and-crosses program object-oriented is to use the Model-View-
Controller (MVC) design pattern; for a nice explanation of MVC, see the Head First Design
Pattern book (Freeman & Freeman 2005), which explains MVC using the example of an MP3
player. There is even a song http://home.in.tum.de/~pittenau/ModelViewController.mp3.
While you are at it, check out the rather nice Head First Java book, (Sierra & Bates 2005).
In the object-oriented design of the MP3 player, we have, roughly speaking:

1. Model, which contains the programs to play MP3s, etc.


2. View, which corresponds to the display and the user-interface (buttons etc.);
3. Controller, which takes user input from the View and tells the Model what to do.

3.4 Back to games


You'll probably threaten to leave the class if I don't get onto something more interesting than
noughts-and-crosses.
The program shown in Figure 3.2 is alright for any sedate game like noughts-and-crosses in which
players take turns and generally operate in a predictable manner. But in a video game, players
certainly do not act in a predictable manner; a video game would not work if we imposed a loop
like Figure 3.2; imagine playing a rst-person-shooter game in which the computer prompted you
for each move: computer prompts, you respond "turn left", computer updates character and
scene; computer prompts, you respond "move forward", computer updates character and scene;
computer prompts, you respond "shoot", computer updates character and scene, . . . .
That simply doesn't work with unpredictable input: rapid sequences of mouse movements and
button clicks; or even rapid sequences of keyboard key presses. Incidentally, if you think about it,
word processors or any application with a graphic user interface (GUI) cannot use a loop like that
in Figure 3.2.
In Figure 3.2, the computer was able to run the show. In video games it cannot. The user calls
the shots.

3{3
Sounds like we need magic. No, what we need is event-driven programming. In event-driven
programming, the operating system collects all user input events | such as keyboard key presses,
mouse movements, mouse button presses and clicks, . . . . It places these in an event queue which
is readable by the application program. The application program must then (i) read the events
from the queue and (ii) for each event gure out what that even means, (iii) update the game
state accordingly, and, nally, display the updated game state.
Even-driven programming is how you must program Windows user interfaces.
The new game loop is shown in Figure 3.3, where we've shown also the display window and the
user input devices.

Figure 3.3: Event-driven game loop.


The loop is performed at the rate of the display update | say 30 times per sec.

3.5 Some Detail on Games Programming


Still bored? Yes, I know that you want me to get quickly to the lesson where you learn to write a
the next great 3D video game. That's later. We have to do some basic nuts-and-bolts stu rst.
In fact there are some very interesting nuts and bolts in Figure 3.3 if it is applied to a video game;
we'll stick to 2D | like the 2D platform game that we'll work on at the end of the year.
We have:

1. Initialise;
2. Read event(s) from queue;

3{4
3. Interpret events;
4. Respond to event(s) and update game state accordingly;
5. Display updated game state.

Initialise Initialise involves setting the state of the game to the beginning. For example, read
data from le; the hero is at the left of the screen facing right; enemies are far way, . . . This is
just initialising that internal representation. It is display at the bottom of the loop that paints the
picture corresponding to this initial state | or any later updated state.

Read event(s) from queue Not much to say. The OS and Java gives you a well de ned way of
retrieving events from the queue.

Interpret events Deep down in the platform game in chapter 5 of Brackeen's book (Brackeen
et al. 2004), you will nd:

inputManager.mapToKey(moveLeft, KeyEvent.VK_LEFT);
inputManager.mapToKey(moveRight, KeyEvent.VK_RIGHT);
inputManager.mapToKey(jump, KeyEvent.VK_SPACE);
inputManager.mapToKey(exit, KeyEvent.VK_ESCAPE);

Ignoring detail, this means that there are just four possible events (I'm mixing up event with
response to event, but you see what I mean): (i) moveLeft; (ii) moveRight; (iii) jump; and (iv) exit.
VK_LEFT, VK_RIGHT, refer to presses of cursor keys, VK_SPACE is the space bar, and VK_ESCAPE
is the escape key up on the top left hand corner of the keyboard; escape is a reasonably standard
method of indicating exit/quit.

Respond to event(s) and update game state accordingly For example, if Hero is at (x = 25,
y = 0) and there has been a moveLeft event, we move him (her, it?) to (x = 24, y = 0); we set
the direction state to left so that he faces left; jump and we move him to (x = 24, y = 3); set
the jumping state to true | a jump action lasts a few display frames ( (x = 24, y = 3), (x = 24,
y = 6), (x = 24, y = 7), (x = 24, y = 8) and then falling down again, (x = 24, y = 7), (x = 24,
y = 5), . . . ).
But that's when the game is at a simple stage. When there are enemies around, or power-ups that
can be collected, we must perform collision detection | detect whether Hero has touched one of
those. How do you do that? Well, if we update the position of Hero and nd him at (x = 25, y =
7), we can inspect our list of enemies and see if any of them is at (x = 25, y = 7) or nearby, say,
at (x = 25+/- 2, y = 7+/-2).
If there is a collision, we must perform a collision response | and that depends on the type of
collision. It could be the Hero dying and the game ending, or the Hero increasing his health by
colliding with a power-up, etc.

3{5
Display updated game state Revise the display according to the current state.
In a noughts and crosses game it is just a matter of drawing something like Figure 3.1 (a) or (b)
on the screen.
If it is a 2D tile-based game, we repaint some parts of the picture and maybe scroll left or right
or up or down. (Note that we have not yet said anything about how to paint pictures; that is
deliberate, wait for chapter 6.)
If it is a 3D game, we might have to take the updated version of a complex 3D model and use
3D graphics techniques to determine what the viewer sees. In e ect we operate a pretend camera
and see what will be projected from the 3D scene onto the 2D camera view. Then the 2D camera
view is painted on the screen.

Sound You could say that sound is part of the display | in the sense that display is how
the player gets feedback on the progress of the game. Many games have a background musical
accompaniment. Most will output a sound to correspond to a state of the game, for example a
bang when there is a collision.
I hope that gives you some hints at what is ahead | in Games Programming 1, and later in
year 2: Graphics and Physical Modelling and Games Programming 2, and then year 3: Games
Programming 3.

3.6 The Programming Language | Java


Why Java? First, because you are learning Java in Software Design 1; we could not burden you
with more than one language. Second, Java is the best language by which to learn programming
| bar none. If you like I'll show you C or C++ versions of some of the stu we are doing |
will make the hair stand on the back of your neck. Third, Brackeen's book. We move to C++ in
second year.

3{6
Chapter 4

Simple Procedural Programming

This chapter has three main objective. First, to cause us to think a bit about procedural program-
ming and how you can analyse a problem and design a program to solve it. Second, it allows us
to do a bit of revision on Java programming. Third, we explore the ve program structures that
programmers use.
Although our Java programs will always be wrapped up in classes/objects (object-oriented), the
programs within the classes (methods | also variously called subprograms, procedures, functions)
are step-like, like recipes. This type of programming is called procedural programming.
In the sequence of programs that follow, we will explore the ve program structures that arise in
procedural programming. The ve are:

 Sequence;
 Selection;
 Repetition;
 Procedures (methods, subprograms, functions);
 Procedures with parameters.

How are computer programs and algorithms designed? They do not spring fully formed from your
ngers!
Ex. 1. Write a program to display the following.

* *
* *
* *
* *
**

Ex. 2. Now make it general enough to take a parameter height.


We'll build up to it; start with one *.

4{1
//----- OneStar.java ---------------------------------
// j.g.c. 2/3/99, 3/3/99, 2007-12-18
// prints single '*' on screen
//--------------------------------------------------
import java.io.*;

class OneStar{
public OneStar(){
PrintStream o= System.out; /* to shorten System.out.println
statements */
o.println(getClass().getName()+"\n"); /* always useful to display
the name of the program */

// the program
o.print('*');
o.print('\n');
}

public static void main(String args[]){


new OneStar();
}
}

4{2
4.1 Sequence
Now, print: *****

//----- FiveStars.java ---------------------------------


// j.g.c. 2/3/99, 3/3/99, 2007-12-18
// prints five '*' line on screen
//--------------------------------------------------
import java.io.*;

class FiveStars{
public FiveStars(){
PrintStream o= System.out;
o.println(getClass().getName()+"\n");

o.print('*');
o.print('*');
o.print('*');
o.print('*');
o.print('*');

o.print('\n');
}
public static void main(String args[]){
new FiveStars();
}
}

//----- FiveStars2.java ---------------------------------


// j.g.c. 2/3/99, 3/3/99, 2007-12-18
// prints five '*' line on screen
// format now clearly shows parallel between data structure and program
// structure
//--------------------------------------------------
import java.io.*;

class FiveStars2{
public FiveStars2(){
PrintStream o= System.out;
o.println(getClass().getName()+"\n");

o.print('*'); o.print('*'); o.print('*'); o.print('*'); o.print('*');

o.print('\n');
}
public static void main(String args[]){
new FiveStars2();
}
}

4{3
4.2 Repetition
//----- RepStars.java ---------------------------------
// j.g.c. 2/3/99, 3/3/99, 2007-12-18
// prints *s line on screen using repetition
//--------------------------------------------------

class RepStars{
public RepStars(int n){
width = n;
PrintStream o= System.out;
o.println(getClass().getName()+"\n");

for(int i= 0; i< width; i++){


o.print('*');
}
o.print('\n');
}

public static void main(String args[]){


new RepStars(5);
}

private int width;


}

4.2.1 Sequence of Repetitions


//----- Tr1.java ---------------------------------
// j.g.c. 2/3/99, 5/3/99, 2007-12-18
// prints a backwards facing triangle
//*
//**
//***
//****
//*****
// Analysis:
// Stars
// Line 0: 1
// 1: 2
// 2: 3 etc.
// ---------------------
// and sequence
//--------------------------------------------------
import java.io.*;

class Triangle1{

4{4
public Triangle1(){
PrintStream o= System.out;
o.println(getClass().getName()+"\n");

int nStars= 1;
for(int i= 0; i< nStars; i++){
o.print('*');
}
o.print('\n');

nStars= 2;
for(int i= 0; i< nStars; i++){
o.print('*');
}
o.print('\n');

nStars= 3;
for(int i= 0; i< nStars; i++){
o.print('*');
}
o.print('\n');

nStars= 4;
for(int i= 0; i< nStars; i++){
o.print('*');
}
o.print('\n');

nStars= 5;
for(int i= 0; i< nStars; i++){
o.print('*');
}
o.print('\n');
}
public static void main(String args[]){
new Triangle1();
}
}

4{5
4.2.2 Repetitions of Repetitions | Nested Loop
We can improve a lot on Triangle1.java.

//----- Triangle1Rep.java ---------------------------------


// j.g.c. 2/3/99, 5/3/99, 2007-12-18
// prints
//*
//**
//***
//****
//*****
// using repetition
// Analysis:
// Stars
// Line 1: 1
// 2: 2
// 3: 3 etc.
// ---------------------
// i.e. generalising:
// Line j j
// and sequence
//--------------------------------------------------
import java.io.*;

class Triangle1Rep{
public Triangle1Rep(int h){
height = h;
PrintStream o= System.out;
o.println(getClass().getName()+"\n");

for(int j= 1; j<= height; j++){


//---- this does the j stars -------------
for(int i= 1; i<=j; i++){
o.print('*');
}
//--------------------------------------
o.print('\n');
}
}
public static void main(String args[]){
new Triangle1Rep(5);
}

private int height;


}

4{6
4.2.3 Data Pattern = Program Pattern
//----- Triangle2.java ---------------------------------
// j.g.c. 2/3/99, 3/3/99, 2007-12-18
// prints filled triangle of '*' on screen
// can you see the program has the same pattern as the data
//--------------------------------------------------
import java.io.*;

class Triangle2{
public Triangle2(){
PrintStream o= System.out;
o.println(getClass().getName()+"\n");

o.print('*');
o.print('\n');
o.print('*'); o.print('*');
o.print('\n');
o.print('*'); o.print('*'); o.print('*');
o.print('\n');
o.print('*'); o.print('*'); o.print('*'); o.print('*');
o.print('\n');
o.print('*'); o.print('*'); o.print('*'); o.print('*'); o.print('*');
o.print('\n');
}
public static void main(String args[]){
new Triangle2();
}
}

4{7
4.3 Procedures | Methods, Subprograms, Functions
4.3.1 Procedures Without Parameters
//----- Triangle2Proc.java ---------------------------------
// j.g.c. 2/3/99, 3/3/99, 2007-12-18
// prints filled triangle of '*' on screen
// using procedures / methods
// here we take the sequences of instructions in
// Triangle2.java and pack them into methods/procedures
//--------------------------------------------------
class Triangle2Proc{
public Triangle2Proc(){
o= System.out;
o.println(getClass().getName()+"\n");

p1(); nl();
p2(); nl();
p3(); nl();
p4(); nl();
p5(); nl();
}

void p1(){
o.print('*');
}
void p2(){
o.print('*'); o.print('*');
}
void p3(){
o.print('*'); o.print('*'); o.print('*');
}
// etc. for p4 and p5

void nl(){
o.print('\n');
}

public static void main(String args[]){


new Triangle2Proc();
}

private PrintStream o;
}

Ex. What criteria should be taken into account when thinking of decomposing a program into
subprograms (procedures / methods)?

4{8
4.3.2 Subprograms with Parameters
//----- Triangle3Proc.java ---------------------------------
// j.g.c. 2/3/99, 3/3/99, 2007-12-18
// bsc1
// see Triangle1Rep.java for basis using repetition
// prints
//*
//**
//***
//****
//*****
// using repetition and procedures/methods
// Analysis:
// Stars
// Line 1: 1
// 2: 2
// 3: 3 etc.
// ---------------------
// i.e. generalising:
// Line j j
// and sequence
//--------------------------------------------------
import java.io.*;

class Triangle3Proc{
public Triangle3Proc(int h){
height = h;
o= System.out;
o.println(getClass().getName()+"\n");

for(int j= 1; j<= height; j++){


stars(j);
nl();
}
}

void stars(int n){


for(int i= 1; i<= n; i++){
o.print('*');
}
}

void spaces(int n){


for(int i= 1; i<= n; i++){
o.print(' ');
}
}

4{9
void nl(){
o.print('\n');
}

public static void main(String args[]){


new Triangle3Proc(5);
}

private PrintStream o;
private int height;
}

// exercise: change h (5) and see if it generalises okay.

Exercise: change h and see if it generalizes okay.

4{10
Equipped with these procedures, we are now in business, and can create nearly any pattern in very
quick time.

//----- Triangle4.java ---------------------------------


// j.g.c. 2/3/99, 3/3/99, 2007-12-18
// now print
//*****
//****
//***
//**
//*
// see Triangle3Proc
// Analysis:
// Stars
// Line (j) 1: 5
// 2: 4
// 3: 3 etc.
// ---------------------
// i.e. generalising: let 'h' be height
// Line j no. stars = h- (j -1)

// notice how little changes from Triangle3Proc -- just // *


//--------------------------------------------------
import java.io.*;

class Triangle4{
public Triangle4(int h){
height = h;
o= System.out;
o.println(getClass().getName()+"\n");

for(int j= 1; j<= height; j++){


stars(h - (j -1) ); // *
nl();
}
}

void stars(int n){


for(int i= 1; i<= n; i++){
o.print('*');
}
}

void spaces(int n){


for(int i= 1; i<= n; i++){
o.print(' ');
}
}

4{11
void nl(){
o.print('\n');
}

public static void main(String args[]){


new Triangle4(5);
}

private PrintStream o;
private int height;
}

Exercise: change h and see if it generalizes okay.

4{12
Now, getting more dicult . . .

//----- Triangle5.java ---------------------------------


// j.g.c. 5/3/99, 2007-12-18
// *
// **
// ***
// ****
//*****
// Analysis:
// Spaces Stars
// Line 1: 4 1
// 2: 3 2
// 3: 2 3 etc.
// ---------------------
// i.e. generalising
// Line j: height -1 - (j -1) j
// same as height - j j
//--------------------------------------------------

class Triangle5{
public Triangle5(int h){
height = h;
o= System.out;
o.println(getClass().getName()+"\n");

for(int j = 1; j<= height; j++){


spaces(height - j);
stars(j);
nl();
}
}
//void stars(int n), spaces, nl as before
public static void main(String args[]){
new Triangle5(5);
}

private PrintStream o;
private int height;
}

Ex. Based on Triangle5.java, analyse, design and implement a program to print:

*****
****
***
**
*

4{13
//----- Triangle10.java ---------------------------------
// j.g.c. 5/3/99, 2007-12-18
// from Triangle5.java
// Pattern Analysis
//
// Sp St
//************ 0 12
// ********** 1 10
// ******** 2 8
// ****** 3 6
// **** 4 4
// ** 5 2
// Let's generalise (height= h) & count from 0
// Spaces Stars
// Line 1: 0 h*2
// 2: 1 (h-1)*2
// 3: 2 (h-2)*2 etc.
// ---------------------
// i.e.
// Line j: j-1 (h- (j -1) )*2
//--------------------------------------------------
import java.io.*;

class Triangle10{
public Triangle10(int h){
height = h;
o= System.out;
o.println(getClass().getName()+"\n");

for(int j = 1; j<= height; j++){


spaces(j -1);
stars((h - (j -1))*2);
nl();
}
}

//void stars(int n), spaces, nl as before

public static void main(String args[]){


new Triangle10(6);
}

private PrintStream o;
private int height;
}

Ex. In Triangle10.java, change h and see if it works okay.

4{14
//----- Triangle20.java ---------------------------------
// j.g.c. 5/3/99, 2007-12-18
// from Triangle10.java
// Pattern Analysis
// Sp1 St Sp2 St
//
//* * 0 1 8 1
// * * 1 1 6 1
// * * 2 1 4 1
// * * 3 1 2 1
// ** 4 1 0 1

// And let's generalise (height= h) & count from 1


// Sp1 Sp2
// Line 1: 0 (h-1)*2
// 2: 1 (h-2)*2
// 3: 2 (h-3)*2 etc.
// ---------------------
// i.e.
// Line j: j - 1 (h-j)*2
//--------------------------------------------------
import java.io.*;

class Triangle20{
public Triangle20(int h){
height = h;
o= System.out;
o.println(getClass().getName()+"\n");

for(int j = 1; j<= h; j++){


spaces(j -1);
stars(1);
spaces((h - j)*2);
stars(1);
nl();
}
}

//void stars(int n), spaces, nl as before

public static void main(String args[]){


new Triangle20(6);
}

private PrintStream o;
private int height;
}

Ex. In Triangle20.java, change h and see if it works okay.

4{15
4.4 Selection
//----- Triangle10Sel.java ---------------------------------
// j.g.c. 5/3/99, 2007-12-18
// from Triangle10.java
// as Triangle10.java, but selecting stars, stripes
//************
// ----------
// ********
// ------
// ****
// --
//--------------------------------------------------
import java.io.*;

class Triangle10Sel{
public Triangle10Sel(int h){
height = h;
o= System.out;
o.println(getClass().getName()+"\n");

for(int j = 1; j<= height; j++){


spaces(j - 1);
if(j%2==0)dashes((h - (j -1))*2); // even
else stars((h - (j -1))*2); // odd
nl();
}
}

void stars(int n){


for(int i = 0; i< n; i++){
o.print('*');
}
}

void spaces(int n){


for(int i = 0; i< n; i++){
o.print(' ');
}
}

void nl(){
o.print('\n');
}

void dashes(int n){


for(int i = 0; i< n; i++){
o.print('-');
}

4{16
}

public static void main(String args[]){


new Triangle10Sel(6);
}

private PrintStream o;
private int height;
}

4.5 Exercises
1. Write a Java program to display an arbitrary lled rectangle of *s (stars) | speci ed by h(eight)
and w(idth);
2. Write a Java program to display a rectangle outline, 5  5, of *s.
3. Write a Java method, printHexDigit(int n) which will print a Hexadecimal digit | 0, 1,
. . . 9, 10 (A), . . . 15 (F) as follows. Design your own font for 3 onwards.

***** * ***** *****


* ** * * *
* * * * ***** *****
** * * * *
***** * ***** *****

4{17
Chapter 5

Threads

5.1 Introduction
In your programming so far, you will think of a program more or less as a sequence of instructions;
maybe the instructions are wrapped up in the constructor or other methods of an object, but they
are still instructions. When you execute the program, the computer steps through the program |
steps through the sequence of instructions. You may think that when you hit the run button, the
program starts at the beginning and runs right through to the end, without any interruptions.
The single uninterrupted ow of execution is not exactly true. What about that Internet Explorer
program; what about the game that you are playing when nobody is watching, an email client,
playing mp3 les, etc. All apparently executing at the same time | the computer is multitasking
or executing taks concurrently.
Today's operating systems software all allow multitasking. The computer can execute a great
many programs at once. A program executing is called a process.

Processes The multiple programs (tasks) you see running on your Windows machine are called
processes; at any time, apart from processes created by programs that you are executing, there
may be fteen or twenty more that Windows is executing as part of the operating system.
Processes operate by having all their programs (and data) loaded into memory at once. The
operating system is a special process that can stop and start other processes (pre-empt them).
On a single processor machine, only once process can be using the CPU at any one time. But if
the operating system starts and stops each of them (in sequence, including itself) quickly enough,
it looks like they are all executing concurrently.

Threads If the computer (and the operating system) can execute many processes at once, could
we arrange our programs so that their execution splits into more than one process? Sounds crazy,
but it does make sense and Java has a program structure that makes it easy to program | a
thread.
The term thread refers to the thread of control, i.e. how the execution program counter threads
its way through memory as a program executes.

5{1
Thread versus Process You may have multiple threads within one process. For example, in
a word processor, you might have: (i) a thread handling input; (ii) one handling formatting for
the screen; (iii) one handling spell-checking; (iv) one handling periodic saves to le; etc. But
each operates on the same memory space. In contrast, separate processes have separate memory
spaces.
As we will see when working with Brackeen's game programs, threads are quite useful when it
comes to games programming.

Dual-core, Quad-core | multiple processors? Multitasking, processes and threads were on the
scene long before multiple processor machine became commonplace; even on a single processor
machine, they confer signi cant advantages. However, when we have multiple processors (e.g.
dual-core, quad-core), we have the additional advantage that the operating system can truly allow
more than one thread to execute at the same time, as opposed to faking it with concurrent
programming.
A warning. Concurrent (or parallel) programming is dicult; hence it is going to be dicult to
harness multiple processors for general tasks. For a start, a lot of programs require that program
steps follow in exact succession; in many of the programs you will write, an instruction depends
on the results of a previous instruction. Another example where concurrency cannot help is data
communications; if you have a 10-Mbps connection and a process dedicated to reading data from
the connection, the no amount of concurrent processes will speed it up.
First, we give a demonstration of the e ect of multitasking.

5.2 An Example of the Advantages of Multitasking


Figure 5.1 shows the execution of three identical jobs, under two di erent operating system
regimes.
Assume that the tasks/processes are in memory to start with, that they do some computation
(taking 10 millisecs) which results in the requirement to read a particular record from a disk le
(taking 10 millisecs), nally they do some more computation (again 10 millisecs). Viewed as
executions of single programs this may not appear too useful - where do the results go to - but, in
fact, the jobs are quite typical of what would be encountered by an operating system during the
execution lifetime of three processes.
Figure 5.1(a) shows how the jobs would have to run on a single tasking system like MS-DOS, see
section 5.3. The computer becomes I-O blocked during each disk input and hence wastes a lot of
time simply waiting for the disk to respond.
Figure 5.1(b) shows the e ect of multitasking; when one process become I-O bound, the CPU can
do processing for another. Thus, a 90 millisecs of computer usage can be reduced to 60; add more
jobs and you get a greater increase in eciency.
Before we go on, we will give a brief description of a single tasking operating system | MS-DOS.

5{2
| Job A Job B Job C
|-----ooooo-----+-----ooooo----+-----ooooo-----|
|
+-----+----+----+----+----+----+-----+----+----+
0 10 20 30 40 50 60 70 80 90
Time millisec

(a)

|..........-----ooooo.....-----+ Job C
|
|.....-----ooooo-----| Job B
|
|-----ooooo..........-----| Job A
+-----+----+----+----+----+----+-----+----+----+
0 10 20 30 40 50 60 70 80 90
Time millisec

(b)

Key: ----- running on CPU


ooooo waiting for I/O
...... waiting for CPU

Figure 5.1: Illustration of multitasking (a) single user; (b) concurrent tasks

5{3
5.3 Brief Case Study { MS-DOS
5.3.1 Introduction
MS-DOS stands for Microsoft Disk Operating System. MS-DOS version 1.0 was developed for the
original IBM PC which was launched in August 1981. The IBM PC used a 16-bit microprocessor
from Intel called the 8088; in fact, the 8088 was a slightly cut-down version of the Intel 8086.

5.3.2 History
The 8086 was the beginning of the computer family we now know as the Pentium. The family
developed as follows: 8086 (16-bits); 80186 { 8086 with some additions to make it easier to use
for data communications; 80286 { a faster 8086 with additional addressing range { could address
16 Megabytes, and with some features to allow multitasking; 80386 { the start of true 32-bit
computing, actually, the only advance since the `386 has been some go faster parts; the 80386
was faster, but most signi cantly its CPU was 32-bit, and it allowed 32-bit addressing (could now
address 4 Gigabytes of memory); 80486 { 80386 with some cache memory on the chip, and the
oating point arithmetic unit also on the chip; 80586 (called the Pentium) . . . You know the rest.
We better mention what came before the 8086. In 1971, Intel developed the rst microprocessor,
the 4-bit Intel 4004; soon after came the 4040. Then in 1973, came the 8-bit Intel 8008, followed
shortly after by the Intel 8080. The 8080 brought about the idea of a personal computer. Later,
a company called Zilog developed a more powerful copy of the 8080, the Z80.
Some reasonably powerful (for the day) microcomputer systems were built around the Z80 and
8080. A small operating system was developed for the 8080/Z80 { Digital Research's CP/M
(Control Program for Microprocessors). When the 16-bit 8086 came on the scene in 1979, initially,
industrial inertia prevented anyone leaving the Z80{CP/M bandwagon. It took a company with
the strength of IBM to introduce 16-bits to the personal computer arena.
Digital Research had started a development of a 16-bit version of CP/M { CP/M-86. When in
late 1980, IBM decided to develop the PC, they initially didn't think too much about the operating
system; eventually, the started to look around. They got in touch with Bill Gates at Microsoft,
who had developed a successful BASIC interpreter for the 8080. Gates agreed to supply a BASIC
compiler. But after some thought, IBM realised they needed some semblance of an operating
system too. Gates suggested Digital Research, but CP/M-86 was away behind schedule and, we
assume, Digital Research had no idea of what was about to happen to the world of computing.
So IBM go back to Gates | we're in trouble, please help us!. Incidentally, Microsoft had been
selling a version of UNIX under licence. UNIX might have done the job, only it needed a hard disk
and 100 Kbytes or more of memory. The IBM PC had just a single 160K oppy disk, and 64 K of
memory. Gates found a local Seattle company (Seattle Computer Products) who had developed a
crude operating system for the 8086 (86-DOS). Seattle Computer Products agreed to sell 86-DOS
to Microsoft; Microsoft enhanced 86-DOS to produce MS-DOS. MS-DOS shipped with the rst
IBM PCs in August 1981.

5{4
5.3.3 Structure
MS-DOS has a structure along the lines of Figure 5.2. As you can see, MS-DOS allows application
programs to directly access the ROM BIOS or even the hardware itself. This freedom is not present
in Windows 2000 (or XP) or UNIX/Linux and we have already mentioned the advantages of a more
controlling operating system in section 2.7.
The memory layout that exists after booting has completed is shown in Figure 5.3. The total
memory addressable by the 8086, and MS-DOS, is 1 Megabyte (20 bits { 16 bits plus 4 additional
bits added by a bit of jiggery-pokery). Of this 1 MB, IBM decided to keep from 640K and above
for special use. At the time, this wasn't a big deal; the rst PCs had 64K of RAM. It was 1985
before PCs started to have the full 640K.

5.3.4 Processes in MS-DOS


When, at the MS-DOS prompt A:\, you type a command like copy file1.txt file2.txt, the
following happens:

1. The shell (command interpreter) reads the command;


2. The shell looks to see if copy is one of those commands it already has in the kernel;
3. copy is in the kernel, so the shell causes a jump to the copy part of the kernel; that runs,
and when it is done, the kernel passes control back to the shell;
4. If the command is something like format c: (not in the kernel), the shell has a look on the
current disk directory for a le called format.exe;
5. If format.exe is there, the shell loads the le into the area just above the shell (marked
program in Figure 5.3; the shell then causes a jump to the program part of memory; that
runs, and when it is done, it passes control back to the shell;
6. The same is true for, e.g. edit file1.txt; or whatever;
7. The same is also true for a program that you may have written yourself, e.g. MYPROG.EXE;
8. The same is also true for a batch le like autoexec.bat which contains, not a program, but
a list of commands.

Thus, roughly speaking, in MS-DOS the life cycle of a process is as follows:

 The shell waits for a command;


 A program is loaded;
 The program becomes a process when the shell causes a jump to its program area;
 The process runs until it is done;
 The last thing a process does is pass control back to the shell or kernel; and the cycle starts
again;

5{5
+-------------------------------------------------+
| |
| Application Program |
| and |
| shell -- 'command.com' |
| |
+------+-------------------------------+-----+----+
| | |
V | |
| | |
+------+---------------------------+ | |
| | | |
| MS-DOS 'Kernel' | | |
| | | |
+------------------------------+---+ | |
| | | |
V | V V
| | | |
+------+-------------------+ | | |
| | | | |
| MS-DOS Device Drivers | | | |
| | V | |
+--------------------------+ | | |
| | | |
V | | |
| | | |
+------+-----------------------+-------+-+ |
| | |
| ROM BIOS Device Drivers | |
| | |
+------+---------------------------------+ |
| |
V |
| |
+------+-------------------------------------+----+
| |
| Hardware |
| |
+-------------------------------------------------+

Figure 5.2: Interface Structure of MS-DOS

5{6
1M (1024K)
+----------------------+ \
| | } really part of OS
| ROM BIOS | }
| | }
+----------------------+ /
| | \
| Video RAM | } also can be considered part of OS
| | }
+----------------------+ 640K /
| |
| Unused |
| |
+. . . . . . . . . . . + program
| Space for |
| User Program |
| |
+----------------------+
| |
| MS-DOS Shell |
| command.com |
+----------------------+
| |
| MS-DOS Kernel |
| |
+----------------------+ OS
| |
| Special use, e.g. |
| interrupt vectors |
+----------------------+ 0

Figure 5.3: MS-DOS Memory Layout after Booting

5{7
5.4 Processes
5.4.1 Process Life cycle
The concept of a process is central to multitasking. A rough de nition of the term process is: a
program in execution. People have diculty comprehending the di erence between a program and
a process. A program is simply a list of instructions; it has no life. Consider the cookery recipe
example
During its life, a process goes through a series of discrete states, see section 5.4:

 Running { it is currently running on the CPU;


 Ready { it could use the CPU if the CPU were available;
 Blocked { a process enters the blocked state when it requests some resource that cannot be
supplied immediately, e.g. it issues a disk read, but a disk read takes a long time and there
is no point in that process waiting twiddling its thumbs while the data is read. The process
remains in that state until the blocking need becomes satis ed, at which point it becomes
ready.

Figure 5.4: Process States: a process can be running, blocked, or ready


When a job is admitted to the system, a corresponding process is created and placed at the back
of the queue and it is marked ready. It gradually moves to the top of the ready list { see scheduling
below; when the CPU becomes available the process makes a state transition from ready to running.
To prevent any one process, call it process A, monopolising the system { i.e. to provide time-sharing
it must be possible possible to stop the process after it has consumed its allocated amount of CPU
time. This is done via an interrupting clock (hardware) which allows a speci ed time quantum { a
time-slot to pass; if process A is still using the CPU the clock generates an interrupt, causing the
system to regain control. Process A now makes the state transition back to ready, i.e. A is put
back in the ready queue.
The next process, process B, on the ready queue is now put into the running state; and so on . . . .
If a running process initiates an I/O operation before its time-slot expires then the process is forced
to release the CPU; it enters the blocked state and releases the CPU.
If a process is in the blocked state and the event for which it is waiting happens, then the process
makes the transition from blocked to ready.

5{8
5.4.2 Process Control Block (PCB)
When a process makes the transition from running to either blocked or ready (in which case it
relinquishes the CPU), it is necessary to store some details, for example: just as in a subprogram
call, where it has got to (the program counter (PC)), but more. The process control block (PCB)
is a record containing important current information about the process, for example: current state
of the process, unique identi cation of the process, the process's priority, open les & IO devices,
use of main memory and backing store, other resources held, copies of the registers (see chapter
7).

5.4.3 Context Switch


When the CPU switches from one process to another, this is called a context switch. A context
switch is a bit like a subprogram call, but requires more housekeeping e ort, in particular mainte-
nance of the PCB. The CPU e ort required to make a context switch is one of the factors involved
in choosing a scheduling algorithm. Context switching can take a lot of processing time and if you
are not careful, the CPU can spend all its time on context switching.
In this connection, Brackeen warns about overuse of threads in games programs; he cites the case
of creating a new thread for each player or item in the game and points out that this would be a
misuse of threads because the thread / process control system would use a lot of processing time
is swapping between the threads.

5.5 The Kernel


All of the operations involving processes are controlled by a portion of the OS called its kernel.
The kernel represents only a small portion of the code of the entire OS but it is intensively used.
One very important function of the kernel is interrupt processing. In large multi-user systems, the
kernel is subjected to a constant barrage of interrupts. Clearly, rapid response is essential: to keep
resources well utilised, and to provide acceptable response times for interactive users.
In addition, while dealing with an interrupt, the kernel disables other interrupts. Thus, to prevent
interrupts being disabled for long periods of time, the kernel is designed to do the minimum amount
of processing possible on each interrupt and to pass the remaining processing to an appropriate
system process.
The kernel provides:

 interrupt handling;
 process creation & destruction;
 process state switching and scheduling;
 interprocess communication;
 manipulation of PCB;

5{9
 support of IO activities;
 support of memory allocation & deallocation;
 support of the le system;
 support of system accounting functions.

5.5.1 Kernel Privileges


The kernel must have privileges that allow it to do execute instructions that normal processes cannot
access, e.g. disable interrupts, access IO devices. Privilege is usually conferred by the (hardware)
mechanism of CPU mode (either privileged or user) which is stored in the CPU hardware, and
is only changeable by a process in privileged mode, i.e. the kernel. User level processes, e.g.
processes such as a running Word program, run in user mode.
In the original Intel 8086/88 and and to a greater extent in the 80286, there was limited support
for privileged modes; however, in the 80386 and after, modes are provided that support kernel
privileges and thus full multitasking.
As we have mentioned above, one of the major activities of the kernel is interrupt servicing. Let us
concentrate on one important type of interrupt, namely, the timing interrupt that the kernel uses
to switch to the next process when it is performing multitasking.
The key to each interrupt is its interrupt vector (or, equivalently, its IRQ). The interrupt vector
gives the address of the interrupt service subprogram. Now, obviously, from the point of view of a
malicious process that might wish to grab control from the OS, that interrupt service routine (and
the interrupt vector) are the key. If the malicious process can overwrite the interrupt vector (to
point to its own program area) and can overwrite the interrupt service routine to do whatever it
(the malicious process) wants | for example, jump to its code | then the OS has lost control.
Being able to overwrite an interrupt vector, or the kernel code, is like being able to change the
door locks on your bank.
Consequently, the key to the OS retaining control is to ensure that no process can write to any
part of the OS's memory, including the interrupt vector area.
In fact, this problem is the same problem as stopping separate processes from interfering with one
another's memory area.

5.6 Thread Examples in Java


.
Figures 5.6, 5.7, 5.8, and 5.9 (from (Horstmann & Cornell 2004b) shows a Java bouncing ball
program implemented without threads.
When you run this program, see Figure 5.5, you will see it acts a bit like the MS-DOS programs
we mentioned above. When the program starts bouncing one ball, that's all it can do until the
bouncing stops and the ball stops over on the right of the window.

5{10
If you click Start, the program waits until the current ball nishes, and only then does it start the
next one; likewise, clicking Close has no e ect until the program is nished what it is currently
doing. The program is executing in a single thread.
In contrast, the program in Figures 5.10, 5.11 and 5.12, creates a new thread for each click of
Start and you can have many balls bouncing at once; it also creates a thread that will take care
of the Close button | you do not have to wait until everything is nished.

Figure 5.5: Bouncing ball.

5{11
/** Bounce.java
@version 1.30 2001-05-06 @author Cay Horstmann
*/

/**
Shows an animated bouncing ball.
*/
public class Bounce{
public static void main(String[] args)
{
JFrame frame = new BounceFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.show();
}
}

/**
The frame with canvas and buttons.
*/
class BounceFrame extends JFrame{
/**
Constructs the frame with the canvas for showing the
bouncing ball and Start and Close buttons
*/
public BounceFrame(){
setSize(WIDTH, HEIGHT); setTitle("Bounce");

Container contentPane = getContentPane();


canvas = new BallCanvas();
contentPane.add(canvas, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
addButton(buttonPanel, "Start",
new ActionListener(){
public void actionPerformed(ActionEvent evt)
{
addBall();
}
});

addButton(buttonPanel, "Close",
new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
System.exit(0);
}
});
contentPane.add(buttonPanel, BorderLayout.SOUTH);
}

Figure 5.6: Bounce Program


5{12 | no threads, part 1
/**
Adds a button to a container.
@param c the container
@param title the button title
@param listener the action listener for the button
*/
public void addButton(Container c, String title,
ActionListener listener){
JButton button = new JButton(title);
c.add(button);
button.addActionListener(listener);
}

/**
Adds a bouncing ball to the canvas and makes
it bounce 1,000 times.
*/
public void addBall(){
try
{
Ball b = new Ball(canvas);
canvas.add(b);

for (int i = 1; i <= STEPS; i++)


{
b.move();
Thread.sleep(TIMESTEP);
}
}
catch (InterruptedException exception)
{
}
}

private BallCanvas canvas;


public static final int WIDTH = 450;
public static final int HEIGHT = 350;
public static final int STEPS = 1000;
public static final int TIMESTEP = 5;
}

Figure 5.7: Bounce Program | no threads, part 2

5{13
/**
The canvas that draws the balls.
*/
class BallCanvas extends JPanel{
/**
Add a ball to the canvas.
@param b the ball to add
*/
public void add(Ball b){
balls.add(b);
}

public void paintComponent(Graphics g){


super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
for (int i = 0; i < balls.size(); i++){
Ball b = (Ball)balls.get(i);
b.draw(g2);
}
}

private ArrayList balls = new ArrayList();


}

/**
A ball that moves and bounces off the edges of a
component
*/
class Ball{
/**
Constructs a ball in the upper left corner
@c the component in which the ball bounces
*/
public Ball(Component c) { canvas = c; }

/**
Draws the ball at its current position
@param g2 the graphics context
*/
public void draw(Graphics2D g2)
{
g2.fill(new Ellipse2D.Double(x, y, XSIZE, YSIZE));
}

Figure 5.8: Bounce Program | no threads, part 3

5{14
/**
Moves the ball to the next position, reversing direction
if it hits one of the edges
*/
public void move()
{
x += dx;
y += dy;
if (x < 0)
{
x = 0;
dx = -dx;
}
if (x + XSIZE >= canvas.getWidth())
{
x = canvas.getWidth() - XSIZE;
dx = -dx;
}
if (y < 0)
{
y = 0;
dy = -dy;
}
if (y + YSIZE >= canvas.getHeight())
{
y = canvas.getHeight() - YSIZE;
dy = -dy;
}

canvas.paint(canvas.getGraphics());
}

private Component canvas;


private static final int XSIZE = 15;
private static final int YSIZE = 15;
private int x = 0;
private int y = 0;
private int dx = 2;
private int dy = 2;
}

Figure 5.9: Bounce Program | no threads, part 4

5{15
/** BounceThread.java
@version 1.30 2001-05-06 @author Cay Horstmann
*/
/**
Shows an animated bouncing ball running in a separate thread
*/
public class BounceThread{
public static void main(String[] args)
{
JFrame frame = new BounceFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.show();
}
}

/**
The frame with canvas and buttons.
*/
class BounceFrame extends JFrame{
/**
Constructs the frame with the canvas for showing the
bouncing ball and Start and Close buttons
*/
public BounceFrame(){
setSize(WIDTH, HEIGHT); setTitle("BounceThread");

Container contentPane = getContentPane();


canvas = new BallCanvas();
contentPane.add(canvas, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
addButton(buttonPanel, "Start",
new ActionListener(){
public void actionPerformed(ActionEvent evt)
{
addBall();
}
});

addButton(buttonPanel, "Close",
new ActionListener(){
public void actionPerformed(ActionEvent evt){
System.exit(0);
}
});
contentPane.add(buttonPanel, BorderLayout.SOUTH);
}

Figure 5.10: Bounce Program | with threads, part 1

5{16
/**
Adds a button to a container.
@param c the container
@param title the button title
@param listener the action listener for the button
*/
public void addButton(Container c, String title,
ActionListener listener){
JButton button = new JButton(title);
c.add(button);
button.addActionListener(listener);
}

/**
Adds a bouncing ball to the canvas and starts a thread
to make it bounce
*/
public void addBall(){
Ball b = new Ball(canvas); canvas.add(b);
BallThread thread = new BallThread(b);
thread.start();
}

private BallCanvas canvas;


public static final int WIDTH = 450;
public static final int HEIGHT = 350;
}
/**
A thread that animates a bouncing ball.
*/
class BallThread extends Thread{
/**
Constructs the thread.
@aBall the ball to bounce
*/
public BallThread(Ball aBall) { b = aBall; }

public void run(){


try {
for (int i = 1; i <= 1000; i++){
b.move();
sleep(5);
}
}
catch (InterruptedException exception){ }
}
private Ball b;
}

Figure 5.11: Bounce Program | with threads, part 2


5{17
/**
The canvas that draws the balls.
*/
class BallCanvas extends JPanel{
/**
Add a ball to the canvas.
@param b the ball to add
*/
public void add(Ball b){
balls.add(b);
}

public void paintComponent(Graphics g){


super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
for (int i = 0; i < balls.size(); i++){
Ball b = (Ball)balls.get(i);
b.draw(g2);
}
}

private ArrayList balls = new ArrayList();


}

/**
A ball that moves and bounces off the edges of a
component
*/
class Ball{ // same as previous

Figure 5.12: Bounce Program | with threads, part 3

5{18
Chapter 6

Images, Displays, Animation, Colour

6.1 Introduction
This chapter discusses the nature of images, human vision, cameras and other image sensors, the
nature of colour. We mention how colour images are represented in a computer.
Also, in discussing eyes and cameras, we prepare ourselves for three-dimensional (3D) graphics.
When we look at a 3D world scene with our eyes, or with a camera, we are collapsing that 3D
scene onto two dimensions (2D). In the 3D graphics part of a video game we create a virtual 3D
world, but to display it on a 2D screen requires the computer to perform (mathematically) the
same task as an eye or a camera does.
Some diagrams in this chapter are taken from Gonzalez and Woods (Gonzalez & Woods 2002),
chapters 2 and 6.

6.2 3D Graphics, Images, Animation


An image is a picture | a two-dimensional (2D) representation of a three-dimensional scene.
2D Graphics is about creating images. Animation is about moving images. 3D Graphics is about
creating 3D virtual scenes; the graphics engine must then manipulate the components of this scene
and eventually `take an image of it' (projecting) using a virtual camera. If the virtual scene is static,
then the graphics engine can take its time over the manipulations and projection. However, games
are dynamic. Most of the hard work in a video game is about manipulating the objects in the 3D
virtual scene, then projecting to create an image and displaying successive images on a screen at
a rate fast enough that the viewer has the sensation of continuity in time; this is animation.

6.3 Digital Images


6.3.1 Continuous/Analogue versus Discrete/Digital
Some aspects of our world are continuous, some discrete; roughly speaking, in a computer world, we
use the terms analogue and digital. Many quantities and `things' in the real world are continuous:

6{1
lengths, volumes, areas, masses, weights; that is until you get down to atomic sizes | as Max
Planck and Albert Einstein discovered when they came across the quantum theory of physics.
If you want to represent real world quantities in a computer you need to convert them to discrete
or digital versions; but apart from digital images taken with a digital camera, our computer game
virtual worlds will be digital from the start.
An image is some sort of representation of some part of the real world. A painting is an image;
a printed photograph is an image; both are continuous representations | meaning that you can
magnify a part of the image larger and larger and it still looks smooth.
Contrast digital images. These are made up of little blocks called pixels | short for picture
elements. An individual pixel has a xed colour. If you keep magnifying a digital image eventually
you will see that it is a joined up patchwork of pixels. But if the pixels are small enough | we say
that the image has high spatial resolution | your eye sees the image as continuous.
If the discrete blocks are small enough the whole lot looks continuous. High resolution.
Same for time and space.
Time is continuous. But in a clock it is represented as a digital quantity; in a normal ticking clock
such as a grandfather clock, it is digitised into seconds; in an electronic watch it is digitised into
whatever is the frequency of the vibrating crystal that drives it.
There are two parts to digitisation: (a) the chopping up into blocks (in the case of images) and
into time samples in the case of sound and video | this is sampling; (b) converting brightness, in
the case of images, and loudness, in the case of sounds, into numbers | this is digitisation. But
sometimes sampling and digitisation are lumped together as digitisation.

Real Numbers versus Integer Numbers First of all we have the natural numbers f0; 1; 2; : : :g;
these are the numbers we use when we count. Integers include the natural numbers together with
their negatives, f 1; : : : ; 2; 1; 0; 1; 2; : : : ; +1g.
Real numbers are an entirely di erent matter. When we measure things, e.g. the weight of a piece
of cheese, the length of a piece of string, we have a real number. You might say, the weight of
this piece of cheese is 25 grams. Not much di erence from a natural number, I hear you say. But,
almost certainly, in coming up with the 25, you merely took the nearest natural number. If you were
in a laboratory, using a very accurate instrument, you might have originally had 25.4124359267
grams. At home you might have got 25.4 grams. Which is correct, 25, 25.4, 25.4124359267
grams? Actually, none of them. If you wanted to be fully correct, you'd have to use thousands
and millions of digits; to be exact, in nity of them. Real numbers form a continuum. Between any
two real numbers, no matter how close, there are an in nite number of other real numbers. The
more precision you use in your measurement, the more digits you get.
In contrast, between 3 and 6, there are just two other integer numbers, 4 and 5. You cannot be
any more precise than counting the people in a classroom and stating, for example, there are 23
students present.
It is easy to represent integers in a computer; the only slight problem is that you cannot get to
+= 1, but you can get as close as you need.
Ex. (a) What are the largest negative and positive numbers possible in a C++ int variable? (b)
C++ short? (c) C++ byte? (d) What is the di erence between signed and unsigned

6{2
6.3.2 Analogue to Digital Converters and Digital to Analogue Converters
When you have an analogue signal and need to get it into a computer, you need an Analogue to
Digital Converter (ADC); an ADC performs two tasks: (a) samples in time and (b) converts to
numbers.
When you have an digital signal or a set of numbers in a computer and you want to send them to,
for example, a loudspeaker, you need an Digital to Analogue Converter (DAC); a DAC converts
to numbers to voltage or current. Because there may be stepiness or blockiness in the analogue
signal so produced, a DAC is often followed by a smoothing lter. Anti-aliasing, see below, is a
form of smoothing out of digital e ects in image displays.
Back to images.

6.3.3 Images and Digital Images


The term image or, strictly, monochrome image, refers to a two-dimensional brightness function
f (x ; y ), where x and y denote spatial coordinates, and the value of f at any point (x ; y ) gives the
brightness (grey level or colour) at that point.

Monochrome versus colour Monochrome images are grey level images; they are sometimes
called black-and-white | but incorrectly, for black-and-white implies that there are just two values:
black, and white, and no in-between. - i.e. f (x ; y ) is a grey level.
In a colour image f (x ; y ) gives a colour. A colour image can be represented by three monochrome
images, each representing the intensity of a primary colour (red, green, blue). Thus,
fr (x ; y ); fg (x ; y ); fb (x ; y ).

Getting closer to programming language notation, a colour image is represented by f (b; x ; y ),


where b denotes colour (b is colour band), where b = 0; 1; or2, for red, green, or blue.

Digital The monochrome image, f (x ; y ), mentioned above is still continuous, and in two senses:
(i) f (x ; y ) is a real (continuous valued) number, and, (ii) x, and y are real numbers. So, continuous
valued, and spatially continuous | like a photograph.
Thus, you can achieve in nitesimally ne resolution in f (x ; y ), and in x , and y .
In computers we must use digital (or discrete) approximations. We approximate f (:; :) by restricting
it to a discrete set of grey levels; often an 8-bit integer 0 : : : 255, and, we sample f (:; :) at a discrete
lattice of points, xi ; i = 0 : : : n 1, and yj ; j = 0 : : : m 1; see Figure 6.1.
Thus, we arrive at a digital image, f (r; c ), where f can take on discrete values 0 : : : G 1 and r ,
0 : : : n 1, c , 0 : : : m 1.
f can now be viewed as a matrix (two-dimensional array) of numbers,

6{3
0 xmax 0 m-1
+--------------------> x +--------------------|> j (c -
| | column)
0 | . . 0 | f(1,1) f(1,2)
| x1,y1 x2,y1 |
| |
| . | f(j, i)
| xi,yj j |
| r - row|
| |
ymax| n-1- f(m-1, n-1)
V V
y
Continuous image: Discrete:
domain of f(,) is
[x = 0,xmax], [y = 0,ymax] {0..n-1}, {0..m-1}
an n-row X m-column
image (m X n)

(a) (b)

There is inconsistency between (a) and (b); in the real world, x is


the horizontal axis and is the first argument, and y is
vertical axis and the second
argument. In digital images we mostly deal with row, $r$, vertical,
and column, $c$, horizontal.

Also, we have the problem that computers like $r$ to grow \emph{down},
rather than as $y$, up, in the real world.

Figure 6.1: Correspondence between continuous and discrete axes

f (0; 0) f (0; 1) : : : f (0; m 1)


 
 f (1; 0) f (1; 1) : : : f (1; m 1) 
f (r; c ) =  (6.1)
 
:
 
 
f (n 1; 0) f (n 1; 1) ::: f (n 1; m 1)

Typically, on graphics cards, we have n = 768; 1024; : : :, m = 1024; 1280; : : :. G , number of


values, is typically 256, i.e. values go 0; 1; : : : 255. In a colour image, we have shades of red going
from 0; 1; : : : 255; and the same for green and blue.
Ex. If we have 256 shades, i.e. 8-bits, each of red, green and blue, how many di erent colours are
possible?
Ex. If we have 8-bits for each of red, green and blue, and the image is 1024  1024, how many
bytes of memory does the image require?

6{4
Ex. Eight bits (each) are adequate to represent colours, i.e. if you went to 12 or 16 bits there
would be no perceptible improvement. Why do many graphics cards use 32-bits per pixel? There
are at least two reasons.
Ex. (a) If you had to digitise a TV image broadcast by RTE or BBC, suggest suitable values of
m, n; (b) using the results of (a), and assuming 25 frames (images) per second, how much data
for a one hour lm ?

Other examples of digital quantities Music on tape or on vinyl LP is continuous. Music on


CD is digital. CD sampling rate is 44, 100 samples per second, with 12 or 16 bits per sample, 2
channels (stereo).
Ex. Verify that a 60 minute album will indeed nearly ll a 650-MB CD-ROM.
In modern telephone systems, speech is transferred digitally between major exchanges | here you
can get away with 8,000 samples per second, and 8-bits per sample.

Raster scanning The image model given above corresponds to the image model used in raster
graphics, i.e. the image is formed by regular sampling of the x , and y axes.

Pixel Each f (r; c ) in eqn 6.1 is a pixel (picture element).

Spatial Resolution Spatial resolution is high if the samples xi , yj are closely spaced, and is low
if they are widely spaced. Clearly, the closer the spacing, the more alike the digital image will be
to the original, i.e. we are always demanding higher resolution. On the other hand, the higher the
resolution, the larger are m, n | more data; data volume grows as the square of the resolution.
Another reason to restrict n and m to numbers like 768, 1024 is that displays, see section 6.4,
cannot handle any higher resolution.
Ex. Laser printers commonly work at 300 dots (pixels) per inch. How many pixels in an A4 page?
The e ects of reducing spatial resolution are shown in Figure 6.2. The original image, upper left, is
256  256; the upper right image simulates the e ects of reducing resolution and thereby reducing
the number of pixels to 128  128; similarly, the lower left simulates a 64  64 pixel image and the
lower right 32  32.
These days a cheap digital camera will give you about 3000  2000 pixels and the most expensive
ones about 4000  3000.

Grey Level Resolution With proper selection of the digitisation range, it is usually possible to
represent, without any humanly perceivable degradation, monochrome images using just 8-bits;
the psychologists tell us that humans can perceive no more than about 160 levels at once. Also,
in my experience, there appears to be some natural law that says that most image sensors cannot
deliver any useful higher grey-level / colour resolution.
Ex. Palette-based image representation works a bit di erently than using red, green, and blue
values for each pixel Explain. See Brackeen book, or do a web search.

6{5
Figure 6.2: Upper left: original image. Upper right: resolution reduced by a factor of 2. Lower left
and right: reduced by factors of 4 and 8.

6{6
The e ects of reducing grey level resolution are shown in Figures 6.3 and 6.4. The original image
is the same one that is in the upper left of Figure 6.2.

Figure 6.3: Image quantised to 16 grey levels, top; eight levels, bottom.
After printing on a laser printer and then photocopying, the e ects may not be too obvious; on a
computer screen, 16 levels (four bits) is indistinguishable from the original, at eight levels (three
bits) you start to notice the quantisation, at four levels (two bits) the quantisation is very obvious;
and then two levels (one bit). The fact that the image was noisy to start o reduces the e ect a
little.
Ex. (a) Make sure you are comfortable with the correspondences: 16 levels, 4-bits; 8 levels, 3-bits;
4 levels, 2-bits; 2 levels, 1-bits; (b) what would a 0-bit image look like?
Ex. (a) We have seen that 16 grey levels (4-bits per pixel) might be enough in some cases; how
could this be used to reduce the size of an image le? (b) If the original image is 256  256  8
bits, how many bytes? (c) How many bytes in the 256  256  4 bits image?

Noise We said that the original in Figure 6.2 is noisy. The original was taken from one frame of
a video; a video camera has to grab image very fast, i.e. spends little time on each grab, hence
it has little time to smooth out random variations in the little sensors. This is noise. You get the
same sort of noise if you record sounds with a very cheap microphone.
Noise is little variations in the image pixels (or sound samples) that were not there in the scene
that the camera pointed at.

6{7
Figure 6.4: Image quantised to four levels, top; two levels, bottom.

6{8
6.3.4 Anti-aliasing
Pixels are little blocks of colour. When you display a line or text character or some object on top
of a background, but which contrasts with the background, the blockiness can be perceivable. We
can get rid of the blockiness (aliasing) by blending the object into its background. This spatial
blending is called anti-aliasing. Most graphics cards will handle anti-aliasing for you.
If you look again at the bottom left image in Figure 6.2 you will see blockiness or aliasing; if you
defocus your eyes, the blockiness will disappear and you will start to think that you see more detail
in the image. The blurring caused by defocussing the eyes is the equivalent of anti-aliasing.

6.3.5 Opacity, alpha


In a Java program the normal way to create a colour is to give the Red, Green, Blue values:

// R G B
Color c = new Color(255, 0, 0); // pure red

// or, you can use 0..1 (float) instead of integer 0..255

Color c = new Color(1.0, 0.0, 0.0); // pure red

But you can also specify the opacity (transparency) of the colour. The opacity is called alpha; so
now we have Red, Green, Blue, Alpha values:

// R G B A
Color c = new Color(255, 0, 0, 255); // pure red, completely opaque

Color c = new Color(255, 0, 0, 0); // pure red, completely


transparent

Color c = new Color(255, 0, 0, 50); // is better if you want to see


// the colour against a background

6.4 Displays and Factors Associated with them


6.4.1 Display Hardware | CRTs, LCDs, Plasmas
Read pp. 106{110 of A.S. Tanenbaum, Structured Computer Organisation, Prentice-Hall, 2005;
handed out.
Before you go on, please note that we now have two spatial resolutions, one in the graphics card,
the other on the display hardware.
A cathode ray tube (CRT) has groups of red, green, and blue light emitting dots painted on the
rear of the glass at the front of the tube, but they are so close together that the surface is treated

6{9
as continuous. Thus you can easily change the resolution of the images that you send to it. Of
course, depending on the size and construction of the CRT, these is an upper limit, normally
something like 1600  1200 for a 17-inch CRT. If, in the operating systems settings, you set the
display to 640  480, and display on the latter CRT, you might see a bit of blockiness, but the
image would ll the screen.
A liquid crystal display (LCD) is truly digital; typical LCD screens are 1024  768 (15{17-inch) and
1280  1024 for 19-inch. If you you set the display to 640  480, and displayed on the one of the
latter LCDs, you would probably get a little image in the middle of the screen and blank around
the edges. Some LCD displays have the capability of resampling.
What follows is taken from a Usenet newsgroup sci.image.processing answer by Dave Martindale
(d|@cs.ubc.ca), to a question by me.
In the case of LCD screens, there is local memory in the LCD controller to remember the state
of every pixel in the screen. Because of this, there are two parts to "refreshing" an LCD screen:
getting the data from the computer frame bu er (graphics card) to the LCD controller, and then
driving the LCD panel itself.
The rst part of the process is done as if the LCD was a raster device. The pixel data is sent from
the computer to the LCD controller in row/column raster order just as if it was a CRT. If you're
using an analog connection between computer and display, the graphics card actually generates
analog RGB and sync signals just as if it was driving a CRT - it has no way of knowing the display
is actually an LCD. Then the display controller generates local clock signals locked to the incoming
analog video, and converts the signal back into digital form before storing the pixel data in the
LCD controller memory. If the frame bu er pixel clock and the LCD controller pixel clock are not
the same, you can end up with pixel jitter artifacts.
On the other hand, if you use a digital (DVI) connection between graphics card and display, the
pixel data is transferred in digital form, with no noise introduced by D/A and A/D conversion, and
without wasting time for horizontal and vertical sync and blanking periods. This is more ecient,
and avoids the need for the digitizing circuitry in the display.
Either way, once the pixel information is in the LCD controller, the controller then uses it to mod-
ulate individual pixel cells in the LCD panel itself. This happens at a frequency that is determined
by the controller and panel's needs, and it's not necessarily synchronized with the incoming video.
LCD screens have a native resolution, i.e. each screen pixel corresponds to a single indivisible
element. However, many LCD monitors contain internal resampling hardware that will accept
an incoming signal at a wide range of resolutions and then resample that to the actual display
resolution. The image tends to lose some crispness when you do this, so it's generally better to
operate at the native resolution.

Raster refresh and icker and tearing The refresh is raster as described above.
The light output from an LCD is continuous; it doesn't come in bright pulses as the electron beam
sweeps over the phosphor like a CRT. This means that LCDs don't icker if they are updated
slowly from the computer; 60 Hz refresh ickers visibly on a CRT but not LCD.
On the other hand, tearing is caused when a single displayed image on screen comes from two
di erent points in time. To avoid this with a CRT, it's simply necessary to have the video con-
troller swap bu ers (between the previous and next rendered frame) during vertical retrace so each

6{10
displayed frame comes from a single point in time. With an LCD, there's the additional delay
between display controller update and screen update which can complicate things.

If 25{30 Hz is good enough for television or movies, why the need for 70{85 Hz refresh on
computer screens? With a ickering light source, we can see icker up to about 72 Hz when
the light is very bright, dropping below 50 Hz when the light is dim.
Television uses a 25 or 30 Hz complete frame rate, but the image is sent in interlaced mode: all the
even scanlines are sent in the rst 1/50 or 1/60 second " eld", followed by all the odd scanlines
in a second eld. CRTs display this signal in the same way, so the screen is actually refreshed at
50 or 60 Hz; it just isn't quite the same data each time. As long as there are no drastic changes
between two adjacent scanlines (and good TV is ltered so there is not), we don't see icker.
Movies are shot at 24 FPS, but the projectors use a shutter that interrupts the light either 2 or
3 times per frame, so the actual icker rate on screen is 48 or 72 Hz. 48 Hz is most common,
and works well in most theatres where even the brightest white in the image is not that bright in
absolute terms, and most of the image is much dimmer. It's common to see a little bit of icker in
the brightest portion of the image only. 72 Hz shutters are better in small theatres with particularly
bright images; that pretty much eliminates visible icker.
But computer screens aren't interlaced (anymore), and they are operated at high brightness, so
you need about 72 or 75 Hz refresh rate to avoid visible icker.

LCD versus TFT versus Plasma TFT is just one LCD panel technology; it's not a separate type
of display. Plasma displays are likely very similar to LCDs when it comes to the computer-display
controller connection. Driving a plasma panel takes very di erent voltages and current from driving
an LCD panel. LCDs use very low voltage and current to change the polarization state of the liquid
crystals, while the light comes from a separate backlight and two polarizing sheets actually absorb
or pass light. Plasma panels emit light directly from each pixel cell.

6.4.2 Flicker and Tearing


Ex. (a) In the context of display updating, what is icker; (b) how does double bu ering solve
the problem of icker? (c) why, in double bu ering, is page ipping preferable to copying between
bu ers?
Ex. (a) What is meant by saying that a CRT display has a refresh rate of 75-Hz? (b) What is
vertical retrace?
Ex. (a) With an LCD display, you shouldn't really experience icker. Why not?
Ex. (a) Explain, with the aid of a diagram, the tearing e ect due to lack of synchronisation of
monitor refresh and page ipping / bu er copying.
Ex. What is the di erence between an analogue LCD display and a digital one? Be precise.

6{11
6.5 Visual Perception
Here, brie y, are some points about human visual perception:

 the perceived image may di er from the actual light image (i.e. the perceived brightness
image is a considerably modi ed `copy' of the physical light intensity emanating from the
scene);
 there are two types of light sensors on the retina { rods and cones;
 rods are more sensitive than cones; rods are used for night (scotopic) vision; rods are largely
colour insensitive (e.g. no colour evident in moonlight);
 cones are used for brighter light, cones can sense colour;
 perceived (subjective) brightness (Bs ) is roughly a logarithmic function of light intensity (L):
thus, if you increase L by 10, Bs increases by only 1 unit, increase L by 100 Bs increases by
2 units, 1000 increases by 3 etc.
 the visual system can handle a range of about 1010 (10 thousand million) in light intensity
(from the threshold of scotopic vision to the glare limit). (Question: how many bits is that?)
 to handle this range, the pupil must adapt by opening and closing the pupil; opening the pupil
{ in darkness { lets more light in; closing it { in bright light { lets less light in;
 the eye can handle only a range of about 160 levels at any one instant, i.e. where there is
no opening and closing of the pupil; of course, this explains why 8-bits (256 levels) usually
suce in a display memory.

Figure 6.5 (from (Gonzalez & Woods 2002)) shows a simpli ed cross section of a human eye.
Note the lens and the light sensitive retina | a cameras also has a lens and light sensitive sensors
or lm.
Figure 6.6 (from (Gonzalez & Woods 2002)) shows image formation in a a human eye. A camera
operates similarly, replacing the retina with light sensitive sensors or lm.

6{12
Figure 6.5: Cross section of the human eye.

Figure 6.6: Image formation in a human eye.

6{13
6.6 A Model of a General Imaging System
Note: in this chapter we treat physical units somewhat informally | a later chapter give a little
more detail.
A general camera-based sensing arrangement is shown in Figure 6.7 ((Gonzalez & Woods 2002)).
The scene element, some distance from the camera lens, is projected onto the image plane. At
the image plane there is a mosaic of light sensitive sensors, see Figure 6.8; this mosaic has the
e ect of transforming the two- dimensional continuous image lightness function, fi (y ; x ), into a
discrete function, f 0 [r; c ], where r (ow) and c (olumn) are the discrete spatial coordinates. Then, the
electrical voltage or current output from each sensor eventually, f 0 [:] gets digitised to yield a digital
image, f [r; c ]. Hence, we have two digitisations: rst spatial | a spatial chopping (sampling) into
rectangular pixels; next amplitude | conversion of analogue sensor output (voltage or current)
into numbers. The data are then ready for transferring to computer memory.
For colour, all we need are alternating red, green and blue sensors. Normally, the sensors will all
be the same, but will have red, green, or blue lters in front of them.

Figure 6.7: Image acquisition system (camera).

Figure 6.8: Continuous image (a) projected onto a sensor array; (b) sampled (spatially) and
quantised (in amplitude values).

6{14
Thus, we arrive at a digital image: f [r; c ] where f can take on discrete values [0; 1; : : : G 1] and
r 2 [0; 1::nRow s 1], c 2 [0; 1::nCol s 1]. And if the camera handles colour, we have three
images, fr ed ; fgr een ; fblue .

f [0; 0] f [0; 1] ::: f [0; nCol s 1]


 
 f [1; 0] f [1; 1] ::: f [1; nCol s 1] 
f [r; c ] =  (6.2)
 

 
 
f [nRow s 1; 0] f [nRow s 1; 1] ::: f [nRow s 1; nCol s 1]

6.6.1 Light and re ection


6.6.2 Motivation
Think monochrome for the moment. Sometimes we talk of a (monochrome) image as representing
a two-dimensional brightness function f (x ; y ), where x and y denote spatial coordinates, and the
value of f at any point (x ; y ) gives the brightness (or, grey level) at that point.
For this section it would be better to talk of light intensity or lightness (instead of brightness).
Correct terms: lightness describes the real physical light intensity, brightness is what we perceive,
that is, brightness is only in the mind.
Think now of the scene as a at two-dimensional plane { a sheet of coloured paper. Its lightness,
f (x ; y ), is the product of two factors:

 i (x ; y ) { the illumination of the scene, i.e. the amount of light falling on the scene, at (x ; y ),
 r (x ; y ) { the re ectance of the scene, i.e. the ratio of re ected light intensity to incident
light.

f (x ; y ) = i (x ; y )r (x ; y ) (6.3)

Eqn. 6.3 is depicted in Figure 6.9; the amount of light falling on the surface is i ; r is the amount
of light that gets re ected. r would be nearly 1 for a very white surface and nearly 0 for a black
surface, but, completely white (r = 1:0) and completely black (r = 0:0) are hard to achieve.
The table below gives some naturally occurring ranges of values of i and r :

Illumination (i) units


Sunny day at surface of earth 9000
Cloudy day 1000
Full Moon 0.01
Oce lighting 100

6{15
Figure 6.9: Light re ection.

Re ectance (r) units


Snow 0.93
White paint 0.80
Stainless steel 0.65
Black velvet 0.01

6.6.3 Uneven Illumination


More often than not, when we sense a scene, we want to measure r (x ; y ), so we assume that
i (x ; y ) is constant I0 , so that f (x ; y ) = r (x ; y )I0 . Thus except for the multiplicative constant, we
have r (x ; y ).
If illumination is not constant across the scene, then we have problems disentangling what variations
are due to r , and what are due to i .

6.6.4 Uneven Sensor Response


Most modern electronic cameras are charge-coupled device (CCD) based. In a CCD you have a
rectangular array of light sensitive devices i = 0; 1; :::n 1, j = 0; 1; :::m 1 at the image plane.
The voltage given out by these is proportional to the amount of light falling on it.
Often it is assumed that an image f (x ; y ) arriving at the cameras image plane, is converted into
values (analogue or digital), fc (x ; y ), which are proportional to f (x ; y ), i.e.

fc (x ; y ) = Kf (x ; y ) (6.4)

If K = K (x ; y ), i.e. it varies across the image plane, then we have non-even sensitivity and if we
look very closely our image may look a little patchy.

6.6.5 Di use and Specular Re ection


The simple model in eqn. 6.3 and Figure 6.9 does not tell the ll story. For a start there is colour,
but colour can be modelled using three equations, one for red, one for green, and another for blue.

6{16
Wavelength (m) Name Frequency (Hz)
10 15 1 femtometer (fm) gamma rays 3  1023 Hz
10 12 1 picameter X-rays 3  1020 Hz
10 9 1 nanometer X-rays 3  1017 Hz
10 8 10 nm Ultraviolet 3  1016 Hz
10 7 100 nm U-V
4  10 7 400 nm Visible light (violet)
7  10 7 700 nm Visible (red)
10 6 1 micrometer Infrared (near) 3  1014 Hz
10 5 10 micrometers Infrared 3  1013 Hz
Infrared (heat)
10 3 1 millimeter Infrared (heat) + 3  1011 Hz
microwaves (300 GigaHz)
10 1 0.1 meters microwaves 3  109 (3 GigaHz)
1 meter TV etc. (UHF) 3  108 (300 MegaHz)
FM radio is  100 Mhz (VHF)
10 meters radio (shortwave) 30 Mhz
100 meters radio (shortwave) 3 MHz
200 600 m radio (medium wave) 1.5 MHz to 500 KHz
1500 m (1 Km) radio (long wave) 200 KHz

Table 6.1: The electromagnetic spectrum.

Things get more complicated when we think about the angle at which the light hits the surface
and the angle of the eye or camera that is seeing the re ected light.
There are two models which give a good approximation of re ection from surfaces; these are
di use, also called Lambertian, and specular.
Di use re ection corresponds to matte surfaces. Specular re ection corresponds to shiny surfaces
(specular means mirror-like).
We will deal with these when we get to 3D graphics programming in Year 2.

6.7 Colour
6.7.1 Electromagnetic Waves and the Electromagnetic Spectrum
Light is a form of energy conveyed by waves of electromagnetic radiation. The radiation is char-
acterised by the length of its wavelength; the range of wavelengths is called the electromagnetic
(EM) spectrum. Visible light occupies a very small part of the spectrum.
Table 6.7.1 shows the EM spectrum: the left hand column gives the wavelength in meters, the
middle gives the name of the band, and the right gives the frequency of the radiation in Hertz
(cycles per second). Figure 6.10 (from (Gonzalez & Woods 2002)) shows another view of the EM
spectrum.

6{17
Figure 6.10: Electromagnetic spectrum.

6{18
Thus, roughly speaking, if you were to speed-up the frequency of vibration of a TV signal, you
would get microwaves, speed-up microwaves ! heat radiation, ! light ! UV ! X-rays, etc.
If you had a very small and light magnet suspended in a vacuum and brought it near to an RTE
TV transmitter, it might start to vibrate at around 600-MHz (600,000,000 times a second);
that corresponds to a wavelength of 0.5-metre; for more on UHF (ultra high frequency), see
http://en.wikipedia.org/wiki/Ultra_high_frequency.
Frequency, f , wavelength  and speed of the waves (speed of light), c are related by equation 6.5,

f = c: (6.5)

f is measured in Hertz (Hz);  is measure in metres (m), and c = 3  108 metres per second
(ms 1 .
Ex. What is the frequency of yellow light? Assume an average wavelength of 600-nm. Is blue light
faster or slower? Which has the smaller wavelength?
Ex. Which has the larger wavelength, a UHF TV signal (e.g. 600-MHz) or yellow light?
It is possible to use various parts of the EM spectrum for imaging: e.g. X-rays, microwaves, infrared
(near), and thermal infrared. Our major interest will be in visible light.

6.7.2 The Visible Spectrum


The visible spectrum stretches from about 400-nm to 700-nm. The reason why this part of the
spectrum is visible is that the rods and cones in our retinas are sensitive to these wavelengths,
and insensitive to the remainder; e.g. if you look at a clothes iron in the dark, you may `feel' the
heat radiated from it, but your eyes will not convert that energy into a light sensation; similarly,
microwaves and X-rays, they may cause damage, but you will not `see' them.
The overall relative spectral sensitivity of human eyes is shown in Figure 6.11, with approximate
indication of corresponding colours. The spectrum of light reaching earth from space, resulting
from the blocking e ects of the earth's atmosphere, looks rather similar.
From Figure 6.11 we can see that the eye is very sensitive to radiation in the green-yellow range
(peak at 550-nm), and relatively insensitive to blue, violet, and deep red; a blue light around 475-nm
(relative sensitivity approx. 10%) would have to put out 10 times more power than the equivalent
green-yellow light. Why did the human evolve this way? Well, the energy emitted by the sun (at
least that part that reaches the earth) has an energy spectrum graph similar to Figure 6.11.
Figure 6.12 (from (Gonzalez & Woods 2002), chapter 6) shows the relative sensitivity of red,
green, and blue cones in the human eye.

6{19
Figure 6.11: Eye overall sensitivity

Figure 6.12: Sensitivity of red, green, and blue cones in the human eye.

6{20
6.7.3 Sensors
A light sensor is likely to have a similar spectral response curve to Figure 6.11, though usually
atter and wider { i.e. more equally sensitive to wavelengths, and sensitive to UV and to near
infrared.
If Figure 6.11 was the spectral response of a sensor, then a blue light (see above), compared to a
green-yellow light of the same power, would produce a sensor output of 10% of the voltage of the
green-yellow.

6.7.4 Spectral Selectivity and Colour


We have already mentioned that a colour sensor (e.g. in a colour TV camera) is merely three
monochrome sensors: one which senses blue, one green, and one red.
What is meant by sensing blue, green, or red? What we do is arrange for the sensor to have an
e ective response curve that is high in green (for example) and low elsewhere. But, we have already
said that sensors have a fairly at curve (maybe 200{1000-nm), so we must arrange somehow to
block out the non-green light.
Wavelength sensitive blocking is done by a colour lter. A green lter allows through green light
but absorbs the other; similarly blue and red. Figure 6.13 shows the transmittivity (relative amount
of light energy allowed to pass through) of a green lter.

Figure 6.13: Green lter transmittivity.


So, we use three separate sensors, each with its own lter (blue, green, and red) located somewhere
between the lens and the sensor.
Considering the e ects of the colour selective lters in front of them, the overall sensitivity of red,
green, and blue sensors would look something like that of human eye cones, see Figure 6.12.

6{21
6.7.5 Spectral Responsivity
The relative response of a sensor can be described as a function of wavelength (forget about (x ; y )
or (r; c ) for the present): d (), where  denotes wavelength.
The light arriving through the lens can also be described as a function of : g (), and the overall
output is found by integration:
Z 1
voltage = d ()g ()d  (6.6)
0

Obviously, the integral can be limited to (say) 100-nm to 1000-nm.


If we have a lter in front of the sensor, relative transmittance (the amount of energy it lets
through), t (), then the light arriving at the sensor, g 0 (), is the product of g () and t ():

g 0 () = g ()t () (6.7)


and the equation changes to:
Z 1
voltage = d ()g ()t ()d  (6.8)
0

or,
Z 1
voltage = d ()g 0 ()d  (6.9)
0

6.7.6 Colour Display


So now we have three images stored in memory; how to display them to produce a proper sensation
of colour?
Similarly to our model of a colour camera as three monochrome cameras, a colour monitor can be
thought of as three monochrome monitors: one which gives out blue light, one green and one red.
A monochrome cathode ray tube display works by using an electron gun to squirt electrons at
a uorescent screen; the more electrons the brighter the image; what controls the amount of
electrons is a voltage that represents brightness, say fv (r; c ).
A monochrome screen is coated uniformly with phosphor that gives out white light { i.e. its energy
spectrum is similar to Figure 6.11
A colour screen is coated with minute spots of colour phosphor: a blue phosphor spot, a green, a
red, a blue, a green, . . . , following the raster pattern mentioned earlier. The green phosphor has
a relative energy output like the curve in Figure; the blue has a curve that peaks in the blue, etc.
There are three electron guns { one controlled by the blue image voltage (say, f (0; r; c )), one by
the green (fg (r; c )) and one by the red (fr (r; c )). Between the guns and the screen, there is an
intricate arrangement called a `shadow-mask' that ensures that electrons from the blue gun reach
only the blue phosphor spots, green ! green spots, etc.

6{22
6.7.7 Additive Colour
If you add approximately equal measures (we are being very casual here, and not mentioning units
of measure) of blue light, green light and red light, you get white light. That's what happens on a
colour screen when you see bright white: each of the blue, green, and red spots are being excited
a lot, and equally. Bring down the level of excitation, but keep them equal, and you get varying
shades of grey.
Your intuition may lead you to think of subtractive colour; lters are subtractive: the more lters,
the darker; combine blue, green and red lters and you get black. However, with additive colour,
the more light added in, the brighter; the more mixture, the closer to grey { and eventually white.

6.7.8 Colour Re ectance


This subsection may be skimmed at the rst reading.
All this brings a new dimension to the discussion of illumination and re ectance in section 6.6.1.
Now we can think of illumination (i ) and re ectance(r ) as functions of  as well as (x ; y ).
Thus, the lightness function is now spectral (and therefore a function of ), i.e.

f (; x ; y ) is the product of two factors:

 i (; x ; y ) { the spectral illumination of the scene, i.e. the amount of light falling on the scene,
at (x ; y ), at wavelength ,
 r (; x ; y ) { the re ectance of the scene, i.e. the ratio of re ected light intensity to incident
light

f (; x ; y ) = i (; x ; y )r (; x ; y ) (6.10)

Why does an object look green (assuming it is being illuminated with white light)? Simply because
its r (; ::) function is high for  in the green region (500-550-nm), and low elsewhere (again, see
Figure 6.13). Of course, illumination comes into the equation: a white card illuminated with green
light (in this case i (; ::) looks like Figure 6.11) will look green, etc.

6.7.9 Exercises
Ex. 1 A coloured card whose re ectivity is r (; x ; y ) is illuminated with coloured light with a
spectrum i () (constant over spatial coordinates (x ; y ); this is sensed with a camera whose
CCD sensor has a responsivity d () (again constant over x ; y ); a lter with transmittance
t () is used. Show that the overall voltage output is
Z
v (x ; y ) = r (; x ; y )i ()t ()d ()d 

Ex. 2 A blue card is illuminated with white light; explain the relative levels of output from a colour
camera for blue, green, red.

6{23
Ex. 3 A blue card is illuminated with red light; explain the relative levels of output from a colour
camera for blue, green, red.
Ex. 4 A blue card is illuminated with blue light; explain the relative levels of output from a colour
camera for blue, green, red. What, if any, will be the change from Ex. 2.5-4 ?
Ex. 5 A white card is illuminated with yellow light; explain the relative levels of output from a
colour camera for blue, green, red.
Ex. 6 A white card is illuminated with both blue and red lights; explain the relative levels of output
from a colour camera for blue, green, red.
Ex. 7 A blue card is illuminated with both blue and red lights; explain the relative levels of output
from a colour camera for blue, green, red; what, if any, will be the change from Ex. 5.

6.8 Cameras and Photographic Film


Many images start o as photographs, so lm cannot be ignored. Realise that:

 just like the eye, lm is limited in the range of illumination that it can handle;
 a camera adapts by opening / closing the lens diaphragm, { or, by increasing or decreasing
exposure time.

6.9 More on Colour Images


We already have seen that colour displays employ red, green, and blue light emitters to create the
sensation of colour. Typically, the computer representation of a colour image involves three `grey
level' images; and normally these correspond to redness, greenness and blueness images.
In a colour camera, you have the opposite of the red, green, and blue light emitters; you have red
sensitive, green sensitive, and blue sensitive light sensors. Often you have three plain light sensors,
one with a red lter in front of it, one with a green lter, and one with a blue lter.
Make sure to remind me to do a demonstration of this in class using a light meter and
camera lters.
The gures that follow are from Gonzalez and Woods (Gonzalez & Woods 2002) p. 317.
Figure 6.14 shows a colour image | a dish of strawberries and a cup of co ee on a cream coloured
table and in a cream coloured dish and cup; at least the image was colour before it was printed on
a monochrome laser printer. I'll let you have full size copies of these so that you can display them
and see more clearly what is meant.
Figure 6.15 shows the red, green, and blue components, i.e. what the red, green, and blue images
would look like if they were displayed as separate monochrome images. Notice that the berries
are quite bright in the red image and the leaves quire bright in the green image; the nearly white
tablecloth and cup and dish are quite bright in red, green, and blue; that is to be expected as white
contains all colours.

6{24
Figure 6.14: Colour image, displayed as colour.

Figure 6.15: Red, green, and blue components of the colour image.

6{25
Next, Figures 6.16 to 6.19 show the cyan, magenta, yellow, and black representations of Fig-
ure 6.14; the black image gives the darkness, i.e. negative of the lightness.
Think colour photography negatives; cyan is sort of the negative of red, magenta the negative of
green, and yellow the negative of blue; and black is the equivalent of a monochrome negative.

Figure 6.16: Cyan components of the colour image.

Figure 6.17: Magenta component of the colour image.

6{26
Figure 6.18: Yellow component of the colour image.

Figure 6.19: Black component of the colour image, i.e. same as a monochrome negative

6{27
Instead of explicit colour components, there is another more useful way of representing colour,
again three components, Hue, Saturation, Intensity (HSI):

 Hue | a coded value which gives the colour;


 Saturation | a coded value which gives the purity of the colour;
 Intensity | a coded value which gives the overall brightness; this is the same as converting
the colour to monochrome.

Figure 6.20 shows the hue, saturation, and intensity image representations of Figure 6.14. Note
that the right hand image in Figure 6.20 (intensity, or lightness) is the negative of Figure 6.19
(darkness).

Figure 6.20: Hue, saturation, and intensity; intensity is the same as a monochrome positive.
HSI, or a variation on it, is used in ordinary television transmission. Of course, when you want to
display on a CRT or LCD, you have to convert back to RGB.
HSI is useful when you want to do data compression. Our eyes are relatively insensitive to colour
variations both spatially and grey level; hence, use high grey level resolution (say 8-bits or 256
levels), and full spatial resolution, for the Intensity part; and use much decreased resolution, e.g.
four bits each in grey level, and maybe half spatial resolution, when transmitting the images or
storing them to le.
Ex. (a) Take a 256  256 colour image, three bytes per colour. How many bytes? (b) using the
scheme mentioned above, i.e. 8-bits for Intensity | how many bytes for the Intensity component?
(c) using the scheme mentioned above for Hue, i.e. 4-bits per pixel and spatial resolution halved
to yield an N  M image; what are N and M? how many bytes for the Hue component? (d) repeat
(c) for Saturation; (e) how many bytes for the HSI image? (f) what percentage is the answer to
(e) of the answer to (a)?
There is a variation on HSI called YIQ that is used by the JPEG image format.

6{28
Chapter 7

Introduction to 2D Graphics

7.1 Introduction
This a an introduction to programming two-dimensional graphics in Java; it is almost entirely based
on Horstmann (Horstmann 2005) Chapter 5 and these notes are based on his HTML slides for the
chapter (available on the web).
What we will do:

 display graphical shapes such as lines, rectangles and ellipses;


 show how to use colours;
 create composite drawing based on the combination of many shapes;
 show how to use Java Swing Frames (windows).

7.2 JFrame Windows


We use the JFrame class. The rst thing to be done is create a window (JFrame) that we can
attach our drawings to.
The resulting frame / window is shown in Figure 7.2.

Self assessment questions

1. How do you display a square frame with a title bar that reads "Hello, World!";
2. Change the program to display two frames at once.

7{1
import javax.swing.*; // Java eXtension, Swing, JFC

public class EmptyFrameViewer{


public static void main(String[] args){
JFrame frame = new JFrame(); // create new JFrame

final int FRAME_WIDTH = 300;


final int FRAME_HEIGHT = 400;

frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setTitle("An Empty Frame");
// what to do with program when someone kills the window
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

// must set it to visible; easy mistake -- where's my window gone?


frame.setVisible(true);
}
}

Figure 7.1: Creating and empty window

Figure 7.2: Empty Frame.

7{2
Answers

1. frame.setSize(300, 300);
frame.setTitle("Hello, World!");
2. public class EmptyFrameViewer2{
public static void main(String[] args){
JFrame frame1 = new JFrame();
JFrame frame2 = new JFrame();

final int FRAME_WIDTH = 300;


final int FRAME_HEIGHT = 400;

frame1.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame2.setSize(400, 100);
frame2.setLocation(250, 150); // location on the screen

frame1.setTitle("An Empty Frame 1");


frame2.setTitle("An Empty Frame 2");

frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame1.setVisible(true);
frame2.setVisible(true);
}
}

7.3 Drawing Shapes


Figure 7.3 shows code that (i) creates a JFrame, (ii) creates a JComponent and attaches the
JComponent onto the JFrame.
That's how we'll do our graphics programming, we'll put the graphics code in a class that
extends JComponent.

Dissection of RectangleComponent

1. RectangleComponent extends JComponent. What that means is that there is a class called
JComponent that the nice people at Sun have already written. JComponent has all of the
code necessary to enable it to be attached (added) to a JFrame and all of the code necessary
to allow it to act as a viewable component. All it is missing is the code to draw the stu in
it; this is provided by paintComponent(Graphics g).
Think of a JComponent as an MP3 player that you can buy in a shop. When you
get it, it has no music; you supply the music, just like you supply the drawings in
paintComponent(Graphics g).

7{3
2. RectangleComponent extends JComponent is called inheritance; you'll cover inheritance
at some stage in your Software Design series of modules. At present, it is enough to think of
inheritance as we have just explained: the JComponent code has almost everything needed
to be a component on a JFrame, we just have to provide the drawing part.
3. Here's another way of thinking about extends; you come in to the college as Person | you
can walk, talk, . . . , do all the things that Persons can do. You have a blank place somewhere
in your head called program-a-computer; it's blank to start o with | if we ask you to
program-a-computer, nothing happens. The purpose of this course is to put something useful
in that program-a-computer space. We could say Computer Programmer extends Person,
i.e. a Computer Programmer is a Person with something in the program-a-computer space.
4. Later we will come across extends in a games situation; we'll have a Sprite class whose
objects can move about the screen and provide an animation. But when we come to develop-
ing a game, we'll want to have a Creature class, class Creature extends Sprite; then
class Player extends Creature, class Fly extends Creature, all the time lling in
more details | or replacing details.
5. Whenever a JFrame is made visible, and JComponent that is attached to it executes its
paintComponent method and the drawings appear.
6. paintComponent(Graphics g) takes a parameter Graphics g; Graphics g is provided by
Java Swing.
7. Graphics g is an object which represents the graphics context.
8. Actually, instead Graphics g, the current version of Swing passes a Graphics2D ob-
ject; Graphics2D objects can do a lot more than old style Graphics objects, but
for backwards compatibility we have paintComponent(Graphics g). However, because
Graphics2D extends Graphics we can relabel Graphics g to Graphics2D g2.
Think of the latter as you visiting a local software company; you are a Person. But because
the company knows where you came from, they say \are you a Computer-Programmer?";
you say \yes", so they say \sit down there and write us some programs". You entered as
a Person, but they recognised you as a Computer-Programmer and they relabelled you as
Computer-Programmer.

Graphics2D g2 = (Graphics2D) g;

Person p;
Computer-Programmer cp = (Computer-Programmer) p;

Of course, you are still a Person, also | you have all the attributes of a Person, but, in
addition, your program-computers space has been lled.
9. The following constructs a Rectangle2D.Double object whose top-left corner is at x =
60; y = 70 and whose width (x ) is 40 and height (y ) is 50.

// Construct a rectangle and draw it


Rectangle2D.Double r1 =
new Rectangle2D.Double(60.0, 70.0, 40.0, 50.0);

7{4
10. Then we set the drawing Color g2.setPaint(Color.red); Had we not done that, the
Color would have been black, the default. g2.setPaint is the same as g2.setColor,
which you may notice is some programs.
11. And nally we draw it onto g2

g2.draw(r1);

12. Now we make another Rectangle and this time we draw it lled.

Rectangle2D.Double r2 =
new Rectangle2D.Double(160.0, 170.0, 50.0, 80.0);
g2.setPaint(Color.blue);
g2.fill(r2);

13. We have introduced two innovations in RectangleViewer.java:


(i) We use the following to automatically get the name of the class (program) and use it as
title. This is handy, because it is easy to put useless nonesense or confusing nonesense
in the title. Whe we look at Figure 7.4 we can be sure that we know which program
produced it
// objects can get the name of their class
frame.setTitle(getClass().getName());

(ii) We go even more object oriented by putting all the active code in the construc-
tor of RectangleViewer. Then all that is in main is an instruction to construct a
RectangleViewer; the act of construction executes the relevant code.

7.4 Y-axis pointing down?


As you can see in RectangleViewer.java, in this graphics system, the y-axis points downwards. In
keeping with this, position coordinates for, e.g., Rectangle2D.Double objects refer to the top-left
corner.
Y-axis pointing downwards is a throwback to how raster scanning works and how coordinates are
treated in the graphics card itself. We stick with for this chapter, but because it is a major PITA
and because old people like me have diculty thinking about things that are upside down, we will
devise a better graphics framework for the next chapter in which the y-axis does in fact point up!

7{5
import javax.swing.JFrame;
import java.awt.Graphics;import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D; import java.awt.Color;
import javax.swing.JPanel;import javax.swing.JComponent;

public class RectangleViewer{


public RectangleViewer(){
JFrame frame = new JFrame();

final int FRAME_WIDTH = 300;


final int FRAME_HEIGHT = 400;

frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);

// objects can get the name of their class


frame.setTitle(getClass().getName());

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

RectangleComponent component = new RectangleComponent();


frame.add(component);
frame.setVisible(true);
}
public static void main(String args[]){
new RectangleViewer();
}

/**
A component that draws two rectangles.
*/
class RectangleComponent extends JComponent{
public void paintComponent(Graphics g){
// Recover Graphics2D
Graphics2D g2 = (Graphics2D) g;

// Construct a rectangle and draw it


Rectangle2D.Double r1 =
new Rectangle2D.Double(60.0, 70.0, 40.0, 50.0);
g2.setPaint(Color.red);
g2.draw(r1);

// Construct another rectangle and fill it


Rectangle2D.Double r2 =
new Rectangle2D.Double(160.0, 170.0, 50.0, 80.0);
g2.setPaint(Color.blue);
g2.fill(r2);
}
}

7{6
Figure 7.3: Drawing shapes in a JComponent.
Figure 7.4: Drawing shapes in a JComponent.

7{7
7.5 Drawing with Di erent Shapes
The simple car drawings in Figure 7.5 are produced by the code in Figures 7.6 and 7.7.

Figure 7.5: Drawings of a car.

7{8
import javax.swing.JFrame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import java.awt.Rectangle;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;

public class CarViewer{


public CarViewer(){
JFrame frame = new JFrame();

final int FRAME_WIDTH = 300;


final int FRAME_HEIGHT = 400;

frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setTitle(getClass().getName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

CarComponent component = new CarComponent();


frame.add(component);
frame.setVisible(true);
}
public static void main(String args[]){
new CarViewer();
}
}

/**
This component draws two car shapes.
*/
class CarComponent extends JComponent{
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
// position at top left corner
Car car1 = new Car(0, 0);

// position at bottom right corner


int x = getWidth() - Car.WIDTH;
int y = getHeight() - Car.HEIGHT;
Car car2 = new Car(x, y);

car1.draw(g2);
car2.draw(g2);
}
} ... continued

Figure 7.6: Code for car drawings, part 1

7{9
/**
A car shape that can be positioned anywhere on the screen.
*/
class Car{
/**
Constructs a car with a given top left corner
@param x the x coordinate of the top left corner
@param y the y coordinate of the top left corner
*/
public Car(int x, int y){
xLeft = x; yTop = y;
}

/**
Draws the car.
@param g2 the graphics context
*/
public void draw(Graphics2D g2){
Rectangle body
= new Rectangle(xLeft, yTop + 10, 60, 10);
Ellipse2D.Double frontTire
= new Ellipse2D.Double(xLeft + 10, yTop + 20, 10, 10);
Ellipse2D.Double rearTire
= new Ellipse2D.Double(xLeft + 40, yTop + 20, 10, 10);

// The bottom of the front windshield


Point2D.Double r1
= new Point2D.Double(xLeft + 10, yTop + 10);
// The front of the roof
Point2D.Double r2
= new Point2D.Double(xLeft + 20, yTop);
// The rear of the roof
Point2D.Double r3
= new Point2D.Double(xLeft + 40, yTop);
// The bottom of the rear windshield
Point2D.Double r4
= new Point2D.Double(xLeft + 50, yTop + 10);

Line2D.Double frontWindshield = new Line2D.Double(r1, r2);


Line2D.Double roofTop = new Line2D.Double(r2, r3);
Line2D.Double rearWindshield = new Line2D.Double(r3, r4);

g2.draw(body); g2.draw(frontTire); g2.draw(rearTire);


g2.draw(frontWindshield); g2.draw(roofTop); g2.draw(rearWindshield);
}

public static int WIDTH = 60; public static int HEIGHT = 30;
private int xLeft; private int yTop;
}

Figure 7.7: Code for car drawings, part 2.


7{10
Dissection of CarViewer.java We mention just a few novelties in CarViewer.java

1. Here, in order to draw the second car at the bottom left hand corner of the JComponent,
we need to nd out where that is. getWidth() and getHeight() allow us to ask the
JComponent its dimensions.

int x = getWidth() - Car.WIDTH;


int y = getHeight() - Car.HEIGHT;

2. We now have Line, Point and Ellipse classes.


Ellipse2D.Double rearTire
= new Ellipse2D.Double(xLeft + 40, yTop + 20, 10, 10);

// The bottom of the front windshield


Point2D.Double r1
= new Point2D.Double(xLeft + 10, yTop + 10);
// The front of the roof
Point2D.Double r2
= new Point2D.Double(xLeft + 20, yTop);

Line2D.Double frontWindshield = new Line2D.Double(r1, r2);

3. Ellipse2D.Double works just like Rectangle2D.Double, the rst two parameters of its
constructor give (x ; y ) of top-left corner, second two parameters give width and height.
4. Point2D.Double needs just position (x ; y ).
5. Line2D.Double's constructor take two Point2D.Double parameters, the beginning and end
points of the line.
6. Actually, there is another Line2D.Double constructor which takes four doubles, (x ; y ) of
line start and (x ; y ) of line end.

7.6 Colour
We have already said plenty about colour in Chapter 6. Refer back to section 6.3.5.

 Any colour can be represented using three components: red, green, blue;
 Java Graphics2D stores red, green, blue as three eight-bit bytes (we should not equate
eight-bits with byte, but nearly everybody does; the proper name is octet, but we'll use byte.
 An eight-bit byte allows numbers in the range [0::255]; 0 means fully o , 255 means colour
fully on.
(r = 255; g = 0; b = 0) is bright red.
(r = 0; g = 255; b = 0) is bright green.
(r = 0; g = 0; b = 0) is black, i.e. no colours at all present; all are fully o .
(r = 255; g = 255; b = 255) is white, i.e. all colours are fully on.

7{11
 There is a fourth attribute that you can specify, namely alpha; alpha is the opacity, or
opaqueness, of the colour. opacity is the opposite to transparency.
al pha = 255 means totally opaque; al pha = 0 means totally transparent.

 Here we repeat from section 6.3.5


 In a Java program the normal way to create a colour is to give the Red, Green, Blue values:

// R G B
Color c = new Color(255, 0, 0); // pure red

// or, you can use 0..1 (float) instead of integer 0..255

Color c = new Color(1.0, 0.0, 0.0); // pure red

 The float constructor is sometimes more intuitive, 1:0 is fully on, 0:0 is fully o , 0:5 is
halfway. However, 1:0 will become 255 in the computer memory, 0:5 will become 128 etc.
The Color will always be store as a byte.
 But you can also specify the opacity (transparency) of the colour. The opacity is called
alpha; so now we have Red, Green, Blue, Alpha values:

// R G B A
Color c = new Color(255, 0, 0, 255); // pure red, completely opaque

Color c = new Color(255, 0, 0, 0); // pure red, completely


transparent

Color c = new Color(255, 0, 0, 50); // is better if you want to see


// the colour against a background

Figure 7.8 is shows some experiments with colour (Color); the paintComponent of the code is
shown Figure 7.9.
You will notice that the last (blue) ellipse is transparent | you can see what is behind it.

r g b a
newColor = new Color(0, 0, 255, 100);

If you change to

r g b a
newColor = new Color(0, 0, 255, 255);

you will have a completely opaque blue, and you will not see behind it.
Notice that you can use drawString to write text on the graphics.

7{12
Figure 7.8: Use of Color, including alpha.

7{13
public void paintComponent(Graphics gr){
Graphics2D g2 = (Graphics2D) gr;

// Select color into graphics context


g2.setColor(fillColor);

int xLen = getWidth()/ 8;


int yLen = getHeight()/8;
int x = (getWidth() - xLen) / 8;
int y = (getHeight() - yLen) / 8;
Rectangle2D.Double sq = new
Rectangle2D.Double(x, y, xLen, yLen);
g2.fill(sq);

int r = fillColor.getRed();
int g = fillColor.getGreen();
int b = fillColor.getBlue();
int a = fillColor.getAlpha();
String s = "rgba = (" + r + ", "+ g + ", "+ b + ", "+ a+ ")";

g2.drawString(s, x, y-5);

Color newColor = Color.red;


int yTrans = yLen + 25;
y = y + yTrans;
Rectangle2D.Double sq2 = new
Rectangle2D.Double(x, y, xLen, yLen);

g2.setColor(newColor);
g2.fill(sq2);
r = newColor.getRed(); g = newColor.getGreen();
b = newColor.getBlue(); a = newColor.getAlpha();
s = "rgba = (" + r + ", "+ g + ", "+ b + ", "+ a+ ")";
g2.drawString(s, x, y -5);

y = y + 30; // again
newColor = new Color(0, 0, 255, 100);
g2.setColor(newColor);
Ellipse2D.Double e = new Ellipse2D.Double(x, y, xLen+20, yLen);
g2.fill(e);
r = newColor.getRed(); g = newColor.getGreen();
b = newColor.getBlue(); a = newColor.getAlpha();
s = "rgba = (" + r + ", "+ g + ", "+ b + ", "+ a+ ")";
y = y + yTrans; // keeping track of y
g2.drawString(s, x, y -5);
}

Figure 7.9: Experiments with colour

7{14
Chapter 8

More 2D Graphics

8.1 Introduction
Following the introduction in Chapter 7, we introduce a framework which has labelled axes and in
which the y-axis points up. We then show some graphics developed in that framework. In the next
chapter, Chapter 9 we do aome programming involving vectors and vector transformations using
the same framework.

8.2 The Framework


Figure 8.1 shows our framework with annotated axes and grid. The code, see Figure 8.2 has the
usual main class together with a JComponent class; nothing new here. The real work is done in a
separate class called Axes, see Figures 8.3 and 8.4.
Obviously, when you are normally creating graphics, you will not need axes and grid, but when you
are learning, the axes will be a big help.

Flipping the y-axis We ip the y-axis so that it points up using the AffineTransform class. As
well as ipping the y-axis, we also want the origin to be in the middle of the frame.
First, we create an AffineTransform, then we add a translate (move) to that to get us to the
proper origin.
Then we apply a scale of 1 to the y-axis | this does the ipping.
Finally, we apply the transformation to the graphics context.
Here is the code:

AffineTransform at = new AffineTransform();


//winHeight (Frame height) is to allow us to think 'upside down'
// in y for a moment, but only until we scale(1, -1);
at.translate(x0, winHeight - y0); // shift origin to (x0, y0)
at.scale(1, -1); // and flip y axis to point up
g2.transform(at); // apply transform to graphics environment

8{1
We note that me must un ip any time we want to draw text, otherwise the text will be upside
down.
All this is a little bit messy, but once we have the framework created, you need not worry about it
and from then on you can think of the y-axis pointing up. Also, the beginning points for Rectangles
and Ellipses is no longer the top-left, but is now the bottom-right.

Figure 8.1: Graphics framework.

8{2
import java.awt.*; import java.awt.geom.*; import javax.swing.*;

public class Framework2D{


public Framework2D(){
JFrame frame = new JFrame();

final int FRAME_WIDTH = 800;


final int FRAME_HEIGHT = 800;

frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
frame.setTitle(getClass().getName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

Container cp = frame.getContentPane();
cp.setBackground(Color.white);

Axes theAxes = new Axes(


-380, 380, -380, 380, 380, 380, FRAME_HEIGHT);
AxesCompt component = new AxesCompt(theAxes);

frame.add(component);
frame.setVisible(true);
}
public static void main(String args[]){
new Framework2D();
}

}
/**
A component that draws axes and content.
*/
class AxesCompt extends JComponent{
private Axes axes;

public AxesCompt(Axes anAxes){


axes = anAxes;
}

public void paintComponent(Graphics g){


Graphics2D g2 = (Graphics2D) g;
axes.draw(Color.darkGray, g2);
// put your drawing code here
}
}

Figure 8.2: Framework code

8{3
class Axes{
private int xLo; private int xHi;
private int yLo; private int yHi;
private int x0; private int y0;
private int winHeight;

public Axes(int xl, int xh, int yl, int yh, int xx0, int yy0, int wh){
xLo= xl; xHi= xh; yLo= yl; yHi= yh;
x0= xx0; y0= yy0;
winHeight = wh;
}

private Color lightenColor(Color c, int a){


int red = c.getRed(), green = c.getGreen(), blue = c.getBlue();
red = add(red, a, 255);
green = add(green, a, 255);
blue = add(blue, a, 255);
Color c1 = new Color(red, green, blue);
return c1;
}

private int add(int x, int a, int limit){


int xx = x + a;
if(xx< 0) xx = 0;
if(xx> limit) xx = limit;
return xx;
}

public void draw(Color c, Graphics2D g2){


Color cPrev = g2.getColor();
RenderingHints hPrev = g2.getRenderingHints();
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
RenderingHints hintsText = new RenderingHints(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2.setRenderingHints(hintsText);
//make a translating and flipping transform
AffineTransform at = new AffineTransform();
//winHeight (Frame height) is to allow us to think 'upside down'
// in y for a moment, but only until we scale(1, -1);
at.translate(x0, winHeight - y0); // shift origin to (x0, y0)
at.scale(1, -1); // and flip y axis to point up
g2.transform(at); // apply transform to graphics environment
// continued ...

Figure 8.3: Axes, part 1

8{4
int lineStep = 10, arrowLen= 6;
Color cDark = lightenColor(c, 60);
Color cMid = lightenColor(cDark, 60);
Color cLight = lightenColor(cMid, 60);

for (int x = xLo; x < xHi ; x += lineStep) {


if(x%100 == 0) g2.setPaint(cDark);
else if(x%50 == 0) g2.setPaint(cMid);
else g2.setPaint(cLight);
Line2D line = new Line2D.Double(x, yLo, x, yHi); g2.draw(line);
}
for (int y = yLo; y < yHi ; y += lineStep) {
if(y%100 == 0) g2.setPaint(cDark);
else if(y%50 == 0) g2.setPaint(cMid);
else g2.setPaint(cLight);
Line2D line = new Line2D.Double(xLo, y, xHi, y); g2.draw(line);
}
// Main axes.
g2.setPaint(c);
Line2D xAxis = new Line2D.Double(xLo, 0.0, xHi, 0.0);
g2.draw(xAxis);
Line2D yAxis = new Line2D.Double(0.0, yLo, 0.0, yHi);
g2.draw(yAxis);
// Arrowheads.
Line2D a1 = new Line2D.Double(xHi - arrowLen, -arrowLen, xHi, 0); g2.draw(a1);
Line2D a2 = new Line2D.Double(xHi, 0, xHi - arrowLen, arrowLen); g2.draw(a2);
a1 = new Line2D.Double(-arrowLen, yHi - arrowLen, 0, yHi); g2.draw(a1);
a2 = new Line2D.Double(0, yHi, arrowLen, yHi - arrowLen); g2.draw(a2);

// must unflip so that text not upside down


at= new AffineTransform(); at.scale(1, -1); g2.transform(at);

g2.drawString("X", xHi-20, 20);


g2.drawString("100", 100, 20); g2.drawString("200", 200, 20);
g2.drawString("300", 300, 20); g2.drawString("-100", -100, 20);
g2.drawString("-200", -200, 20); g2.drawString("-300", -300, 20);

g2.drawString("Y", -20, - (yHi-20));


g2.drawString("100", -40, -100); g2.drawString("200", -40, -200);
g2.drawString("300", -40, -300); g2.drawString("-100", -40, 100);
g2.drawString("-200", -40, 200); g2.drawString("-300", -40, 300);
// go back to y pointing up
// we have to assume that the client program requires
// the environment to remain in the flipped state
g2.transform(at);
// otherwise, leave graphics context the way we found it
g2.setPaint(cPrev); g2.setRenderingHints(hPrev);
}

Figure 8.4: Axes, part 2


8{5
8.3 Some Graphics Using the Framework
Figure 8.5 shows four di erently coloured rectangles. Figure 8.6 shows the relevant parts of the
code.

Figure 8.5: Four rectangles.

8{6
public class Rects2D{
public Rects2D(){
// same as FrameWork2D
frame.add(component);
frame.setVisible(true);
}
public static void main(String args[]){
new Rects2D();
}

}
/**
A component that draws axes and content.
*/
class AxesCompt extends JComponent{
private Axes axes;

public AxesCompt(Axes anAxes){


axes = anAxes;
}

public void paintComponent(Graphics g){


Graphics2D g2 = (Graphics2D) g;
axes.draw(Color.darkGray, g2);

Rectangle2D.Double r1 =
new Rectangle2D.Double(-250, -200, 100, 150);
g2.setPaint(Color.red);
g2.fill(r1);

Rectangle2D.Double r2 =
new Rectangle2D.Double(-250, 200, 100, 150);
g2.setPaint(Color.green);
g2.fill(r2);

Rectangle2D.Double r3 =
new Rectangle2D.Double(250, 200, 100, 150);
g2.setPaint(Color.blue);
g2.fill(r3);

Rectangle2D.Double r4 =
new Rectangle2D.Double(250, -200, 100, 150);
g2.setPaint(Color.orange);
g2.fill(r4);
}
}

Figure 8.6: Four rectangles.

8{7
Figure 8.7 shows an amended version of the Car program that we saw in Chapter 7. Figures 8.8
and 8.9 show the relevant parts of the code.

Figure 8.7: Car drawn in the axes framework.

8{8
/**
A car shape that can be positioned anywhere on the screen.
*/
public class Car{
/**
Constructs a car with a given top left corner
@param x the x coordinate of the bottom left corner
@param y the y coordinate of the bottom left corner
@param h full height
@param w full width
*/
public Car(double x, double y, double w, double h){
x0 = x;
y0 = y;
width = w;
height = h;
}

private Ellipse2D createCircle(Point2D c, double r){


return new Ellipse2D.Double(
c.getX() - r, c.getY() - r, r*2.0, r*2.0);
}

/**
Draws the car.
@param g2 the graphics context
*/
public void draw(Graphics2D g2){
//System.out.println("Car.draw");
double wheelRadius = height/5.0;
double xFront = x0 + width;
double yBodyBase = y0 + height/5.0;
double hBody = height*2.0/5.0;
double hRoof = height*2.0/5.0;

Point2D.Double centreRearWheel =
new Point2D.Double(x0 + 2.0*wheelRadius, yBodyBase);
Ellipse2D rearWheel =
createCircle(centreRearWheel, wheelRadius);
g2.setPaint(Color.darkGray);
g2.fill(rearWheel);

Point2D.Double centreFrontWheel =
new Point2D.Double(xFront - 2.0*wheelRadius, yBodyBase);
Ellipse2D frontWheel =
createCircle(centreFrontWheel, wheelRadius);
g2.fill(frontWheel);

Figure 8.8: Code for car drawings, part 1

8{9
Rectangle.Double body =
new Rectangle.Double(x0, yBodyBase, width, hBody);
g2.setPaint(Color.red); g2.fill(body);

double wBonnet = width/4.0;


double wBoot = width/5.0;
double wFrontWS = width/6.0;
double wRearWS = width/5.0;

double yBonnet = yBodyBase + hBody;


double yBoot = yBonnet;
double yRoof = y0 + height;
double xBonnet = xFront - wBonnet;
double xBoot = x0 + wBoot;
double xRoofFront = xBonnet - wFrontWS;
double xRoofRear = x0 + wRearWS+ wBoot;
// The bottom of the front windshield
Point2D.Double p1
= new Point2D.Double(xBonnet, yBonnet);
// The front of the roof
Point2D.Double p2
= new Point2D.Double(xRoofFront , yRoof);
// The bottom of the rear windshield
Point2D.Double p3
= new Point2D.Double(xBoot, yBoot);
// The rear of the roof
Point2D.Double p4
= new Point2D.Double(xRoofRear, yRoof);

Line2D.Double frontWindshield
= new Line2D.Double(p1, p2);
Line2D.Double roofTop
= new Line2D.Double(p2, p4);
Line2D.Double rearWindshield
= new Line2D.Double(p4, p3);

g2.draw(frontWindshield);
g2.draw(roofTop);
g2.draw(rearWindshield);
}

private double width;


private double height;
private double x0;
private double y0;
}

Figure 8.9: Car Code

8{10
8.4 Shape and General Path
The way we drew the roof of the car above is alright as long as we only want to draw the outline
of the roof. What if we want to ll the area above the body, with, say, a tinted glass colour?
That is where Shape and GeneralPath come in. You can creat a path of line drawing and the ll
it.
Figure 8.10 shows an amended version of the above Car program that; unfortunately, moveTo and
lineTo take float arguments, so me had to change some doubles to floats.
Figures 8.11 shows the relevant parts of the code.

Figure 8.10: Car drawn partly with GeneralPath.

8{11
// same as above

float yBonnet = yBodyBase + hBody;


float yBoot = yBonnet;
float yRoof = y0 + height;
float xBonnet = xFront - wBonnet;
float xBoot = x0 + wBoot;
float xRoofFront = xBonnet - wFrontWS;
float xRoofRear = x0 + wRearWS+ wBoot;

GeneralPath roof = new GeneralPath();


// The bottom of the front windshield
roof.moveTo(xBonnet, yBonnet);
// The front of the roof
roof.lineTo(xRoofFront , yRoof);
// The rear of the roof
roof.lineTo(xRoofRear, yRoof);
// The bottom of the rear windshield
roof.lineTo(xBoot, yBoot);
// and back to the beginning
roof.lineTo(xBonnet, yBonnet);
g2.draw(roof);
Color cBlue = new Color(0, 0, 255, 50); // transparent blue
g2.setPaint(cBlue);
g2.fill(roof);
}

Figure 8.11: Car drawn partly with GeneralPath.

8{12
8.5 Drawing Images
In our tile game, we will have to draw images of the tiles and of sprites.
Figure 8.12 displays all the tiles and sprites from Chapter 5 of Brackeen's book.
Figure 8.13 shows how we load the tiles images into an ArrayList<Image> tiles and compute
the position of the tile and place it in an ArrayList<Point2D> pts.
Figure 8.14 shows how we display the list of tiles at their relevant positions.

Figure 8.12: Displaying tile and sprite images.

8{13
/**
A component that draws axes and displays tiles
*/
class TileCompt extends JComponent{
private Axes axes;
private ArrayList<Image> tiles;
private ArrayList<Point2D> pts;

public TileCompt(Axes anAxes){


axes = anAxes;
tiles = new ArrayList<Image>();
pts = new ArrayList<Point2D>();

String[] files = {
"tile_A.png", "tile_B.png", "tile_C.png", "tile_D.png",
"tile_E.png", "tile_F.png", "tile_G.png", "tile_H.png",
"tile_I.png",
"star1.png", "star2.png", "star3.png", "star4.png",
"powerup1.png", "powerup2.png", "powerup3.png", "powerup4.png",
"player1.png", "player2.png", "player3.png",
"music1.png", "music2.png", "music3.png",
"heart1.png", "heart2.png", "heart3.png",
"fly1.png", "fly2.png", "fly3.png",
"grub1.png", "grub2.png"};

int x= axes.xLo+10, y= axes.yLo+10;


int xwd = 66, yht = 66;
for(String s : files){
Image tile = loadImage("images/"+s); // images directory
tiles.add(tile); // tile image
pts.add(new Point2D.Double(x, y)); // poistion
x+= xwd; // position ready for next tile
if(x+xwd > axes.xHi){
x= axes.xLo;
y+= yht;
}
}
}

private Image loadImage(String fileName) {


return new ImageIcon(fileName).getImage();
}

Figure 8.13: Displaying tile and sprite images, part 1

8{14
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
axes.draw(Color.darkGray, g2);
Point2D pt;
int x, y;
AffineTransform at = new AffineTransform();
at.scale(1.0, -1.0);
g2.transform(at);
for(int i = 0; i< tiles.size(); ++i){
Image im = tiles.get(i);
pt= pts.get(i);
x= (int)pt.getX();
y= (int)pt.getY();
System.out.println("x, y = " + x +", "+ y);
g2.drawImage(im, x, y, null);
Rectangle rect = new Rectangle(x, y, 2, 2);
g2.setPaint(Color.black);
g2.draw(rect);
}
g2.transform(at);
}
}

Figure 8.14: Displaying tile and sprite images, part 2

8{15
Chapter 9

Vectors and Vector transformations

9.1 Introduction
Following the introductions in Chapters 7 and 8 we now explore the role of vectors vector trans-
formations in computer graphics. All this is covered in more detail in (Campbell 2007b), but here
we look at it from a programming point of view.
We rst examine the programming of vectors vector transformations using our own Vector2D and
Transform2D software. Then, we introduce Java's AffineTransform class.
One aspect of vectors and points that we attempt to highlight in this section is that points and
vectors are di erent entities; what can be confusing is that they are similar enough for there to be
situations where they can be used interchangeably. That makes it all the more confusing when we
arrive at situations where their di erence is important.
As we indicate in Chapter 5.4 of our Maths. notes (Campbell 2007b), in the context of computer
graphics, the distinctions are as follows.

9{1
Points and Vectors

 A point is a position;
 A vector is a displacement (description of a movement from one position to another);
 The latter is the same as saying that a vector has magnitude and direction;
 If we are working in a Cartesian (rectangular) coordinate system, the origin (O) is a point;
 The potential interchangeability of points and vectors comes from the fact that a general
point (P ) at (x ; y ) may be thought of as a displacement from O, i.e. we add a vector
v = (x ; y ) to O = (0; 0) and get a new point P = (x ; y );
You can add a vector to a point to get a new point; that is, move from the rst point to
the new point;
 We note also that vectors have no position, but when we want to display a vector, we need to
position it somewhere; if we draw the vector as an arrow on a rectangular coordinate system,
we sometimes place its start (tail) at the origin O;
 When we rotate a vector, all that changes is its direction | its angle;
 Quite often, it is useful to represent a vector not as v = (x ; y ), but as magnitude (also called
length), for which we use kvk and angle, in mathematics, called its argument arg v;
 The latter representation is p
called polar: v = (r; ), where r = kvk and  = arg v, see
Figure 9.1. Note that kvk = x 2 + y 2 .

Figure 9.1: Polar representation of a vector.

9{2
9.2 Vector Addition
Figure 9.2 shows a graphical demonstration of vector addition. Figure 9.3 shows the relevant part
of the code.

Figure 9.2: Graphical display of vector addition.

9.3 Vector Rotation


Figure 9.4 shows a graphical demonstration of vector addition. Figure 9.5 shows the relevant part
of the code.

9{3
private class AxesCompt extends JComponent{
private Axes axes;

public AxesCompt(Axes anAxes){


axes = anAxes;
}

public void paintComponent(Graphics g){


Graphics2D g2 = (Graphics2D) g;
axes.draw(Color.darkGray, g2);

double ang1 = Math.toRadians(30.0);


double len1 = 200.0;
Vector2D v1 = Vector2D.fromLengthAngle(len1, ang1);
Point2D p0 = new Point2D.Double(0.0, 0.0);
v1.draw(p0, Color.black, g2, "v1 = "+v1);

double ang2 = Math.toRadians(75.0);


double len2 = 120.0;
Vector2D v2 = Vector2D.fromLengthAngle(len2, ang2);
v2.draw(p0, Color.black, g2, "v2 = "+v2, -120, 0);

Point2D p1 = Vector2D.add(p0, v1);


v2.draw(p1, Color.black, g2, "v2 = "+v2);

Vector2D v3 = Vector2D.add(v1, v2);


v3.draw(p0, Color.red, g2, "v3 = v1 + v2 = "+ v3, 0, 20);
}
}

Figure 9.3: Graphical display of vector addition.

9{4
Figure 9.4: Graphical display of vector rotation.

9{5
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
axes.draw(Color.darkGray, g2);

Vector2D v1 = new Vector2D(173.2, 100.0);


Point2D p0 = new Point2D.Double(0.0, 0.0);

// make some different colours and store them


ArrayList<Color> cc = new ArrayList<Color>();
cc.add(Color.black);
cc.add(Color.darkGray); cc.add(Color.gray); cc.add(Color.lightGray);
cc.add(Color.red); cc.add(Color.pink); cc.add(Color.orange);
cc.add(Color.yellow); cc.add(Color.green); cc.add(Color.magenta);
cc.add(Color.cyan); cc.add(Color.blue);
cc.add(new Color(0, 0, 128));

Transform2D tr = new Transform2D();


tr.setRot(Math.toRadians(30.0));
Transform2D ts = new Transform2D();
//ts.setScale(1.05, 1.05); // can be used to enlarge vector as rotates
Transform2D t = tr.compose(ts);
System.out.println("t = " + t);
Vector2D v = v1; // start with original

for(int i = 0; i < 13; ++i){


if(i == 12)v.draw(p0, cc.get(i), g2, "v"+ i + " = "+v, 0, 20);
else v.draw(p0, cc.get(i), g2, "v"+ i + " = "+v);
System.out.println("v = "+ v.toStringPolar());
v = t.transform(v); // apply another transform
}
}
}

Figure 9.5: Graphical display of vector addition.

9{6
When we print the transform and the polar version of the rotated vector, we get get following:

t = [( 0.866, -0.500, 0.000)


( 0.500, 0.866, 0.000)]

v = [200.0, 30.0]
v = [200.0, 60.0]
v = [200.0, 90.0]
v = [200.0, 120.0]
v = [200.0, 150.0]
v = [200.0, 180.0]
v = [200.0, 210.0]
v = [200.0, 240.0]
v = [200.0, 270.0]
v = [200.0, 300.0]
v = [200.0, 330.0]
v = [200.0, 0.0]
v = [200.0, 30.0]

Notice that the length (magnitude) of the vector does not change, only its angle. Notice also that
when we rotate the vector by 30 twelve (12) times, we arrive back at the original. Why?.
Ex. Explain the contents of the matrix in the transform t . What are 0:866, 0:500, 0:500?

9.3.1 Homogeneous Coordinates


When you print an Transform2D object, you get two rows of a matrix. This is because
Transform2D uses homogeneous coordinates as described in Chapter 6.5 of our Maths. notes
(Campbell 2007b).
Recalling eqns. 6.3 and 6.4 of Chapter 6.5 of the Maths. notes, to use homogeneous coordinates,
we extend [vx vy ]t with a third coordinate vw ,

vx
 
vx
!
 

vy
 vy  : (9.1)
vw

In homogeneous coordinates, we can include a translation vector [tx ty ]t in the 3  3 homogeneous


ane transformation matrix so that all transformations can now be implemented as a matrix-vector
multiplication:

vx a11 a12 tx ux
    
 vy  =  a21 a22 ty   uy  : (9.2)
vw 0 0 1 uw

Since the bottom row of the ane transformation matrix is always (0 0 1), we do not bother to
print it.

9{7
9.4 Rotating Shapes
Figure 9.6 shows a graphical demonstration of rotation of a house shape. Figure 9.8 shows the
relevant part of the code and Figure 9.9 shows the code that creates the key points that make up
the house. To meke it clearer, Figure 9.7 shows the house without the vectors.
I hope that displaying the points as vectors will help you see why rotation has the e ect it has; the
vector displacements from the origin are rotated, this means that all rotation is wit respect to the
origin | the house does not rotate about its centre or any other part of itself.
If you want the house to rotate about some other point, you have to translate (move) the house
to that point, rotate, and then translate back. However, there mathematics allows us to do all
three in one operation, see (Campbell 2007b) and we will do a programming exercise on this.

Figure 9.6: Rotation of a house shape, showing the vector rotation.

9{8
Figure 9.7: Rotation of a house shape.

9{9
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.setBackground(Color.white);
axes.draw(Color.darkGray, g2);
double x0 = 50.0, y0 = 50.0, width = 200.0,
wallHt = 100.0, roofHt= 200.0;
VectorHouse aHouse =
new VectorHouse(x0, y0, width, wallHt, roofHt);
aHouse.drawAsPoints(g2, Color.green);
aHouse.drawAsLines(g2, Color.red);
aHouse.drawAsVectors(g2, Color.darkGray);

aHouse.rotate(Math.toRadians(60.0));
aHouse.drawAsPoints(g2, Color.green);
aHouse.drawAsLines(g2, Color.black);
aHouse.drawAsVectors(g2, Color.darkGray);

aHouse.rotate(Math.toRadians(90.0));
aHouse.drawAsPoints(g2, Color.green);
aHouse.drawAsLines(g2, Color.black);
aHouse.drawAsVectors(g2, Color.darkGray);

aHouse.rotate(Math.toRadians(120.0));
aHouse.drawAsPoints(g2, Color.green);
aHouse.drawAsLines(g2, Color.black);
aHouse.drawAsVectors(g2, Color.darkGray);
}

Figure 9.8: Graphical display of rotation of a house shape.

9{10
public VectorHouse(double x0, double y0, double width,
double wallHt, double roofHt) {
v0 = new Vector2D(x0, y0);
vecs = new ArrayList<Vector2D>();

//left wall, ...


vecs.add(new Vector2D(0.0, 0.0)); // bottom left-hand corner
vecs.add(new Vector2D(0.0, wallHt)); // top of left wall
vecs.add(new Vector2D(width/2, roofHt)); // roof apex
vecs.add(new Vector2D(width, wallHt)); // top of right wall
vecs.add(new Vector2D(0.0, wallHt)); // top of left wall
// below duplication, but make drawing easier
vecs.add(new Vector2D(width, wallHt)); // top of right wall
vecs.add(new Vector2D(width, 0.0)); // bottom right-hand corner
vecs.add(new Vector2D(0.0, 0.0)); // bottom left-hand corner

// door
vecs.add(new Vector2D(width/2.0, 0.0));
vecs.add(new Vector2D(width/2.0, wallHt/2.0));
vecs.add(new Vector2D(width*3.0/4.0, wallHt/2.0));
vecs.add(new Vector2D(width*3.0/4.0, 0.0));

// add offset from origin


for(int i = 0; i< vecs.size(); ++i){
//System.out.println(vecs.get(i));
vecs.get(i).addTo(v0);
//System.out.println("-- "+ vecs.get(i));
}
}

public void rotate(double angle){


Transform2D t = new Transform2D();
t.setRot(angle);
System.out.println(t);
for(int i = 0; i< vecs.size(); ++i){
vecs.set(i, t.transform(vecs.get(i)) );
}
}

public void scale(double sx, double sy){


Transform2D t = new Transform2D();
t.setScale(sx, sy);
System.out.println(t);
for(int i = 0; i< vecs.size(); ++i){
vecs.set(i, t.transform(vecs.get(i)) );
}
}
// etc.

Figure 9.9: Creation of the house shape.


9{11
9.5 Scaling Shapes
Figure 9.10 shows a graphical demonstration of scaling of a house shape.
As with rotation, displaying the points as vectors will help you see why scaling has the e ect it has;
it is the vector displacements from the origin that are scaled. The house itself is scaled accordingly,
but it also moves, which can be suprising to beginners.
If you scale by Sx = 1; Sy = 1, the shape re ects about the x-axis | or turns upside-down plus
some movement; see the lower huse shape in Figure 9.10.
The latter is the trick we use in these gures to get the y-axis to point up, see Chapter 7.

Figure 9.10: Scaling of a house shape, showing the vector rotation.

9{12
9.6 Transformation using java.awt.AneTransform
Figure 9.11 shows the results of rotating the house shape using java.awt.AffineTransform.
The relevant part of the code is shown in Figure 9.12.

Figure 9.11: Rotation using AneTransform.


Notice that the e ects of g2.transform(at); are cumulative; that is, the rst rotation
at.rotate(Math.toRadians(60.0)); causes later drawings to be rotated bt 60 with re-
spect to the original; the next, at.rotate(Math.toRadians(90.0)); causes later draw-
ings to be rotated bt 60 + 90 = 150 with respect to the original; and the last
at.rotate(Math.toRadians(120.0)); causes later drawings to be rotated bt 60 +90 +120 =
270 . As you can see, the last shape is only 90 away from the original.
Ex. The following are the outputs from //A2, //B2, //C2 of Figure 9.12. Explain each of the
six numbers in each of the three matrices.

//A

9{13
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.setBackground(Color.white);
axes.draw(Color.darkGray, g2);
double x0 = 50.0, y0 = 50.0, width = 200.0,
wallHt = 100.0, roofHt= 200.0;
VectorHouse aHouse =
new VectorHouse(x0, y0, width, wallHt, roofHt);
aHouse.drawAsLines(g2, Color.red);

AffineTransform at = new AffineTransform();


at.rotate(Math.toRadians(60.0)); //A1
g2.transform(at);
System.out.println(at); //A2
aHouse.drawAsLines(g2, Color.magenta);

at = new AffineTransform();
at.rotate(Math.toRadians(90.0)); //B1
g2.transform(at);
System.out.println(at); //B2
aHouse.drawAsLines(g2, Color.magenta);

at = new AffineTransform();
at.rotate(Math.toRadians(120.0)); //C1
g2.transform(at);
System.out.println(at); //C2
aHouse.drawAsLines(g2, Color.black);
}

Figure 9.12: Rotation using AneTransform.

9{14
AffineTransform[[0.5, -0.866025403784439, 0.0],
[0.866025403784439, 0.5, 0.0]]
//B
AffineTransform[[0.0, -1.0, 0.0],
[1.0, -0.0, 0.0]]
//C
AffineTransform[[-0.5, -0.866025403784439, 0.0],
[0.866025403784439, -0.5, 0.0]]

Answer. Refer to sections 9.3 and 9.3.1 above.

9{15
Chapter 10

Programming Audio

10.1 Introduction
The purpose of this chapter is to give a brief introduction to the part of the Java Sound API that
matter to us | and I'm glad to say that it is a very small part.
There is a fairly decent guide to the Java Sound API at:
http://java.sun.com/j2se/1.5.0/docs/guide/sound/programmer_guide/.
It says that it was last updated in 2001, but that must be wrong, since it refers to Java 5.0 (aka
Java 1.5) which was released only in 2005.
Also, chapter 4 of Brackeen gives an adequate introduction.
I know there are people in the class with a special interest in audio. The following books make be
of interest, but are in no way necessary for this course.
Ken C. Pohlmann, Principles of Digital Audio, McGraw-Hill, 4th ed., 2000. Large and expensive
(60 Euro) (Pohlmann 2000).
Bruce Fries, Digital Audio Essentials: A Comprehensive Guide to Creating, Recording, Editing, and
Sharing Music and Other Audio, O'Reilly, 2005. Lighter and less expensive (30 Euro) (Fries 2005).
First, we give an introduction to the physics of sound and how sound gets in and out of computers.

10.2 Audio Data and Sound in Computers


Figure 10.1 shows audio data being captured by a computer system, stored, then played back via
a loudspeaker.

10{1
Figure 10.1: Audio data on a computer.

10.2.1 Sound
Sound is produced by air vibrating; by that we mean rapid changes in air pressure. If you go to a
really loud disco, you will be able to feel the loudspeakers pushing the air at you.
When the string of a guitar is plucked, or the string of a violin bowed, the string vibrates. That,
in turn, causes the air to vibrate. Those vibrations, just like the waves when you drop a stone in
a lake, travel outwards. Eventually, if you are nearby, the vibrations reach your eardrum and cause
the sensation of sound.
If the vibrations are very slow, slower than about 20 vibrations per second, the eardrum will not
receive any sensation { but, like in the disco, you may feel you body being shaken by them.
Likewise, if the vibrations are faster than about 16,000 times per second, you will not hear them
{ but dogs and other animals will.
Human voices produce sound by vibrating the vocal chords. Wind instruments produce sound by
air vibrating in the instrument tube. The rate of vibration can be governed by the length of the
tube and this is done by lengthening and shortening the tube { as in a trombone, or by pressing
your ngers over holes in the tube { as in a ute or whistle.

10.2.2 Loudspeakers
Loudspeakers (or headphones) are electrically controlled sources of vibration or pressure. If you
pass a voltage wave (vibrating voltage) to a loudspeaker, the core of the loudspeaker vibrates in
sympathy with the voltage. When the loudspeaker core pushes out, you get positive pressure { the
air is pushed at you; when the core moves back, you get negative pressure { the air is sucked away
from you.

10.2.3 Microphones
Microphones are like loudspeakers in reverse; in fact, you can use loudspeakers or headphones as
microphones. When the air near a microphone vibrates (i.e. there is sound), the microphone
produces a voltage at its electrical outputs.
Therefore, we can convert from sound to electricity (voltage) using a microphone. And we can
convert from electricity to sound using a microphone.

10{2
10.2.4 Analogue versus Digital
Radios, TVs and cassette tape recorders handle sound as electricity (voltage and current); this is
called analogue { the voltage is analogous to the sound. If you drew a graph of the intensity of
the air pressure that causes the sound, and another graph of the intensity of the electrical voltage,
the two graphs would be the same shape.
Computers use numbers (digits { digital) instead of voltage. Thus, when we need to get sound
into a computer, we have to convert the pressure (sound, vibrations) into numbers.
When you want to get sound into a computer, rst you convert the sound to electrical voltage
using a microphone. Next, read the voltage at rapid intervals (about 44,100 times per second for
Compact Disc (CD)) and convert the reading into a number. This conversion into numbers is
done by an electronic circuit called an Analogue-to-Digital Converter (ADC). Now, the sound can
be stored on a computer, and on its disk, and in its memory, and copied onto CD.
Eventually, when you want to hear the digital sounds again, we must convert from numbers back
to voltages, and then, using a loudspeaker, back to air pressure.
The conversion from numbers to voltage is done by an electronic circuit called a Digital-to-Analogue
Converter (DAC), see section 6.3.2. Most of a Sound Blaster card is taken up with its Digital-
to-Analogue Converter. A Sound Blaster also has Analogue-to-Digital Converter, to which you
connect a microphone or other input source.

10.2.5 Electronic (or Electro-acoustic) Music


When we produce music for a CD we do the following: (a) cause air vibrations by a variety of
means (voices, strings, wind instruments); (b) convert those vibrations to electrical voltages; (c)
convert these voltages to numbers; (d) copy the numbers onto the CD. And then when we want
to hear the sound, we: (e) convert the numbers back to voltages; (f) convert the voltages back
to sound.
But computers can produce numbers on their own. And they can be programmed to produce strings
of numbers that correspond to certain sounds; this includes sounds that are hard to produce using
ordinary instruments. So, we can use the computer to jump straight to (c) and cut out the
conventional sound production altogether.

10.2.6 Electronic Instruments


What if you want to produce an electronic piano? Let's say it has 40 keys, numbered k1 to k40.
Now you take a real piano with 40 keys; you take each key in turn, press it, and record the sound
produced; these days, the recording is more likely to be digital (numbers), but the same principle
applies to analogue (voltage) recording. You store the sound for all 40 keys.
Now, you connect a computer to an electronic piano keyboard; when you press the key k20 say,
the keyboard indicates to the computer that k20 has been pressed. The computer looks for the
sound that we recorded for k20 on the real piano and sends that to the equivalent of a Sound
Blaster card and to an Digital-to-Analogue and then to loudspeakers or headphones.

10{3
10.2.7 Magnetic Recording
See Operating Systems, chapter on disks and le systems.

10.2.8 Data Compression


If you sample sounds at CD rates (44,100 Hz in stereo and use 16 bits per sample), you get 88,200
numbers per second, or 176,400 bytes. That's about 10.6 Megabytes per minute, or about 635
Megabytes per hour (this gures { a CD holds about 650 Megabytes of data).
That's an awful lot of data. How could it be reduced, without detracting from the quality? (If you
decrease the sampling frequency, the sound will become soft and dull { like listening to Medium
Waveband radio or at the end of a telephone; if you decrease to one byte or less, you will get other
annoyances.)
The answer is data compression.
There are a number of principles by which we can compress data { i.e. store or transmit as near as
possible the same information, only using less bits. There are two categories of data compression:

 Lossy. Used for audio (e.g. MP3) and images, where the data are eventually going to be
converted into an analogue form to be listened to or viewed by a person. Here, loss of the
odd bit may not make any di erence.
 Lossless. Used where the data are, and always will remain, digital; where each bit is crucial.
E.g. the ASCII string 74 68 65 20 63 61 74 20 73 63 61 20 6F 6E 20 74 68 65 20 6D 61
74 2E reads: "the cat sat on the mat".
If I decide to remove all the least-signi cant bits (reasonable for lossy, but not here), I get:
74 68 64 20 62 60 74 20 72 62 60 20 6E 6E 20 74 68 64 20 6C 60 74 2E, which reads:
"thd b't rb' nn thd l't." Not much use!

Some of the principles by which we can compress data are:

 Redundancy. For example:


TH_R_ _S _NLY _N_ W_Y T_ F_LL _N TH_ V_W_LS _N TH_S S_NT_NC_.
You use redundancy all the time when texting on mobile phones. Can be lossless.
 Use less bits for the more common parts of the message. For example, 'e' is very common in
English text, 'z' and 'q' very uncommon. If you were to use a two bit code for 'e' and similarly
small code sizes for 'a', 't', etc. and 16 bits for 'z' and 'q', you would achieve considerable
savings. This is the basis of Hu man coding. Hu man coding can achieve compression ratios
of around 50% for English text. Lossless.
 Perceptual coding. Work out what parts of sounds (and pictures) are most noticed by
humans { gives these larger codes; give small codes to parts where the humans will never
notice. Lossy.

10{4
 Di erential coding. Let's say the following sequence of numbers appear in a digital recording
of a voice: 32,001 32,015, 32,020 32,010 32,005 . . . . As it is, we require 16 bits per number.
However, let's send the rst, and send only di erences for the remainder: 32,001 14 5 -10
-5. For the di erences, we can get away with ve bits. Can be lossless.

Recall that we started o at 176,400 bytes per second, that is 1.4 Mbits per second. MP3 com-
pression can compress music to 128-Kbits per second (around 10%) and still be quite acceptable.

10.3 Java Sound API


The Java Sound API provides a hugely comprehensive set of functionality; fortunately we'll use
only a minute fraction of it.
There are two major packages:

 javax.sound.sampled;
 javax.sound.midi.

10.3.1 Sampled Audio


This is like we discussed previously in section 10.2. Physical sound is made up of pressure variations
(waves); pressure can be measured by a microphone and then sampled and digitised to produce
a sequence of numbers that represent the pressure at instants in time. Figure 10.2 shows a 900
samples of a pure sine-wave tone (about 200-Hz) sampled at 8000-Hz. Since the sampling rate is
8000-Hz, that picture represents just over 101 of a second of data.

Sample Rates Typical sample rates are:

 8 KHz, speech; used by telephone service;


 44.1-KHz, CD quality;
 22.05-KHz, roughly AM radio quality;
 11.025-KHz, speech.

Bits per sample Typical values are:

 8 bits; signed -128 { +127; unsigned 0 { 255; telephony and low quality audio;
 16 bits; signed -32768 { +32767; unsigned 0 { 65535; CD quality.

PCM | Pulse Code Modulation

10{5
Figure 10.2: Sampled sound.

Compression If you examined a .wav le, you would be able to nd the numbers that represent
the sound; and if you made a graph of them with time on the horizontal axis, you would end up
with something that looks like Figure 10.2.
Compressed les like MP3s are di erent; if you looked at the data in an MP3 le they would look
like nothing on earth; that is until you pushed them through an MP3 decoder; after the decoder
the data look just the same as those in a .wav le. At least you hope they do!
DPCM (Di erential Pulse Code Modulation) can achieve modest compression rates.

PCM | Pulse Code Modulation I have no idea how the term PCM persists; it was introduced
at a time when digital data equaled pulses. In the audio context, PCM means raw encode numbers.

Mu-Law, A-Law etc. It is found that we can save bits by making the digitisation steps larger for
small values of sound. This is done by a scheme called companding { applied before the analogue-
to-digital converter (ADC); the opposite to companding is then done after the digital-to-analogue
converter (DAC). Mu-Law and A-Law are useful when trying to get the most out of the eight bit
samples used in telephony.
If you are interested, I can draw a diagram on this in class; but in general, don't worry about it.

10.3.2 MIDI | Musical Instrument Digital Interface


MIDI les are completely di erent; they contain instructions with which to create sound. Thus, a
bit like a program; or the coded version of a music sheet.

10{6
Remember what we said in section 10.2 about electronic instruments | or synthesisers. A partic-
ular note might look (and sound) like Figure 10.2 (you'll get a chance to hear that in a practical),
so, when the instruction to play that note appears, the program or sound card can pump that data
out to the output port. The sound (from a soundbank, see below) might be recorded from a real
instrument.
As Brackeen (p. 212) says \... the Java Sound API synthesises MIDI music through the use of a
soundbank..."; he then mentions that the soundbank is available in the SDK (J2SDK), and not in
the runtime (J2RE); actually, he means the Windows runtime. In any case, we use the SDK, so we
are okay; by runtime, he means the Java interpreter that exists on non-development computers.
By soundbank, he means a collection of di erent lists of numbers like those shown in Figure 10.2.
Later Brackeen says that if no soundbank exists, the MIDI port on the sound card is used; he
appears to think that this option is inferior.
Finally, he points to higher quality soundbanks available at:
http://java.sun.com/products/java-media/sound/soundbanks.html.

Synthesiser The term synthesiser means that the sound is synthesised (made up). On the other
hand, sampled sound comes ready to push into the digital-to-analogue converter (DAC) on the
sound card.
Synthesis is the sound equivalent of rendering | of images or video.

10.3.3 Java Sound Terminology


See
http://java.sun.com/j2se/1.5.0/docs/guide/sound/programmer_guide/
but don't spend too much time on it.

Let's move straight into software.

10.4 Programs Using Java Sound API


Here is Brackeen's SimpleSoundPlayer.java; I've scrunched it up a little for the purposes of
these notes. We dissect it below.

import java.io.*;
import javax.sound.sampled.*;
/**
The SimpleSoundPlayer encapsulates a sound that can be opened
from the file system and later played.
*/

10{7
public class SimpleSoundPlayer {
public static void main(String[] args) {
// load a sound
SimpleSoundPlayer sound =
new SimpleSoundPlayer("../sounds/voice.wav");
// create the stream to play
InputStream stream =
new ByteArrayInputStream(sound.getSamples());
// play the sound
sound.play(stream);
// exit
System.exit(0);
}
private AudioFormat format;
private byte[] samples;
/**
Opens a sound from a file.
*/
public SimpleSoundPlayer(String filename) {
try {
// open the audio input stream
AudioInputStream stream =
AudioSystem.getAudioInputStream(new File(filename));
format = stream.getFormat();
System.out.println(format);
// get the audio samples
samples = getSamples(stream);
}
catch (UnsupportedAudioFileException ex) {
ex.printStackTrace();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
/**
Gets the samples of this sound as a byte array.
*/
public byte[] getSamples() {
return samples;
}
/**
Gets the samples from an AudioInputStream as an array
of bytes.
*/
private byte[] getSamples(AudioInputStream audioStream) {
// get the number of bytes to read
int length = (int)(audioStream.getFrameLength() *
format.getFrameSize());

10{8
// read the entire stream
byte[] samples = new byte[length];
DataInputStream is = new DataInputStream(audioStream);
try {
is.readFully(samples);
}
catch (IOException ex) {
ex.printStackTrace();
}
// return the samples
return samples;
}
/**
Plays a stream. This method blocks (doesn't return) until
the sound is finished playing.
*/
public void play(InputStream source) {

// use a short, 100ms (1/10th sec) buffer for real-time


// change to the sound stream
int bufferSize = format.getFrameSize() *
Math.round(format.getSampleRate() / 10);
byte[] buffer = new byte[bufferSize];
// create a line to play to
SourceDataLine line;
try {
DataLine.Info info =
new DataLine.Info(SourceDataLine.class, format);
line = (SourceDataLine)AudioSystem.getLine(info);
line.open(format, bufferSize);
}
catch (LineUnavailableException ex) {
ex.printStackTrace();
return;
}
// start the line
line.start();
// copy data to the line
try {
int numBytesRead = 0;
while (numBytesRead != -1) {
numBytesRead =
source.read(buffer, 0, buffer.length);
if (numBytesRead != -1) {
line.write(buffer, 0, numBytesRead);
}
}
}
catch (IOException ex) {

10{9
ex.printStackTrace();
}
// wait until all data is played, then close the line
line.drain();
line.close();
}
}

Dissection

1. Create a SimpleSoundPlayer object; the sound will be the sound in the le


../sounds/voice.wav.

SimpleSoundPlayer sound =
new SimpleSoundPlayer("../sounds/voice.wav");

2. Get the sampled data from the SimpleSoundPlayer object sound and create a
ByteArrayInputStream with it; there is not a pile of di erence between using a
ByteArrayInputStream and just an array. ByteArrayInputStream behaves a bit like a
le | but you don't know about les, so that is not much use to you.
Byte? Does not mean that the data samples are 8-bits; could be 16-bit { two bytes per
sample.

InputStream stream =
new ByteArrayInputStream(sound.getSamples());

3. Use SimpleSoundPlayer.play() to read the data from the stream and push it out to the
sound card; play is described later.

sound.play(stream);

4. Data members.

private AudioFormat format; //describes the format of the data


private byte[] samples; // array of bytes

The AudioFormat format object contains details like (below is a printout of one):

Format: PCM_SIGNED 8000.0 Hz, 8 bit, mono, 1 bytes/frame,


SampleSizeInBits: 8

In PCM, a frame is a sample, but in a compressed format, frame and sample mey be quite
di erent.
An AudioInputStream stream knows all about formats and stream.getFormat(); gets
the format.
Why do we need the format? We need it when we want to open a Line out through which
we play the data; in other words we pass on details of the format to the sound card, so that
the card knows how to handle the data that we push at it.

10{10
5. Reading the samples from AudioInputStream audioStream

private byte[] getSamples(AudioInputStream audioStream)

The length of the data in bytes is FrameLength*FrameSize.


6. The following reads the complete le into a byte array.

byte[] samples = new byte[length];


DataInputStream is = new DataInputStream(audioStream);
try {
is.readFully(samples);
}

try, catch are to do with exceptions; let's say there was a le error (somebody disconnects
the hard disk, say), an exception (a special program signal) will be generated; the fact that
is.readFully(samples); is in a try section means that ex.printStackTrace(); will be
executed to tell the user that something bad happened, and some further explanation.
7. Play the sound (play). This is complicated by the fact that Brackeen sends the sound out
in 101 th of a second chunks.
8. We need to create a Line out through which we send the data | a bit like the line-out or
the headphone outputs in your hi- or MP3 player. First we create a DataLine.Info object
that we inform about the format (see above).
We then get (create) a SourceDataLine line; I nd the name odd, the SourceDataLine
is actually a Line that receives data for playing.
We open the SourceDataLine; oddly enough, we again specify the format.

SourceDataLine line;
try {
DataLine.Info info =
new DataLine.Info(SourceDataLine.class, format);
line = (SourceDataLine)AudioSystem.getLine(info);
line.open(format, bufferSize);
}
catch (LineUnavailableException ex) {
ex.printStackTrace();
return;
}

9. Start the Line.


10. Finally, read chunks (bu ers of 101 th of a second) of data from
ByteArrayInputStream source and write that data out to the line.
When reading from ByteArrayInputStream source, a return of 1 indicates that you
have reached the end; otherwise it returns the number of bytes read.

10{11
int numBytesRead = 0;
while (numBytesRead != -1) {
numBytesRead =
source.read(buffer, 0, buffer.length);
if (numBytesRead != -1) {
line.write(buffer, 0, numBytesRead);
}
}

That will have to do for an introduction, and I'm in a hurry to get these notes printed.
Other things that will need to be brie y touched on are:

 Creating a special ByteArrayInputStream, called LoopingByteArrayInputStream that


when it reaches the end, starts back again at the beginning | handy for the y buzzing
sound; we need only a small le which we repeat; y conversation is boring and repetitive;
 How to produce an echo; see Brackeen, p. 180;
 Changing volume to fake 3D sound; here we need distance and our old friend Pythagoras
comes to our aid again,
float distance = (float)Math.sqrt(dx*dx = dy*dy);

where dx, dy are the distances along the z- and y-axes.


Once we have the distance, we work out the volume as:
newVolume = (maxDistance - distance)/ maxDistance;

Q. If distance = 0, what is volume?


Q. If distance = maxDistance, what is volume?
Q. If distance = 0.5*maxDistance, what is volume?
 In the practical I will give you a program JSoundPlayer that I hacked together based on
SimpleSoundPlayer; amongst other modi cations, it displays the played sound as a graph.
It also allows you to specify a synthesised sound { based on our friend the trigonometric
function sin. No doubt the musicians in the class will be able to suggest more interesting
synthetic sounds.
I may be able to demonstrate the e ects of clipping, and of speeding up playback by dropping
samples, or slowing down, by repeating samples.
But please note: the program is a hack; it works only on 8-bit mono data. It may crash for
a whole lot of reasons; indeed, I should be ashamed of myself for letting it into public.
 Playing MIDI, see Brackeen p. 212.
 See Brackeen p. 210 for a discussion on the crippling amount of memory used by full CD
quality sound;
 MP3 and Ogg Vorbis use less space because of compression, but, see Brackeen p. 210, there
are penalties: the CPU must be used to decompress the data before playing; that is not good
if your CPU is already hard-pressed.

10{12
Chapter 11

Brief Notes on the 2D Platform Game

11.1 Introduction
The purpose of this chapter is to pick through certain parts of Brackeen's chapter 5 (Brackeen
et al. 2004) and show how you can make some modi cations to, for example, the behaviour of
Player.

11.2 Selected parts of the code


The class Sprite.java shown in Figures 11.2 and 11.3 supplies a general Sprite class. Using
inheritance, both Creature and Powerup extend Sprite; then Grub, Fly, and Player extend Creature;
and Music, Star, and Goal (all in Powerup.java) extend Powerup.
These relationships are shown in the UML diagram Figure 11.1.

Figure 11.1: Sprite class hierarchy.

11{1
package com.brackeen.javagamebook.graphics;
import java.awt.Image;

public class Sprite {


protected Animation anim;
// position (pixels)
private float x; private float y;
// velocity (pixels per millisecond)
private float dx; private float dy;
/**
Creates a new Sprite object with the specified Animation.
*/
public Sprite(Animation anim) {
this.anim = anim;
}
/**
Updates this Sprite's Animation and its position based
on the velocity.
*/
public void update(long elapsedTime) {
x += dx * elapsedTime; y += dy * elapsedTime;
anim.update(elapsedTime);
}

public float getX() {


return x;
}

public float getY() {


return y;
}

public void setX(float x) {


this.x = x;
}

public void setY(float y) {


this.y = y;
}

/**
Gets this Sprite's width, based on the size of the
current image.
*/
public int getWidth() {
return anim.getImage().getWidth(null);
}

Figure 11.2: Sprite.java, part 1.

11{2
/**
Gets this Sprite's height, based on the size of the
current image.
*/
public int getHeight() {
return anim.getImage().getHeight(null);
}
/**
Gets the horizontal velocity of this Sprite in pixels
per millisecond.
*/
public float getVelocityX() {
return dx;
}
/**
Gets the vertical velocity of this Sprite in pixels
per millisecond.
*/
public float getVelocityY() {
return dy;
}
/**
Sets the horizontal velocity of this Sprite in pixels
per millisecond.
*/
public void setVelocityX(float dx) {
this.dx = dx;
}
/**
Sets the vertical velocity of this Sprite in pixels
per millisecond.
*/
public void setVelocityY(float dy) {
this.dy = dy;
}
/**
Gets this Sprite's current image.
*/
public Image getImage() {
return anim.getImage();
}
/**
Clones this Sprite. Does not clone position or velocity
info.
*/
public Object clone() {
return new Sprite(anim);
}
}

Figure 11.3: Sprite.java,


11{3 part 2.
/**
The Animation class manages a series of images (frames) and
the amount of time to display each frame.
*/
public class Animation {

private ArrayList frames;


private int currFrameIndex;
private long animTime;
private long totalDuration;
/**
Creates a new, empty Animation.
*/
public Animation() {
this(new ArrayList(), 0);
}
private Animation(ArrayList frames, long totalDuration) {
this.frames = frames;
this.totalDuration = totalDuration;
start();
}

/**
Creates a duplicate of this animation. The list of frames
are shared between the two Animations, but each Animation
can be animated independently.
*/
public Object clone() {
return new Animation(frames, totalDuration);
}

/**
Adds an image to the animation with the specified
duration (time to display the image).
*/
public synchronized void addFrame(Image image,
long duration) {
totalDuration += duration;
frames.add(new AnimFrame(image, totalDuration));
}

/**
Starts this animation over from the beginning.
*/
public synchronized void start() {
animTime = 0;
currFrameIndex = 0;
} ... continued

Figure 11.4: Animation.java, part 1.


11{4
... continued
/**
Updates this animation's current image (frame), if
neccesary.
*/
public synchronized void update(long elapsedTime) {
if (frames.size() > 1) {
animTime += elapsedTime;

if (animTime >= totalDuration) {


animTime = animTime % totalDuration;
currFrameIndex = 0;
}

while (animTime > getFrame(currFrameIndex).endTime) {


currFrameIndex++;
}
}
}

/**
Gets this Animation's current image. Returns null if this
animation has no images.
*/
public synchronized Image getImage() {
if (frames.size() == 0) {
return null;
}
else {
return getFrame(currFrameIndex).image;
}
}

private AnimFrame getFrame(int i) {


return (AnimFrame)frames.get(i);
}

private class AnimFrame {

Image image;
long endTime;

public AnimFrame(Image image, long endTime) {


this.image = image;
this.endTime = endTime;
}
}
}

Figure 11.5: Animation.java, part 2.


11{5
/**
A Creature is a Sprite that is affected by gravity and can
die. It has four Animations: moving left, moving right,
dying on the left, and dying on the right.
*/
public abstract class Creature extends Sprite {

/**
Amount of time to go from STATE_DYING to STATE_DEAD.
*/
private static final int DIE_TIME = 1000;

public static final int STATE_NORMAL = 0;


public static final int STATE_DYING = 1;
public static final int STATE_DEAD = 2;

private Animation left;


private Animation right;
private Animation deadLeft;
private Animation deadRight;
private int state;
private long stateTime;

/**
Creates a new Creature with the specified Animations.
*/
public Creature(Animation left, Animation right,
Animation deadLeft, Animation deadRight) {
super(right);
this.left = left;
this.right = right;
this.deadLeft = deadLeft;
this.deadRight = deadRight;
state = STATE_NORMAL;
}
/**
Gets the maximum speed of this Creature.
*/
public float getMaxSpeed() {
return 0;
}
/**
Wakes up the creature when the Creature first appears
on screen. Normally, the creature starts moving left.
*/
public void wakeUp() {
if (getState() == STATE_NORMAL && getVelocityX() == 0) {
setVelocityX(-getMaxSpeed());
}
} ... continued

11{6
Figure 11.6: Creature.java, part 1.
/** ... continued
Gets the state of this Creature. The state is either
STATE_NORMAL, STATE_DYING, or STATE_DEAD.
*/
public int getState() {
return state;
}

/**
Sets the state of this Creature to STATE_NORMAL,
STATE_DYING, or STATE_DEAD.
*/
public void setState(int state) {
if (this.state != state) {
this.state = state;
stateTime = 0;
if (state == STATE_DYING) {
setVelocityX(0);
setVelocityY(0);
}
}
}
/**
Checks if this creature is alive.
*/
public boolean isAlive() {
return (state == STATE_NORMAL);
}
/**
Checks if this creature is flying.
*/
public boolean isFlying() {
return false;
}
/**
Called before update() if the creature collided with a
tile horizontally.
*/
public void collideHorizontal() {
setVelocityX(-getVelocityX());
}

/**
Called before update() if the creature collided with a
tile vertically.
*/
public void collideVertical() {
setVelocityY(0);
} ... continued

11{7
Figure 11.7: Creature.java, part 2.
/** ... continued
Updates the animation for this creature.
*/
public void update(long elapsedTime) {
// select the correct Animation
Animation newAnim = anim;
if (getVelocityX() < 0) {
newAnim = left;
}
else if (getVelocityX() > 0) {
newAnim = right;
}
if (state == STATE_DYING && newAnim == left) {
newAnim = deadLeft;
}
else if (state == STATE_DYING && newAnim == right) {
newAnim = deadRight;
}

// update the Animation


if (anim != newAnim) {
anim = newAnim;
anim.start();
}
else {
anim.update(elapsedTime);
}

// update to "dead" state


stateTime += elapsedTime;
if (state == STATE_DYING && stateTime >= DIE_TIME) {
setState(STATE_DEAD);
}
}
}

Figure 11.8: Creature.java, part 3.

11{8
public class Player extends Creature {
private static final float JUMP_SPEED = -0.95f;
private boolean onGround;
private float jumpSpeed; // added

public Player(Animation left, Animation right,


Animation deadLeft, Animation deadRight)
{
super(left, right, deadLeft, deadRight);
//setHealth(0);
jumpSpeed= JUMP_SPEED;
}

public void collideHorizontal() {


setVelocityX(0);
}

public void collideVertical() {


// check if collided with ground
if (getVelocityY() > 0) {
onGround = true;
}
setVelocityY(0);
}

public void setY(float y) {


// check if falling
if (Math.round(y) > Math.round(getY())) {
onGround = false;
}
super.setY(y);
}

public void wakeUp() {


// do nothing }

/**
Makes the player jump if the player is on the ground or
if forceJump is true.*/
public void jump(boolean forceJump) {
if (onGround || forceJump) {
onGround = false;
setVelocityY(jumpSpeed);
}
}

public float getMaxSpeed() {


return 0.5f;
}
}

11{9
Figure 11.9: Player.java.
private int health;

public void addHealth(int a){


health+=a;
}

public void setHealth(int a){


health = a;
}

public int getHealth(){


return health;
}

Figure 11.10: Health code.

11{10
/**
Checks for Player collision with other Sprites. If
canKill is true, collisions with Creatures will kill
them.
*/
public void checkPlayerCollision(Player player,
boolean canKill)
{
if (!player.isAlive()) {
return;
}

// check for player collision with other sprites


Sprite collisionSprite = getSpriteCollision(player);
if (collisionSprite instanceof PowerUp) {
acquirePowerUp((PowerUp)collisionSprite);
player.addHealth(2);
displayHealth(player);
}
else if (collisionSprite instanceof Creature) {
Creature badguy = (Creature)collisionSprite;
if (canKill) {
// kill the badguy and make player bounce
soundManager.play(boopSound);
badguy.setState(Creature.STATE_DYING);
player.setY(badguy.getY() - player.getHeight());
player.jump(true);
player.addHealth(3);
displayHealth(player);

}
else {
player.addHealth(-2);
displayHealth(player);
if(player.getHealth()<0){
player.setState(Creature.STATE_DYING);
}
}
}
}

private static void displayHealth(Player p){


System.out.println("Health= "+ p.getHealth());
}

Figure 11.11: Code added to GameManager.java for Adding or Losing Health after Collisions.

11{11
// draw the visible tiles
int firstTileX = pixelsToTiles(-offsetX);
int lastTileX = firstTileX +
pixelsToTiles(screenWidth) + 1;
for (int y=0; y<map.getHeight(); y++) {
for (int x=firstTileX; x <= lastTileX; x++) {
Image image = map.getTile(x, y);
if (image != null) {
g.drawImage(image,
tilesToPixels(x) + offsetX,
tilesToPixels(y) + offsetY, null);
}
}
}

Font font= new Font("SansSerif", Font.BOLD, 10);


g.setFont(font); g.setColor(Color.black);

// ************************* draw player


float px= player.getX() + offsetX;
float py= player.getY() + offsetY;

g.drawImage(player.getImage(),
Math.round(player.getX()) + offsetX,
Math.round(player.getY()) + offsetY,
null);

int h= player.getHealth();
if(h>0){
String s= "h= "+h;
g.drawString(s, px, py);
System.out.println("h= "+h);
}
//**************************************
// draw sprites
Iterator i = map.getSprites();
while (i.hasNext()) {
Sprite sprite = (Sprite)i.next();
int x = Math.round(sprite.getX()) + offsetX;
int y = Math.round(sprite.getY()) + offsetY;
g.drawImage(sprite.getImage(), x, y, null);
// wake up the creature when it's on screen
if (sprite instanceof Creature &&
x >= 0 && x < screenWidth)
{
((Creature)sprite).wakeUp();
}
}
}
}

11{12
Figure 11.12: Health display code added to TileMapRenderer.java
# Map file for tile-based game
# (Lines that start with '#' are comments)
# The tiles are:
# (Space) Empty tile
# A..Z Tiles A through Z
# o Star
# ! Music Note
# * Goal
# 1 Bad Guy 1 (grub)
# 2 Bad Guy 2 (fly)

o o
o o o
o o o o o o o o o
IIIIIII IIIIIII o o 2
o 2
2 2EF
EF EGD
EF 1 CD 1 1 EGAD *
BBBBBBBBGHBBBBBBBGHBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBGAAHBBBBBBBBBBB

Figure 11.13: Map le for level 1.

11{13
private TileMap loadMap(String filename)
throws IOException
{
ArrayList lines = new ArrayList();
int width = 0;
int height = 0;

// read every line in the text file into the list


BufferedReader reader = new BufferedReader(
new FileReader(filename));
while (true) {
String line = reader.readLine();
// no more lines to read
if (line == null) {
reader.close();
break;
}

// add every line except for comments


if (!line.startsWith("#")) {
lines.add(line);
width = Math.max(width, line.length());
}
}

// parse the lines to create a TileEngine


height = lines.size();
TileMap newMap = new TileMap(width, height);
for (int y=0; y<height; y++) {
String line = (String)lines.get(y);
for (int x=0; x<line.length(); x++) {
char ch = line.charAt(x);

// check if the char represents tile A, B, C etc.


int tile = ch - 'A';
if (tile >= 0 && tile < tiles.size()) {
newMap.setTile(x, y, (Image)tiles.get(tile));
}
// continued ....

Figure 11.14: Level reader (ResourceManager.java), part 1.

11{14
// check if the char represents a sprite
else if (ch == 'o') {
addSprite(newMap, coinSprite, x, y);
}
else if (ch == '!') {
addSprite(newMap, musicSprite, x, y);
}
else if (ch == '*') {
addSprite(newMap, goalSprite, x, y);
}
else if (ch == '1') {
addSprite(newMap, grubSprite, x, y);
}
else if (ch == '2') {
addSprite(newMap, flySprite, x, y);
}
}
}
// add the player to the map
Sprite player = (Sprite)playerSprite.clone();
player.setX(TileMapRenderer.tilesToPixels(3));
player.setY(0);
newMap.setPlayer(player);
return newMap;
}
private void addSprite(TileMap map,
Sprite hostSprite, int tileX, int tileY) {
if (hostSprite != null) {
// clone the sprite from the "host"
Sprite sprite = (Sprite)hostSprite.clone();

// center the sprite


sprite.setX(
TileMapRenderer.tilesToPixels(tileX) +
(TileMapRenderer.tilesToPixels(1) -
sprite.getWidth()) / 2);
// bottom-justify the sprite
sprite.setY(TileMapRenderer.tilesToPixels(tileY + 1) -
sprite.getHeight());
// add it to the map
map.addSprite(sprite);
}
}

Figure 11.15: Level reader (ResourceManager.java), part 2.

11{15

Das könnte Ihnen auch gefallen