From ab17532f672f24afc0c053bed6e030a3c681b5ac Mon Sep 17 00:00:00 2001 From: lephe Date: Mon, 4 Jul 2016 18:30:25 +0200 Subject: [PATCH] Major update for bopti (gray, light still not tested). Reworked out gray engine. --- Makefile | 29 +-- TODO | 47 ++--- doc/bopti.md | 305 +++++++++++++++++++++++++++++++ ginttest.c | 25 ++- ginttest.g1a | Bin 18760 -> 20136 bytes include/ctype.h | 4 +- include/display.h | 130 ++++---------- include/gray.h | 90 ++++++++-- include/stdlib.h | 13 -- include/tales.h | 5 +- libc.a | Bin 1770 -> 1770 bytes libgint.a | Bin 27912 -> 30732 bytes src/bopti.c | 450 ++++++++++++++++++++++++++++------------------ src/display.c | 281 +++++++++++++++++++---------- src/gint_7305.c | 4 - src/gray.c | 181 ++++++++++++++++--- src/tales.c | 4 +- 17 files changed, 1105 insertions(+), 463 deletions(-) create mode 100644 doc/bopti.md diff --git a/Makefile b/Makefile index 6ed5617..ea3834a 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/TODO b/TODO index 304e43c..1c84e6c 100644 --- a/TODO +++ b/TODO @@ -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)) diff --git a/doc/bopti.md b/doc/bopti.md new file mode 100644 index 0000000..5113f56 --- /dev/null +++ b/doc/bopti.md @@ -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. diff --git a/ginttest.c b/ginttest.c index a1ef815..9e54fd0 100644 --- a/ginttest.c +++ b/ginttest.c @@ -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; diff --git a/ginttest.g1a b/ginttest.g1a index 78b5a4e904db4db9bbb3a118fb7e7d416b3d0abf..5542edb849a37d9e4404701f6f6d54892e631ece 100644 GIT binary patch delta 5434 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 delta 3885 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 diff --git a/include/ctype.h b/include/ctype.h index 0a716be..82748b5 100644 --- a/include/ctype.h +++ b/include/ctype.h @@ -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 diff --git a/include/display.h b/include/display.h index 58a111e..afca785 100644 --- a/include/display.h +++ b/include/display.h @@ -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 - - -//--- -// 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); diff --git a/include/gray.h b/include/gray.h index c685fd1..94653ac 100644 --- a/include/gray.h +++ b/include/gray.h @@ -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 + //--- -// 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. diff --git a/include/stdlib.h b/include/stdlib.h index 8d0fb91..de053e8 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -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); diff --git a/include/tales.h b/include/tales.h index 14e6646..f191c32 100644 --- a/include/tales.h +++ b/include/tales.h @@ -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() diff --git a/libc.a b/libc.a index 79d61d81aea241964cf75ba93c101d51f7c4ee42..c3e9146628e689e83c754692e9e406607c31a5a7 100644 GIT binary patch delta 42 qcmaFG`-*phG`qQ(iMgSX$wZ}WAk)~y#K3anltLz$@Ma$7d&~d>MhkZU delta 42 lcmaFG`-*phG`q36xv{C4@kFI;7<1#4LM9k{GY|7UW&i`V3w{6q diff --git a/libgint.a b/libgint.a index cf2e8b91dfd6bfdfc67b3eb222e72148aa7938ec..045f71e583a98c3d7224161a1fde46db99bf0dd5 100644 GIT binary patch delta 6919 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 U3_ucYU*^SG0lf$y4k70M27p3)DgXcg delta 4041 zcmb7H4^Wfm8GpYoU*HR0C?Oyq1iugvX+^#yPz1qBh!&YBNxe#J>dt9vVVgT5y>{!_y98R{ly({{VsWtNg9L)-?zWeE z@;=Y={+{Rk_dM_OapDeP|AIJ~WXs;XIs56{Y`I)1=UeEnT&~Ji<}9k^#t4K^2|~i^ z6%v1l(4!P}38BYPVk1I4$sVpITTqb(jvq%^We5dHeFCBXmo$?IZHI;a7uvNvP!0?D zU#MyXp-`$ZBgB*WjQc2&E5LuIo{Q@0F?FC?Yp3Z@ZBu(wH?9)aB-VF!E3@je4suHe zDp%#?2F@P;q9V>cCDew8hbqbXvFcQwOOM%-ko9d7R;uY`?U<$Y4scK9w7?6&WZdRmD4_3PMjlr zuiv-r>A2$p&y)DA(r0X=Yacb}B(}(%lX-ovzf?|pqL&=~5b2`<-_un)eBmamWAACiq0S^+x2Ek*P=oe@hLb66w$%(a#nd z(T&fy53@$5B+_dGqp`XAer+Z=fy2!&beL6za@vt#7Yw|SAAcNQFHpUy5!EZ;Q1Ky3 zb*30_>cVUp5}6b!FDay*618&Rfy;(OfS$X~Jq~T{3E&ScKNh_ss$L<81s|o~J zDQ%rLNqZ8;VUoHRwUP#D-3CQdio!Yj0_A!Z5V56n9UwXrCIHbOEs@lxr#7dkoey9< zH!wZ(xak9vA6}lEMWUmDV}TC;nybXy_7oCE+_$FyP6QqqDMAGFd!VZfksClZhR8i2 zw}LDQ;Ew{o*d$BcNf_?~Ru&k46J#KCEFiy)5Gez>J0QQ0P#g(H%Led{kmrHk!hPkh zV6iR-4^U6o06=Ux2>#RxhJ;m<0Sp-%!Y71a zjv-`N2H8VE@KgpTBi~ukQ}Pf#Ericl;e};*Q2#n9)M4FPMOgT|u&00EYJ&n7Yu4hQ z8lp(rj^8n)lR`TlS?W%T(@FXimRF>Q3D58+fXgZrVPR8Xc?EY@q=vpQlv<_Cfd^&d z9xn_FP!9Yel!2m;7UMzg06v(Xww5>Ii4Oq^ z`se_X#=4XwLX36$y7Dsv0ahOvLvYo6>j26GN(+o1=9PR4jK7A5Xtw48D0D!v0o0#> zHv=UEeCUV#qt28MQ{KOhVbv= zjsuB!zFMq`4EiVxF)LchGX7kb*P;XrE~qIWl5iALi2tRCz!z&QBzhlvTT(@^QUpU(g>=A}0&3g?=tG*5>wK6}K zugbQtA@H@5MIsbt>>83d$RWZbS-;jn50RdzI;ly^CqSK4aUewjI?59fqTA2ww$mPA zgViKm-;yJ1C|=)`X5e#fFEsFvxL3W^-08-8?pIA+-0yGAaaT*vbD!;`Ior*0h3%ur zd1Kcw%hDsiV|TIpix~Eh=QmHj$)0dDrjYvY4G%Tvb>FLH!>pWklI(`T;nFpRInyXq zt^_u&G}^hBUEj*|$$bk(raneoXvcalc;S;06G ze5?0slk;D5fIRr%Yb5-;{_c+C2LJkkQJdFjc8AYvnU{#IgU+uHQ@Zck`}H%1drBEA zRLj`3BSgh?{&?4d<06}_o0Zc8B3og3dizY9ZKidmWu|$iX$CQkGx}N4=eh^h7{m9< zgy-j!nPaR%(`V zopF1?w2@WSyS2$C=7l0v#;#;m-I~g}cdv1NT-e+CSJM@*v32}5`lq2ihW*qw7#A^r z++g~sO=cN2_gl|B=PzY;e51c>?zfDBG`%Cal<6jP_YM=vcO7r{Qbxa>a^AF)u8Fp* zun4P<@R8%n35n|>Cw0E=HR|uzjaJOHBo))DGwi7^@~iKQ=226pwW<@9*oM1re|lV-;c2yaVVf4fCm z-f)dsYHlaEuHGVCq6Ytkh5Xkp+~lMezT(t;bO#&vYGVa=x(TAZ8KRuK%6-N`cpHoC zA2=dxcWjcy8(($d$84IhxfZd>ZFFlk@$qevIp{GoMk~{u)EF@)QEqUwjgiNSHFT?3 zl}UVp5{Ctl1$mK)$F#nvI&sRA#+Y`kop5Y9LfJ{@HDl_K#G`iZ9iv86kVn$%Dzl)N zVN>;ftGfYP`px6)Sq;No9?9g(o?+Tn%$NmwKd-jBxyw96!{pp}+*-HNtEYUKa$Y?` zElwEcV65&j3cN_!lm^UQUh=|E%AO6%=JCgPO`UQ51Pn)W%2+9YSB$4zztsB|t8FDd z!_`7*jNKIc_^pHz%RkpKfrAg0%C#U@!gq+5AIBNZ=~)8!Iv|XJx>gi`2diQEBeD_q zcCU*>wJ&y>+E8uX!H(`Ge63s-)n>9<0{@MfO`R>+S03NI_`g|eleMw?IfG_5?+7Yt zu*3*55E2FIi2&-xvL5P2@MDWQ7Q(~3&IIt(iIuuYo#cy1ZHtYhPQkZSstA1X^$x1+ z2(yl)hWOILcXyb0e6S~KQ6o+d3j-f$3%(Sk2%~NQ>%}9jia6niz;*xy+v1G>0f0&t A #include #include + //--- -// 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++; + } + } diff --git a/src/display.c b/src/display.c index e6d2d48..2fa046f 100644 --- a/src/display.c +++ b/src/display.c @@ -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 #include @@ -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; } } diff --git a/src/gint_7305.c b/src/gint_7305.c index 12f310e..d103920 100644 --- a/src/gint_7305.c +++ b/src/gint_7305.c @@ -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) { diff --git a/src/gray.c b/src/gray.c index 8687df9..3e86b2e 100644 --- a/src/gray.c +++ b/src/gray.c @@ -1,3 +1,11 @@ +//--- +// +// gint core/drawing module: gray +// +// Runs the gray engine and handles drawing for the dual-buffer system. +// +//--- + #include #include #include @@ -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); } diff --git a/src/tales.c b/src/tales.c index fa564a2..777134e 100644 --- a/src/tales.c +++ b/src/tales.c @@ -6,7 +6,6 @@ #include 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; } /*