Major update for bopti (gray, light still not tested). Reworked out gray engine.

This commit is contained in:
lephe 2016-07-04 18:30:25 +02:00
parent aa0f4b7285
commit ab17532f67
17 changed files with 1105 additions and 463 deletions

View file

@ -1,8 +1,8 @@
#
# fx-9860g lib Makefile.
#
#! /usr/bin/make -f
.PHONY: all clean fclean re install
#---
# fx-9860g lib Makefile.
#---
@ -23,8 +23,7 @@ bin = build/ginttest.bin
elf = build/ginttest.elf
# Command-line options
cflags = -m3 -mb -nostdlib -ffreestanding \
-W -Wall -pedantic -std=c11 \
cflags = -m3 -mb -nostdlib -ffreestanding -W -Wall \
-I . -isystem include
lib = -lgcc -L. -lgint -lc
@ -42,20 +41,20 @@ src-lib = crt0.c syscalls.s \
hea-lib = 7305.h 7705.h gint.h \
stdlib.h \
mpu.h keyboard.h screen.h display.h gray.h timer.h tales.h
obj-lib = $(addprefix build/, $(addsuffix .o, $(src-lib)))
hdr-lib = $(addprefix include/, $(hea-lib))
obj-lib = $(patsubst %, build/%.o, $(src-lib))
hdr-lib = $(patsubst %, include/%, $(hea-lib))
# Standard library.
src-std = setjmp.s string.c
hea-std = setjmp.h string.h ctype.h
obj-std = $(addprefix build/, $(addsuffix .o, $(src-std)))
hdr-std = $(addprefix include/, $(hea-std))
obj-std = $(patsubst %, build/%.o, $(src-std))
hdr-std = $(patsubst %, include/%, $(hea-str))
# Test application.
src-app = ginttest.c
img-app = bitmap_opt.bmp swords.bmp sprites.bmp symbol.bmp symbol2.bmp \
illustration.bmp
res-app = build/font.o $(addprefix build/, $(addsuffix .o, $(img-app)))
res-app = $(patsubst %, build/%.o, $(img-app)) build/font.o
#
@ -77,7 +76,7 @@ libc.a: $(obj-std)
@ echo "\033[32;1mStandard file size: "`stat -c %s libc.a` \
"bytes\033[0m"
ginttest.g1a: libgint.a $(src-app) $(res-app)
$(g1a): libgint.a $(src-app) $(res-app)
$(cc) $(src-app) $(res-app) -T ginttest.ld -o $(elf) $(cflags) $(lib)
$(ob) -R .comment -R .bss -O binary $(elf) $(bin)
$(wr) $(bin) -o ginttest.g1a -i icon.bmp
@ -97,7 +96,7 @@ build/%.s.o: src/%.s
$(as) -c $^ -o $@
build/%.bmp.o: resources/%.bmp
fxconv $^ -o $@
fxconv $^ -o $@ --preview
build/font.o: resources/font.bmp
fxconv --font $^ -o $@
@ -136,3 +135,7 @@ re: distclean all
install:
usb-connector SEND ginttest.g1a ginttest.g1a fls0
.PHONY: all clean mrproper distclean re install

47
TODO
View file

@ -1,27 +1,34 @@
@ vram overflow
@ keyboard interface
- upgraded blending modes
- blending modes for text
- information masks for text
- test all font encodings
- font clipping
--------------
Lots of things to do
--------------
- bitmap parts
- bitmap clipping
@ known bugs
+ simple improvements
- important milestones
~ needs investigation
- multi-getkey repeats (if possible, which doesn't seem likely)
- it appears that multi-getkey does not always trigger rectangle effects on
sh7305
- write and test gray engine
- full rtc driver (time)
- callbacks and complete user API
@ vram overflow
@ keyboard test threading interface
- exhaustive save for setjmp()
- registers that need to be saved when configuring gint
+ compute frequencies
+ gray text
+ effective rtc callback
+ properly test gray drawing
+ upgraded blending modes
+ blending modes for text
+ information masks for text
+ test all font encodings
+ font clipping
+ bitmap parts
+ bitmap clipping
- check possible bug for optimization of __attribute__((interrupt_handler))
- write and test gray engine
- full rtc driver (time)
- callbacks and complete user API
- 7305.h
- libc
~ shadowy rectangle effect for Shift + Alpha + Left + Down
~ exhaustive save for setjmp()
~ registers that need to be saved when configuring gint
~ possible bug when -O2 __attribute__((interrupt_handler))

305
doc/bopti.md Normal file
View file

@ -0,0 +1,305 @@
# gint documentation: bitmap rendering #
*Warning: this is a draft. The current implementation of bopti is different*
*from this description, though similar.*
## Basics
The bitmap drawing module, *bopti*, is based on video-ram (vram) bitwise
operations. The images are made of layers that describe (more or less) which
pixels of the image an operation applies to. Rendering the image consists in
applying an operation function to the existing vram pixels.
*bopti* makes an extensive use of longword operations and 4-alignment to take
advantage of the bit-based structure of the monochrome vram and enhance
performance. Among all possible optimizations, avoiding direct pixel access has
proven to be the most efficient.
---
## Operations
Operations are functions applied to update a vram longword in accordance with
an operation mask. Bits that are set in the mask indicate pixels which have to
be updated by the operation. Bits that are reset indicate pixels that must not
be changed.
All the point is, the functions must not access the bit information in the mask
or the vram data individually. They must operate globally using longword
bitwise instructions, so that performance is maintained.
Consider for instance a logical and operation (`(a, b) -> a & b`).
Operating on pixels would need to move some data, test the value of a bit in
the mask, edit the vram data, and eventually shift both the data and the mask, for all of the 32 pixels.
One could not expect this from happening in less than 150 processor cycles
(in comparison, using generic-purpose `setPixel()`-like functions would be at
least 10 times as long). The smarter method operates directly on the longword
parameters, and performs `data = data & ~mask`, which is 2 processor cycles
long.
The following operations are defined by *bopti*:
- `Draw `: Draws black pixels.
- `Alpha `: Erases non-transparent pixels.
- `Change `: Changes the pixels' color.
- `Lighten `: Lightens gray pixels.
- `Lighten2`: Lightens gray pixels more.
- `Darken `: Darkens gray pixels.
- `Darken2 `: Darkens gray pixels more.
To perform an operation, *bopti* uses the mask data, which is taken from a
layer, and calls the associated operation function. Every operation has its
default layer mask (except `change`), but this setting may be overridden.
*bopti* allows user programs to use any monochrome image as a mask for an
operation. For instance, a black rectangle may be drawn by any of the operation
functions, resulting in various results.
An additional operation, `fill`, is defined by the library. It does all the job
necessary to render the full image, which often falls back to performing the
operations that correspond to the kind of image.
---
## Operation on gray pixels
*Detailed article: [Gray engine](gray-engine)*
Gray pixels are made of one four colors, each of which is represented by two
bits. Arguments `light` and `dark` of gray operation functions are longwords
containing the least significant and most significant of these bits,
respectively.
white = 0 [00]
lightgray = 1 [01]
darkgray = 2 [10]
black = 3 [11]
The `Lighten` operation affects pixels as if decrementing their value (white
pixels are not changed), and `darken` does the opposite (black pixels are not
changed).
Operations `Lighten2` and `darken2` do the same two times.
From this description, and considering two bits `light` and `dark`, it follows
that:
```c
lighten2 (light, dark) = (0, light & dark)
lighten (light, dark) = (light & dark, light & ~dark)
darken (light, dark) = (light | dark, light | ~dark)
darken2 (light, dark) = (1, light | dark)
```
This does not take account of a possible operation mask. See section
[Operation functions](#operation-functions) for more flexible functions.
---
## Partial transparency
*bopti* allows monochrome images to have semi-transparent pixels. Consider for
example a white background. An opaque black pixel will render black, while a
1/3-transparent black pixel will render dark gray, and a 2/3-transparent black
pixel will render light gray. Which means that:
* 1/3-transparent white pixels form the mask for `lighten2`
* 2/3-transparent white pixels form the mask for `lighten`
* 2/3-transparent black pixels form the mask for `darken`
* 1/3-transparent black pixels form the mask for `darken2`
Partial transparency on gray pixels is not allowed. Apart from the complexity
of the generic partial transparency rendering operation, semi-transparent gray
pixels are not of any use.
---
## Operation functions
Operations on monochrome buffers are defined as functions of two parameters:
the vram data longword to update, `data`, and the operation mask, `x`. Every
of these functions must satisfy `f(data, 0) = data`.
Operations on gray buffers take three arguments: `light` and `dark`, which are
longwords from the [gray buffers](gray-engine), and the operation mask `x`.
They update both longwords and return them. These functions must satisfy
`f(light, dark, 0) = (light, dark)`.
The functions for each of the operations are the following:
~~~c
# Draw function
draw(data, x) = data | x
# Alpha function
alpha(data, x) = data & ~x
# Change function
change(data, x) = data ^ x
# Lighten function
lighten(light, dark, x) = (light & (dark | ~x), (light | ~x) & (x ^ dark))
# Lighten2 function
lighten2(light, dark, x) = (light & ~x, (light | ~x) & dark)
# Darken function
darken(light, dark, x) = (light | (dark & x), (light & x) | (x ^ dark))
# Darken2 function
darken2(light, dark, x) = (light | x, (light & x) | dark)
~~~
One could easily check that these functions do their jobs when `x = 1` and
leave the data unchanged when `x = 0`.
---
## Image format
Images are made of *layers*, each of which describe the mask for an operation.
When an image is rendered, *bopti* draws some of those layers in the vram
using the operation functions.
* Non-transparent monochrome images only have one layer, which describes the mask for the `draw` operation.
* Transparent monochrome images have two layers. The first describes the mask
for the `draw` operation, while the other is the `alpha` operation mask (which
means that it indicates which pixels are transparent).
* Non-transparent gray images also have two layers: one for each
[gray buffer](gray-engine). Both are for the `draw` operation.
* Transparent gray images have three layers. Two of them constitute the two-bit
color information for the `draw` operation, and the third is the `alpha`
operation mask.
* Semi-transparent monochrome images also have three layers. Two are used to
store the two-bit transparency level information (0 is opaque, 3 is fully
transparent), and the third indicates the color.
Layers are encoded as a bit map. The image is split into a *grid*, which is
made of 32-pixel *columns*, and an *end*.
32 32 32 e
+------+------+------+---+
| | | | |
| | | | |
| | | | |
+------+------+------+---+
Bitmap
The first bytes of the layer data is the column data. Each column is encoded
as a 32-bit integer array from top to bottom. Columns are written from left to
right. The end is encoded as an 8-bit or 16-bit integer array depending on its
size, and written from top to bottom. Additionally, 0 to 3 NUL (0x00) bytes are
added to make the layer size a multiple of 4 (to allow 32-bit access to the
column data of the following layer).
In case of big images (see the image structure below), the end is expanded to
a 32-pixel column to improve performance.
The image itself is a structure of the following kind (in case of small
images):
```c
struct Image
{
unsigned char magic;
unsigned char format;
unsigned char width;
unsigned char height;
const uint32_t data[];
} __attribute__((aligned(4)));
```
For bigger images (`width` > 255 or `height` > 255), both `width` and `height`
are set to `0` and the actual size information is written on two shorts just
where the data resides:
```c
struct BigImage
{
unsigned char magic;
unsigned char format;
unsigned char null_width; /* contains 0 */
unsigned char null_height; /* contains 0 */
unsigned short width;
unsigned short height;
const uint32_t data[];
} __attribute__((aligned(4)));
```
This does not create a memory loss because a two-byte gap was needed to make
the data 4-aligned.
* The `magic` number, which is common to all the file formats of *gint*,
identifies the file type and version of the structure. *bopti* will not render
an image which is not encoded for its specific version.
* The `format` attribute describes the layer distribution, as specified by the
following enum:
```c
enum ImageFormat
{
ImageFormat_Mono = 0x01,
ImageFormat_MonoAlpha = 0x09,
ImageFormat_Gray = 0x06,
ImageFormat_GrayAlpha = 0x0e,
ImageFormat_GreaterAlpha = 0x31,
ImageFormat_ColorMask = 0x07,
ImageFormat_AlphaMask = 0x38,
};
```
`Alpha` refers to uniform transparency. The only format that supports
partial transparency is `GreaterAlpha`, and it is always encoded as
monochrome (because using gray pixels would lead to 9 different colors,
which is rather unoptimized). Gray images with partial transparency
will be refused by *fxconv*.
* The `width` and `height` attributes are exactly what you expect.
* The `data` is simply made of all the layers put one after another. Layers are
put in the following order:
[0] Monochrome `draw` layer
[1] Dark gray `draw` layer
[2] Light gray `draw` layer
[3] Uniform `alpha` layer
[4] First semi-`alpha` layer
[5] Second semi-`alpha` layer
Not every format uses the six layers, of course. The layers used by
each format may be found by reading the position of the `1`'s in the
corresponding `enum ImageFormat` entry. Layers that are not needed are
skipped.

View file

@ -213,7 +213,7 @@ void bitmap_test(void)
Image *sybl = &binary_resources_symbol_start;
Image *sybl2 = &binary_resources_symbol2_start;
enum BlendingMode blend = Blend_Or;
// enum BlendingMode blend = Blend_Or;
uint32_t a32 = 0xffffffff;
int black_bg = 0;
int key;
@ -223,20 +223,22 @@ void bitmap_test(void)
dclear();
if(black_bg) dreverse_area(0, 0, 127, 63);
dimage(opt, 0, 57, Blend_Invert);
dimage(opt, 0, 57);
dimage(sprites, 2 & a32, 2, blend);
dimage(sybl, 30 & a32, 40, blend);
dimage(sybl2, 62 & a32, 40, blend);
dimage(sprites, 2 & a32, 2);
dimage(sybl, 30 & a32, 40);
dimage(sybl2, 62 & a32, 40);
dupdate();
key = getkey();
if(key == KEY_EXIT) break;
/*
if(key == KEY_F1) blend = Blend_Or;
if(key == KEY_F2) blend = Blend_And;
if(key == KEY_F3) blend = Blend_Invert;
*/
if(key == KEY_F4) black_bg = !black_bg;
if(key == KEY_F5) a32 ^= 31;
@ -257,7 +259,7 @@ void text_test(void)
extern Font binary_resources_font_start;
Font *font = &binary_resources_font_start;
print_configure(font, Blend_Or);
print_configure(font);
dclear();
@ -290,18 +292,23 @@ void gray_test(void)
gray_getDelays(&light, &dark);
gray_start();
dimage(illustration, 0, 0, Blend_Or);
while(1)
{
gclear();
dimage(illustration, 0, 0);
gclear_area(64, 0, 127, 63);
// gupdate();
key = getkey();
if(key == KEY_EXIT) break;
/*
if(key == KEY_F1) gray_setDelays(--light, dark);
if(key == KEY_F2) gray_setDelays(++light, dark);
if(key == KEY_F5) gray_setDelays(light, --dark);
if(key == KEY_F6) gray_setDelays(light, ++dark);
*/
}
gray_stop();
@ -367,7 +374,7 @@ int main(void)
extern Font binary_resources_font_start;
Font *font = &binary_resources_font_start;
print_configure(font, Blend_Or);
print_configure(font);
int app;

Binary file not shown.

View file

@ -21,7 +21,7 @@
((c) >= 'a' && (c) <= 'f'))
// Character manipulation macros.
#define tolower(c) (isupper(c) ? (c)|32 : (c))
#define toupper(c) (islower(c) ? (c)&~32 : (c))
#define tolower(c) (isupper(c) ? (c) | 32 : (c))
#define toupper(c) (islower(c) ? (c) & ~32 : (c))
#endif // _CTYPE_H

View file

@ -1,62 +1,29 @@
//---
//
// gint drawing module: display
//
// Handles vram manipulation and drawing for plain monochrome display.
//
//---
#ifndef _DISPLAY_H
#define _DISPLAY_H 1
//---
// Included submodules.
// Heading declarations.
//---
#include <tales.h>
//---
// Types and constants.
//---
enum Color
{
Color_White = 0, // White (AND 0)
Color_Black = 1, // Black (OR 1)
Color_None = 2, // Transparent (NOP)
Color_Invert = 3, // Invert (XOR 1)
};
/*
enum BlendingMode
Describes the various blending modes available for drawing images.
Blending modes may be combined for special effects! For instance:
- Or Only sets black pixels.
- And Only erases white pixels.
- Or | And Fully draws the bitmap.
- Invert Inverts pixels where the bitmap is black.
- Or | Invert Erases black pixels.
Adding Checker to an existing combination limits the operation to
pixels (x, y) that satisfy (x + y) & 1 == 1.
Operations are done in the following order : Or - Invert - And.
*/
enum BlendingMode
{
Blend_Or = 0x01,
Blend_Invert = 0x02,
Blend_And = 0x04,
Blend_Checker = 0x10,
};
/*
enum ImageFormat
Describes the various image formats available. Colors may be encoded
as monochrome (1 layer) or gray (2 layers). Whatever the color map, any
bitmap may also have an additional alpha layer.
*/
enum ImageFormat
{
ImageFormat_Mono = 0x01,
ImageFormat_Gray = 0x02,
ImageFormat_Alpha = 0x10,
ImageFormat_ColorMask = 0x0f,
Color_White = 0,
Color_Light = 1,
Color_Dark = 2,
Color_Black = 3,
Color_None = 4,
Color_Invert = 5,
};
/*
@ -69,10 +36,10 @@ enum ImageFormat
struct Image
{
unsigned char magic;
unsigned char format;
unsigned char width;
unsigned char height;
unsigned char format;
const unsigned char __attribute__((aligned(4))) data[];
@ -94,12 +61,10 @@ typedef struct Image Image;
/*
display_getLocalVRAM()
Returns the local video ram. This function always return the same
address.
Returns the local video ram address. This function always return the
same address.
The buffer returned by this function should not be used directly when
running the gray engine.
@return Video ram address of the monochrome display module.
*/
void *display_getLocalVRAM(void);
@ -114,13 +79,11 @@ void *display_getCurrentVRAM(void);
/*
display_useVRAM()
Changes the current video ram address. The argument *MUST* be a
4-aligned 1024 buffer ; otherwise any drawing operation will crash the
program.
Changes the current video ram address. The argument MUST be a 4-
aligned 1024-byte buffer; otherwise any drawing operation will crash
the program.
This function will most likely have no effect when running the gray
engine.
@arg New video ram address.
*/
void display_useVRAM(void *vram);
@ -132,7 +95,8 @@ void display_useVRAM(void *vram);
/*
dupdate()
Displays the vram on the physical screen.
Displays the vram on the physical screen. Does nothing when the gray
engine is running.
*/
void dupdate(void);
@ -144,23 +108,15 @@ void dclear(void);
/*
dclear_area()
Clears an area of the video ram.
@arg x1
@arg y1
@arg x2
@arg y2
Clears an area of the video ram. Both (x1, y1) and (x2, y2) are
cleared.
*/
void dclear_area(int x1, int y1, int x2, int y2);
/*
dreverse_area()
Reverses an area of the screen.
@arg x1
@arg y1
@arg x2
@arg y2
Reverses an area of the vram. (x1, y1) and (x2, y2) are reversed as
well.
*/
void dreverse_area(int x1, int y1, int x2, int y2);
@ -172,45 +128,25 @@ void dreverse_area(int x1, int y1, int x2, int y2);
/*
dpixel()
Puts a pixel on the screen.
@arg x
@arg y
@arg color
Puts a pixel in the vram.
*/
void dpixel(int x, int y, enum Color color);
/*
dline()
Draws a line on the screen. Automatically optimizes horizontal and
Draws a line in the vram. Automatically optimizes horizontal and
vertical lines.
Uses an algorithm written by PierrotLL for MonochromeLib.
@arg x1
@arg y1
@arg x2
@arg y2
@arg color
*/
void dline(int x1, int y1, int x2, int y2, enum Color color);
//---
// Image drawing.
//---
/*
dimage()
Displays an image in the vram. Does a real lot of optimization.
@arg image
@arg x
@arg y
@arg mode
Displays a monochrome image in the vram. Does a real lot of
optimization.
*/
void dimage(struct Image *image, int x, int y, enum BlendingMode mode);
void dimage(struct Image *image, int x, int y);

View file

@ -1,10 +1,26 @@
//---
//
// gint core/drawing module: gray
//
// Runs the gray engine and handles drawing for the dual-buffer system.
//
//---
#ifndef _GRAY_H
#define _GRAY_H 1
#include <display.h>
//---
// Public API.
// Engine control.
//---
/*
gray_runs()
Returns 1 if the gray engine is running, 0 otherwise.
*/
int gray_runs(void);
/*
gray_start()
Starts the gray engine. The control of the screen is transferred to the
@ -33,10 +49,7 @@ void *gray_darkVRAM(void);
/*
gray_getDelays()
Returns the gray engine delays.
@arg light Will be set if non-NULL.
@arg dark Will be set if non-NULL.
Returns the gray engine delays. Pointers are not set if NULL.
*/
void gray_getDelays(int *light, int *dark);
@ -47,23 +60,72 @@ void gray_getDelays(int *light, int *dark);
Finding values that give proper grays is quite the hard part of the
gray engine. Usual values are about 1000, with light being between 75
and 90% of dark.
@arg light Light gray duration (the lower).
@arg dark Dark gray duration (the higher).
*/
void gray_setDelays(int light, int dark);
//---
// Global drawing functions.
//---
/*
gupdate()
Swaps the vram buffer sets.
*/
void gupdate(void);
/*
gclear()
Clears the video ram.
*/
void gclear(void);
/*
gclear_area()
Clears an area of the video ram. End points (x1, y1) and (x2, y2) are
included.
*/
void gclear_area(int x1, int y1, int x2, int y2);
/*
greverse_area()
Reverses an area of the vram. End points (x1, y1) and (x2, y2) are
included.
*/
void greverse_area(int x1, int y1, int x2, int y2);
//---
// Local drawing functions.
//---
/*
gpixel()
Puts a pixel in the vram.
*/
void gpixel(int x, int y, enum Color color);
/*
gline()
Draws a line in the vram. Automatically optimizes special cases.
*/
void gline(int x1, int y1, int x2, int y2, enum Color color);
/*
gimage()
Displays a gray image in the vram.
*/
void gimage(struct Image *image, int x, int y);
//---
// Internal API.
// Referenced here for documentation purposes only. Do not call.
//--
/*
gray_swap()
Swaps the vram buffers.
*/
void gray_swap(void);
/*
gray_interrupt()
Answers a timer interrupt. Swaps the two buffers.

View file

@ -23,8 +23,6 @@ void abort(void);
exit()
Stops the program execution with the given status code, after calling
the exit handlers.
@arg status
*/
void exit(int status);
@ -38,10 +36,6 @@ void exit(int status);
malloc()
Allocs 'size' bytes and returns a pointer to a free memory area.
Returns NULL on error.
@arg size Size to allocate, in bytes.
@return Memory area address, or NULL.
*/
void *malloc(size_t size);
@ -49,19 +43,12 @@ void *malloc(size_t size);
calloc()
Allocs 'n' elements of size 'size' and wipes the memory area. Returns
NULL on error.
@arg n Element number.
@arg size Element size.
@return Memory area address, or NULL.
*/
void *calloc(size_t n, size_t size);
/*
free()
Frees a memory block allocated with malloc().
@arg ptr Pointer to free.
*/
void free(void *ptr);

View file

@ -8,8 +8,6 @@
// Types and constants.
//---
enum BlendingMode;
/*
enum ImageFormat
This type holds information about the characters in the font. Each bit
@ -78,7 +76,6 @@ typedef struct Font Font;
//---
// Generic functions.
//---
@ -90,7 +87,7 @@ typedef struct Font Font;
@arg font
@arg mode
*/
void print_configure(struct Font *font, enum BlendingMode mode);
void print_configure(struct Font *font);
/*
print_raw()

BIN
libc.a

Binary file not shown.

BIN
libgint.a

Binary file not shown.

View file

@ -1,74 +1,152 @@
//---
//
// gint drawing module: bopti
//
// bopti does every job related to display images. There is only one
// public function, but there are lots of internal optimizations.
//
// Some bit-manipulation expressions may look written out of nowhere. The
// idea is always the same: get a part of the image in an 'operator',
// which is a 32-bit variable, shift this operator so that its bits
// correspond to the desired position for the bitmap on the screen, and
// edit the video-ram long entry which correspond to this position using
// a 'mask' that indicates which bits of the operator contain information.
//
//---
#include <display.h>
#include <stdint.h>
#include <gray.h>
//---
// Image drawing. There is only one public function dimage(), but there
// are lots of local methods and optimizations.
//
// Some expressions may look nonsense sometimes. The procedure is always
// the same : get a part of the image in an operator, shift it depending
// on the drawing x-coordinate, compute a mask that indicates which bits
// of the operator contain information, and modify a vram long using the
// operator and the mask.
// Heading declarations.
//---
// This pointer is set by bopti().
static int *vram;
/*
enum Channel
Determines the kind of information written into a layer. Every image is
made of one or more channels.
*/
enum Channel
{
Channel_Mono = 0x01,
Channel_Light = 0x02,
Channel_Dark = 0x04,
Channel_FullAlpha = 0x08,
Channel_LightAlpha = 0x10,
Channel_DarkAlpha = 0x20,
};
/*
enum Format
Describes the various combination of channels allowed by bopti.
*/
enum Format
{
Format_Mono = Channel_Mono,
Format_MonoAlpha = Format_Mono | Channel_FullAlpha,
Format_Gray = Channel_Light | Channel_Dark,
Format_GrayAlpha = Format_Gray | Channel_FullAlpha,
Format_GreaterAlpha = Format_Mono | Channel_LightAlpha |
Channel_DarkAlpha
};
// These pointers are set by dimage() to avoid having to repeatedly determine
// which video ram to use.
static int *vram, *v1, *v2;
// The following variables refer to parameters that do not change during the
// drawing operation (at least for the time of a layer). They could be passed
// on by every function from the module, but this would be heavy and useless.
static enum Channel channel;
static int height;
static void (*op)(int offset, uint32_t operator, uint32_t op_mask);
//---
// Drawing operation.
//---
/*
bopti_op()
Operates on a vram long. The operator will often not contain 32 bits of
image information. In this case, the bits outside the image must be set
to 0 for Or and Invert operations... 1 for And operations. Which means
that the calling procedure must indicate what part of the operator
belongs to the image, which is done through the image_mask argument.
@arg offset Vram offset where edition is planned.
@arg operator Longword to operate with.
@arg image_mask Part of the operator that is inside the image.
@arg mode Operation mode.
image information. Since neutral bits are not the same for all
operations, the op_mask argument indicates which bits should be used
for the operation. Which operation has to be done is determined by the
channel setting.
*/
static void bopti_op(int offset, uint32_t operator, uint32_t image_mask,
enum BlendingMode mode)
static void bopti_op_mono(int offset, uint32_t operator, uint32_t op_mask)
{
if(mode & Blend_Checker) operator &= 0x55555555;
if(mode & Blend_Or) vram[offset] |= operator;
if(mode & Blend_Invert) vram[offset] ^= operator;
operator |= ~image_mask;
if(mode & Blend_And) vram[offset] &= operator;
operator &= op_mask;
switch(channel)
{
case Channel_Mono:
vram[offset] |= operator;
break;
case Channel_FullAlpha:
vram[offset] &= ~operator;
break;
default:
break;
}
}
static void bopti_op_gray(int offset, uint32_t operator, uint32_t op_mask)
{
operator &= op_mask;
switch(channel)
{
case Channel_Mono:
v1[offset] |= operator;
v2[offset] |= operator;
break;
case Channel_Light:
v1[offset] |= operator;
break;
case Channel_Dark:
v2[offset] |= operator;
break;
case Channel_FullAlpha:
v1[offset] &= ~operator;
v2[offset] &= ~operator;
break;
case Channel_LightAlpha:
case Channel_DarkAlpha:
break;
}
}
/*
bopti_grid() -- general form
bopti_grid_a32() -- when x is a multiple of 32
Draws a layer whose length is a multiple of 32.
Draws the grid at the beginning of a layer's data. The length of this
grid is always a multiple of 32.
The need for bopti_grid_a32() is not only linked to optimization,
because one of the bit shifts in bopti_grid() will reach 32 when x is
a multiple of 32, which is undefined behavior.
@arg layer Raw column data (column data is located at the
beginning of layer data).
@arg x
@arg y
@arg column_number
@arg height
@arg mode
because bopti_grid() will perform a 32-bit shift when x is a multiple
of 32, which is undefined behavior.
*/
static void bopti_grid_a32(const uint32_t *layer, int x, int y,
int column_number, int height, enum BlendingMode mode)
int column_count)
{
int vram_column_offset = (y << 2) + (x >> 5);
int vram_offset = vram_column_offset;
int column, row;
for(column = 0; column < column_number; column++)
for(column = 0; column < column_count; column++)
{
for(row = 0; row < height; row++)
{
bopti_op(vram_offset, *layer, 0xffffffff, mode);
(*op)(vram_offset, *layer, 0xffffffff);
layer++;
vram_offset += 4;
}
@ -77,10 +155,15 @@ static void bopti_grid_a32(const uint32_t *layer, int x, int y,
vram_offset = vram_column_offset;
}
}
static void bopti_grid(const uint32_t *layer, int x, int y, int column_number,
int height, enum BlendingMode mode)
static void bopti_grid(const uint32_t *layer, int x, int y, int column_count)
{
if(!column_count) return;
if(!(x & 31))
{
bopti_grid_a32(layer, x, y, column_count);
return;
}
const uint32_t *p1, *p2;
uint32_t l1, l2;
int right_column, line;
@ -95,33 +178,26 @@ static void bopti_grid(const uint32_t *layer, int x, int y, int column_number,
uint32_t and_mask_0 = 0xffffffff >> shift2;
uint32_t and_mask_1 = 0xffffffff << shift1;
if(!column_number) return;
if(!(x & 31))
{
bopti_grid_a32(layer, x, y, column_number, height, mode);
return;
}
// Initializing two pointers. Since the columns are written one after
// another, they will be updated directly to parse the whole grid.
p1 = layer - height;
p2 = layer;
// Drawing vram longwords, using pairs of columns.
for(right_column = 0; right_column <= column_number; right_column++)
for(right_column = 0; right_column <= column_count; right_column++)
{
and_mask = 0xffffffff;
if(right_column == 0) and_mask &= and_mask_0;
if(right_column == column_number) and_mask &= and_mask_1;
if(right_column == column_count) and_mask &= and_mask_1;
for(line = 0; line < height; line++)
{
l1 = (right_column > 0) ? (*p1) : (0);
l2 = (right_column < column_number) ? (*p2) : (0);
l2 = (right_column < column_count) ? (*p2) : (0);
p1++, p2++;
operator = (l1 << shift1) | (l2 >> shift2);
bopti_op(vram_offset, operator, and_mask, mode);
(*op)(vram_offset, operator, and_mask);
vram_offset += 4;
}
@ -131,65 +207,44 @@ static void bopti_grid(const uint32_t *layer, int x, int y, int column_number,
}
/*
bopti_rest_get()
Returns the line of a bitmap, whose width is lower than 32. The given
pointer is read and set according to the bitmap width.
@arg ptr Address of data pointer.
@arg size Element size (should be 1, 2, or 4 bytes).
bopti_end_get()
Returns an operator for the end of a line, whose width is lower than 32
(by design: otherwise, it would have been a column). The given pointer
is read and updated so that it points to the next line at the end of
the operation.
*/
static uint32_t bopti_rest_get(const unsigned char **data, int size)
static uint32_t bopti_end_get1(const unsigned char **data)
{
uint32_t line;
if(size == 4)
{
line = *((uint32_t *)*data);
*data += 4;
return line;
}
else if(size == 2)
{
line = *((uint16_t *)*data);
*data += 2;
return line;
}
else
{
line = **data;
(*data)++;
return line;
}
uint32_t operator = **data;
*data += 1;
return operator;
}
static uint32_t bopti_end_get2(const unsigned char **data)
{
uint32_t operator = *((uint16_t *)*data);
*data += 2;
return operator;
}
/*
bopti_rest() -- general form
bopti_rest_nover() -- when the bitmap does not overlap two longs
bopti_rest_nover() -- when the end does not overlap two vram longs
Draws a bitmap, whose width is lower than 32. It is called the 'rest'
since the biggest part will be drawn by bopti_grid().
@arg rest Ending data. Encoded on 1, 2 or 4 bytes per line
depending on the rest width.
@arg x
@arg y
@arg width Rest width.
@arg height
@arg mode
Draws the end of a layer, which can be considered as a whole layer
whose with is lower than 32. (Actually is it lower or equal to 16;
otherwise it would have been a column and the end would be empty).
*/
static void bopti_rest_nover(const unsigned char *rest, int x, int y,
int width, int height, enum BlendingMode mode)
static void bopti_end_nover(const unsigned char *end, int x, int y, int width)
{
int element_size = (width > 16) ? (4) : (width > 8) ? (2) : (1);
uint32_t (*get)(const unsigned char **data) =
(width > 8) ? bopti_end_get2 : bopti_end_get1;
int vram_offset = (y << 2) + (x >> 5);
int row;
// We *have* shift >= 0 because of this function's 'no overlap'
// requirement.
int shift_base = (4 - element_size) << 3;
int shift_base = (width > 8) ? 16 : 24;
int shift = shift_base - (x & 31);
uint32_t and_mask = (0xffffffff << (32 - width)) >> (x & 31);
@ -197,28 +252,28 @@ static void bopti_rest_nover(const unsigned char *rest, int x, int y,
for(row = 0; row < height; row++)
{
operator = bopti_rest_get(&rest, element_size);
operator = (*get)(&end);
operator <<= shift;
bopti_op(vram_offset, operator, and_mask, mode);
(*op)(vram_offset, operator, and_mask);
vram_offset += 4;
}
}
static void bopti_rest(const unsigned char *rest, int x, int y, int width,
int height, enum BlendingMode mode)
static void bopti_end(const unsigned char *end, int x, int y, int width)
{
if((x & 31) + width <= 32)
{
bopti_rest_nover(rest, x, y, width, height, mode);
bopti_end_nover(end, x, y, width);
return;
}
int element_size = (width > 16) ? (4) : (width > 8) ? (2) : (1);
uint32_t (*get)(const unsigned char **data) =
(width > 8) ? (bopti_end_get2) : (bopti_end_get1);
int vram_offset = (y << 2) + (x >> 5);
int row;
int shift_base = (4 - element_size) << 3;
int shift_base = (width > 8) ? 16 : 24;
int shift1 = (x & 31) - shift_base;
int shift2 = shift_base + 32 - (x & 31);
@ -229,99 +284,150 @@ static void bopti_rest(const unsigned char *rest, int x, int y, int width,
for(row = 0; row < height; row++)
{
row_data = bopti_rest_get(&rest, element_size);
row_data = (*get)(&end);
operator = row_data >> shift1;
bopti_op(vram_offset, operator, and_mask_0, mode);
(*op)(vram_offset, operator, and_mask_0);
operator = row_data << shift2;
bopti_op(vram_offset + 1, operator, and_mask_1, mode);
(*op)(vram_offset + 1, operator, and_mask_1);
vram_offset += 4;
}
}
//---
// Wrappers and various functions.
//---
/*
bopti()
Draws an image layer in the video ram.
@arg bitmap Raw layer data.
@arg x
@arg y
@arg width
@arg height
@arg mode
Draws a layer in the video ram.
*/
static void bopti(const unsigned char *layer, int x, int y, int width,
int height, enum BlendingMode mode)
static void bopti(const unsigned char *layer, int x, int y, int columns,
int end_size)
{
int column_number = width >> 5;
int rest_width = width & 31;
const unsigned char *end = layer + ((columns * height) << 2);
int end_x = x + (columns << 5);
vram = display_getCurrentVRAM();
bopti_grid((const uint32_t *)layer, x, y, columns);
if(end_size) bopti_end(end, end_x, y, end_size);
}
// Skipping 'column_number' columns of 'height' longs.
const unsigned char *rest = layer + ((column_number * height) << 2);
int rest_x = x + (width - rest_width);
/*
getStructure()
Determines the image size and data pointer.
*/
static void getStructure(struct Image *img, int *width, int *height,
int *layer_size, const unsigned char **data, int *columns,
int *end_size)
{
int column_count, end, end_bytes, layer;
bopti_grid((const uint32_t *)layer, x, y, column_number, height, mode);
if(!rest_width) return;
bopti_rest(rest, rest_x, y, rest_width, height, mode);
// Large images.
if(!img->width && !img->height)
{
if(width) *width = (img->data[0] << 8) | img->data[1];
if(height) *height = (img->data[2] << 8) | img->data[3];
if(data) *data = img->data + 4;
column_count = (*width + 31) >> 5;
end = end_bytes = 0;
}
else
{
if(width) *width = img->width;
if(height) *height = img->height;
if(data) *data = img->data;
column_count = img->width >> 5;
end = img->width & 31;
end_bytes =
!end ? 0 :
end <= 8 ? 1 :
end <= 16 ? 2 :
4;
}
// The layer size must be rounded to a multiple of 4.
layer = img->height * ((column_count << 2) + end_bytes);
if(layer & 3) layer += 4 - (layer & 3);
if(columns) *columns = column_count;
if(end_size) *end_size = end;
if(layer_size) *layer_size = layer;
}
/*
dimage()
Displays an image in the vram.
@arg image
@arg x
@arg y
@arg mode
Displays a monochrome image in the video ram.
*/
void dimage(struct Image *image, int x, int y, enum BlendingMode mode)
void dimage(struct Image *img, int x, int y)
{
int width = image->width;
int height = image->height;
const unsigned char *data = (const unsigned char *)&(image->data);
int width, layer_size, columns, end;
int format = img->format, i = 0;
const unsigned char *data;
if(image->magic != 0xb7) return;
if(img->magic != 0xb7) return;
if(img->format != Format_Mono && img->format != Format_MonoAlpha)
return;
op = bopti_op_mono;
// Computing the layer size.
int columns = image->width >> 5;
int rest = image->width & 31;
int rest_size =
!rest ? 0 :
rest <= 8 ? 1 :
rest <= 16 ? 2 :
4;
int layer_size = ((columns << 2) + rest_size) * image->height;
// The layer size must be a multiple of 4.
if(layer_size & 3) layer_size += 4 - (layer_size & 3);
// 'height' refers to a static variable for this file.
getStructure(img, &width, &height, &layer_size, &data, &columns, &end);
switch(image->format & ImageFormat_ColorMask)
vram = display_getCurrentVRAM();
while(format)
{
case ImageFormat_Mono:
if(image->format & ImageFormat_Alpha)
// Drawing every layer, in order of formats.
if(format & 1)
{
bopti(data + layer_size, x, y, width, height,
Blend_And);
}
bopti(data, x, y, width, height, mode);
break;
case ImageFormat_Gray:
if(image->format & ImageFormat_Alpha)
{
bopti(data + 2 * layer_size, x, y, width, height,
Blend_And);
channel = (1 << i);
bopti(data, x, y, columns, end);
data += layer_size;
}
display_useVRAM(gray_darkVRAM());
bopti(data, x, y, width, height, mode);
display_useVRAM(gray_lightVRAM());
bopti(data + layer_size, x, y, width, height, mode);
break;
format >>= 1;
i++;
}
}
/*
gimage()
Displays a gray image in the video ram.
*/
void gimage(struct Image *img, int x, int y)
{
int width, layer_size, columns, end;
int format = img->format, i = 0;
const unsigned char *data;
if(img->magic != 0xb7) return;
op = bopti_op_gray;
// 'height' refers to a static variable for this file.
getStructure(img, &width, &height, &layer_size, &data, &columns, &end);
v1 = gray_lightVRAM();
v2 = gray_darkVRAM();
while(format)
{
// Drawing every layer, in order of formats.
if(format & 1)
{
channel = (1 << i);
bopti(data, x, y, columns, end);
data += layer_size;
}
format >>= 1;
i++;
}
}

View file

@ -1,27 +1,29 @@
/*
display
Handles vram manipulation and drawing.
:: Rectangle masks
The concept of 'rectangle masks' is used several times in this module.
It consists in saying that an operation that affects a rectangle acts
the same on all the lines (considering that only the lines that
intersect the rectangle are changed) and therefore it is possible to
represent the behavior on a single line using 'masks' that indicate
whether a pixel is affected (1) or not (0).
For example when clearing the screen rectangle (16, 16, 112, 48), the
masks will represent information '16 to 112 on x-axis', and will hold
the following values : 0000ffff, ffffffff, ffffffff and ffff0000. These
masks can then be used by setting vram[offset] &= ~masks[i]. This
appears to be very flexible : for instance, reversing a rectangle of
vram only needs vram[offset] ^= masks[i].
This technique can also be used in more subtle cases with more complex
patterns, but within this module it is unlikely to happen.
*/
//---
//
// gint drawing module: display
//
// Handles vram manipulation and drawing.
//
//
// :: Rectangle masks
//
// The concept of 'rectangle masks' is used several times in this module.
// It is based on the fact that an operation that affects a rectangle acts
// the same on all its lines. Therefore the behavior of the operation is
// determined by its behavior on a single line, which is represented using
// 'masks' whose bits indicate whether a pixel is affected (1) or not (0).
//
// For example when clearing the screen rectangle (16, 16, 112, 48), the
// masks will represent information '16 to 112 on x-axis', and will hold
// the following values : 0000ffff, ffffffff, ffffffff and ffff0000. These
// masks can then be used by setting vram[offset] &= ~masks[i]. This
// appears to be very flexible : for instance, vram[offset] ^= masks[i]
// will reverse the pixels in the same rectangle.
//
// This technique can also be used in more subtle cases with more complex
// patterns, but within this module it is unlikely to happen.
//
//---
#include <screen.h>
#include <display.h>
@ -47,14 +49,10 @@ static int *vram = local_vram;
/*
adjust()
Adjusts the given rectangle coordinates to ensure that :
- The rectangle is entirely contained in the screen,
- x1 < x2 and y1 < y2,
- the rectangle is entirely contained in the screen
- x1 < x2
- y1 < y2
which is needed when working with screen rectangles.
@arg x1
@arg y1
@arg x2
@arg y2
*/
static void adjust(int *x1, int *y1, int *x2, int *y2)
{
@ -75,11 +73,8 @@ static void adjust(int *x1, int *y1, int *x2, int *y2)
getmasks()
Computes the rectangle masks needed to affect pixels located between x1
and x2 (both included).
@arg x1
@arg x2
@arg masks Four-integer-array pointer.
and x2 (both included). The four masks are stored in the third argument
(seen as an array).
*/
static void getmasks(int x1, int x2, unsigned int *masks)
{
@ -99,11 +94,10 @@ static void getmasks(int x1, int x2, unsigned int *masks)
// number of null bits to add in l1 and l2.
x1 &= 31;
// Inverting x2 is here the same as computing 32 - x, since 32 is a
// power of 2 (actually it creates positive bits at the left but those
// ones are removed by the bitwise-and mask).
// power of 2 (positive bits at the left are removed by the mask).
x2 = ~x2 & 31;
// Setting the last masks.
// Setting the first and last masks.
masks[l1] &= (0xffffffff >> x1);
masks[l2] &= (0xffffffff << x2);
}
@ -116,34 +110,37 @@ static void getmasks(int x1, int x2, unsigned int *masks)
/*
display_getLocalVRAM()
Returns the local video ram.
@return Video ram address.
Returns the local video ram address. This function always return the
same address.
The buffer returned by this function should not be used directly when
running the gray engine.
*/
void *display_getLocalVRAM(void)
inline void *display_getLocalVRAM(void)
{
return (void *)local_vram;
}
/*
display_getCurrentVRAM()
Returns the current vido ram.
@return Video ram address.
Returns the current video ram. This function usually returns the
parameter of the last call to display_useVRAM(), unless the gray engine
is running (in which case the result is undefined). Returns the local
vram address by default.
*/
void *display_getCurrentVRAM(void)
inline void *display_getCurrentVRAM(void)
{
return (void *)vram;
}
/*
display_useVRAM()
Changes the current video ram address. Expects a *4-aligned* 1024-byte
buffer.
@arg New video ram address.
Changes the current video ram address. The argument MUST be a 4-
aligned 1024-byte buffer ; otherwise any drawing operation will crash
the program.
This function will most likely have no effect when running the gray
engine.
*/
void display_useVRAM(void *ptr)
inline void display_useVRAM(void *ptr)
{
vram = (int *)ptr;
}
@ -160,6 +157,7 @@ void display_useVRAM(void *ptr)
*/
void dupdate(void)
{
if(gray_runs()) return;
screen_display((const void *)local_vram);
}
@ -175,12 +173,8 @@ void dclear(void)
/*
dclear_area()
Clears an area of the vram using rectangle masks.
@arg x1
@arg y1
@arg x2
@arg y2
Clears an area of the vram using rectangle masks. Both (x1, y1) and
(x2, y2) are cleared.
*/
void dclear_area(int x1, int y1, int x2, int y2)
{
@ -188,23 +182,19 @@ void dclear_area(int x1, int y1, int x2, int y2)
adjust(&x1, &y1, &x2, &y2);
getmasks(x1, x2, masks);
int offset = y1 << 2;
int begin = y1 << 2;
int end = (y2 + 1) << 2;
int i;
for(i = 0; i < 4; i++) masks[i] = ~masks[i];
while(offset < end) vram[offset] &= masks[offset & 3], offset++;
for(i = begin; i < end; i++) vram[i] &= masks[i & 3];
}
/*
dreverse_area()
Reverses an area of the vram. This function is a simple application of
the rectangle masks concept.
@arg x1
@arg y1
@arg x2
@arg y2
the rectangle masks concept. (x1, y1) and (x2, y2) are reversed as
well.
*/
void dreverse_area(int x1, int y1, int x2, int y2)
{
@ -212,10 +202,25 @@ void dreverse_area(int x1, int y1, int x2, int y2)
adjust(&x1, &y1, &x2, &y2);
getmasks(x1, x2, masks);
int offset = y1 << 2;
int begin = y1 << 2;
int end = (y2 + 1) << 2;
int i;
while(offset < end) vram[offset] ^= masks[offset & 3], offset++;
if(gray_runs())
{
int *v1 = gray_lightVRAM();
int *v2 = gray_darkVRAM();
for(i = begin; i < end; i++)
{
v1[i] ^= masks[i & 3];
v2[i] ^= masks[i & 3];
}
}
else for(i = begin; i < end; i++)
{
vram[i] ^= masks[i & 3];
}
}
@ -226,15 +231,12 @@ void dreverse_area(int x1, int y1, int x2, int y2)
/*
dpixel()
Puts a pixel on the screen.
@arg x
@arg y
@arg color
Puts a pixel in the vram.
*/
void dpixel(int x, int y, enum Color color)
{
if((unsigned int)x > 127 || (unsigned int)y > 63) return;
int offset = (y << 2) + (x >> 5);
int mask = 0x80000000 >> (x & 31);
@ -248,12 +250,12 @@ void dpixel(int x, int y, enum Color color)
vram[offset] |= mask;
break;
case Color_None:
return;
case Color_Invert:
vram[offset] ^= mask;
break;
default:
break;
}
}
@ -261,12 +263,6 @@ void dpixel(int x, int y, enum Color color)
dline()
Draws a line on the screen. Automatically optimizes horizontal and
vertical lines.
@arg x1
@arg y1
@arg x2
@arg y2
@arg color
*/
static void dhline(int x1, int x2, int y, enum Color color)
@ -275,9 +271,60 @@ static void dhline(int x1, int x2, int y, enum Color color)
int offset = y << 2;
int i;
// Swapping x1 and x2 if needed.
if(x1 > x2) x1 ^= x2, x2 ^= x1, x1 ^= x2;
getmasks(x1, x2, masks);
switch(color)
int *v1 = gray_lightVRAM();
int *v2 = gray_darkVRAM();
if(gray_runs()) switch(color)
{
case Color_White:
for(i = 0; i < 4; i++)
{
v1[offset + i] &= ~masks[i];
v2[offset + i] &= ~masks[i];
}
break;
case Color_Light:
for(i = 0; i < 4; i++)
{
v1[offset + i] |= masks[i];
v2[offset + i] &= ~masks[i];
}
break;
case Color_Dark:
for(i = 0; i < 4; i++)
{
v1[offset + i] &= ~masks[i];
v2[offset + i] |= masks[i];
}
break;
case Color_Black:
for(i = 0; i < 4; i++)
{
v1[offset + i] |= masks[i];
v2[offset + i] |= masks[i];
}
break;
case Color_Invert:
for(i = 0; i < 4; i++)
{
v1[offset + i] ^= masks[i];
v2[offset + i] ^= masks[i];
}
break;
default:
break;
}
else switch(color)
{
case Color_White:
for(i = 0; i < 4; i++) vram[offset + i] &= ~masks[i];
@ -287,12 +334,12 @@ static void dhline(int x1, int x2, int y, enum Color color)
for(i = 0; i < 4; i++) vram[offset + i] |= masks[i];
break;
case Color_None:
return;
case Color_Invert:
for(i = 0; i < 4; i++) vram[offset + i] ^= masks[i];
break;
default:
break;
}
}
@ -302,7 +349,61 @@ static void dvline(int y1, int y2, int x, enum Color color)
int end = (y2 << 2) + (x >> 5);
int mask = 0x80000000 >> (x & 31);
switch(color)
int *v1 = gray_lightVRAM();
int *v2 = gray_darkVRAM();
if(gray_runs()) switch(color)
{
case Color_White:
while(offset <= end)
{
v1[offset] &= ~mask;
v2[offset] &= ~mask;
offset += 4;
}
break;
case Color_Light:
while(offset <= end)
{
v1[offset] |= mask;
v2[offset] &= ~mask;
offset += 4;
}
break;
case Color_Dark:
while(offset <= end)
{
v1[offset] &= ~mask;
v2[offset] |= mask;
offset += 4;
}
break;
case Color_Black:
while(offset <= end)
{
v1[offset] |= mask;
v2[offset] |= mask;
offset += 4;
}
break;
case Color_Invert:
while(offset <= end)
{
v1[offset] ^= mask;
v2[offset] ^= mask;
offset += 4;
}
break;
default:
break;
}
else switch(color)
{
case Color_White:
while(offset <= end) vram[offset] &= ~mask, offset += 4;
@ -312,12 +413,12 @@ static void dvline(int y1, int y2, int x, enum Color color)
while(offset <= end) vram[offset] |= mask, offset += 4;
break;
case Color_None:
return;
case Color_Invert:
while(offset <= end) vram[offset] ^= mask, offset += 4;
break;
default:
break;
}
}

View file

@ -91,10 +91,6 @@ static void kdelay(void)
krow()
Reads a keyboard row. Works like krow() for SH7705. See gint_7705.c for
more details.
@arg row Row to check (0 <= row <= 9).
@return Bit-based representation of pressed keys in the checked row.
*/
static int krow(int row)
{

View file

@ -1,3 +1,11 @@
//---
//
// gint core/drawing module: gray
//
// Runs the gray engine and handles drawing for the dual-buffer system.
//
//---
#include <display.h>
#include <gray.h>
#include <screen.h>
@ -9,8 +17,25 @@ const void *vrams[4];
static int current = 0;
static int delays[2];
static int runs = 0;
#define GRAY_PRESCALER TIMER_Po_64
//---
// Engine control.
//---
/*
gray_runs()
Returns 1 if the gray engine is running, 0 otherwise.
*/
inline int gray_runs(void)
{
return runs;
}
/*
gray_start()
Starts the gray engine. The control of the screen is transferred to the
@ -19,6 +44,7 @@ static int delays[2];
void gray_start(void)
{
timer_start(TIMER_GRAY, delays[0], GRAY_PRESCALER, gray_interrupt, 0);
runs = 1;
}
/*
@ -29,6 +55,8 @@ void gray_start(void)
void gray_stop(void)
{
timer_stop(TIMER_GRAY);
runs = 0;
display_useVRAM(display_getLocalVRAM());
}
@ -36,26 +64,23 @@ void gray_stop(void)
gray_lightVRAM()
Returns the module's gray vram address.
*/
void *gray_lightVRAM(void)
inline void *gray_lightVRAM(void)
{
return (void *)vrams[current];
return (void *)vrams[current & 2];
}
/*
gray_lightVRAM()
Returns the module's dark vram address.
*/
void *gray_darkVRAM(void)
inline void *gray_darkVRAM(void)
{
return (void *)vrams[current + 1];
return (void *)vrams[(current & 2) | 1];
}
/*
gray_getDelays()
Returns the gray engine delays.
@arg light Will be set if non-NULL.
@arg dark Will be set if non-NULL.
Returns the gray engine delays. Pointers are not set if NULL.
*/
void gray_getDelays(int *light, int *dark)
{
@ -66,9 +91,6 @@ void gray_getDelays(int *light, int *dark)
/*
gray_setDelays()
Changes the gray engine delays.
@arg light Light gray duration (the lower).
@arg dark Dark gray duration (the higher).
*/
void gray_setDelays(int light, int dark)
{
@ -82,15 +104,6 @@ void gray_setDelays(int light, int dark)
// Internal API.
//---
/*
gray_swap()
Swaps the vram buffers.
*/
void gray_swap(void)
{
current = (current + 2) & 3;
}
/*
gray_interrupt()
Answers a timer interrupt. Swaps the buffers.
@ -113,6 +126,130 @@ void gray_init(void)
vrams[2] = (const void *)internal_vrams[1];
vrams[3] = (const void *)internal_vrams[2];
delays[0] = 3269;
delays[1] = 6987;
delays[0] = 900;
delays[1] = 1000;
}
//---
// Global drawing functions
//---
/*
gupdate()
Swaps the vram buffer sets.
*/
inline void gupdate(void)
{
current ^= 2;
}
/*
gclear()
Clears the video ram.
*/
void gclear(void)
{
int *v1 = gray_lightVRAM();
int *v2 = gray_darkVRAM();
int i;
for(i = 0; i < 256; i++) v1[i] = v2[i] = 0;
}
/*
gclear_area()
Clears an area of the video ram. End points (x1, y1) and (x2, y2) are
included.
*/
void gclear_area(int x1, int y1, int x2, int y2)
{
display_useVRAM(gray_lightVRAM());
dclear_area(x1, y1, x2, y2);
display_useVRAM(gray_darkVRAM());
dclear_area(x1, y1, x2, y2);
}
/*
greverse_area()
Reverses an area of the vram. End points (x1, y1) and (x2, y2) are
included.
*/
void greverse_area(int x1, int y1, int x2, int y2)
{
display_useVRAM(gray_lightVRAM());
dreverse_area(x1, y1, x2, y2);
display_useVRAM(gray_darkVRAM());
dreverse_area(x1, y1, x2, y2);
}
//---
// Local drawing functions.
//---
/*
gpixel()
Puts a pixel in the vram.
*/
void gpixel(int x, int y, enum Color color)
{
if((unsigned int)x > 127 || (unsigned int)y > 63) return;
int offset = (y << 2) + (x >> 5);
int mask = 0x80000000 >> (x & 31);
int *v1 = gray_lightVRAM();
int *v2 = gray_lightVRAM();
switch(color)
{
case Color_White:
v1[offset] &= ~mask;
v2[offset] &= ~mask;
break;
case Color_Light:
v1[offset] |= mask;
v2[offset] &= ~mask;
break;
case Color_Dark:
v1[offset] &= ~mask;
v2[offset] |= mask;
break;
case Color_Black:
v1[offset] |= mask;
v2[offset] |= mask;
break;
case Color_Invert:
v1[offset] ^= mask;
v2[offset] ^= mask;
break;
default:
break;
}
}
/*
gline()
Draws a line in the vram. Automatically optimizes special cases.
*/
void gline(int x1, int y1, int x2, int y2, enum Color color)
{
enum Color c1, c2;
if(color == Color_None) return;
else if(color == Color_Invert) c1 = c2 = Color_Invert;
else c1 = color & 1, c2 = color >> 1;
display_useVRAM(gray_lightVRAM());
dline(x1, y1, x2, y2, c1);
display_useVRAM(gray_darkVRAM());
dline(x1, y1, x2, y2, c2);
}

View file

@ -6,7 +6,6 @@
#include <tales.h>
static struct Font *font;
static enum BlendingMode mode;
//---
// Local functions.
@ -198,10 +197,9 @@ static int update(uint32_t *operators, int height, int available,
@arg font
@arg mode
*/
void print_configure(struct Font *next_font, enum BlendingMode next_mode)
void print_configure(struct Font *next_font)
{
font = next_font;
mode = next_mode;
}
/*