Began to re-write bopti. Bitmap tests changed. Added a continuous integration file.

This commit is contained in:
lephe 2016-05-06 20:47:15 +02:00
parent e1a51dae21
commit 13aad50ab7
14 changed files with 366 additions and 375 deletions

3
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,3 @@
build:
script:
- make

View File

@ -37,7 +37,7 @@ lib = -lgcc -L. -lgint -lc
# Gint library.
src-lib = crt0.c syscalls.s \
gint.c gint_vbr.s gint_7705.c gint_7305.c \
mpu.c keyboard.c screen.c display.c gray.c timer.c
mpu.c keyboard.c screen.c display.c bopti.c gray.c timer.c
hea-lib = 7305.h 7705.h gint.h \
stdlib.h \
mpu.h keyboard.h screen.h display.h gray.h timer.h
@ -52,7 +52,8 @@ hdr-std = $(addprefix include/, $(hea-std))
# Test application.
src-app = ginttest.c
res-app = icon.o swords.o sprites.o symbol.o symbol2.o
img-app = bitmap_opt.bmp swords.bmp sprites.bmp symbol.bmp symbol2.bmp
res-app = $(addprefix build/, $(addsuffix .o, $(img-app)))
#
@ -81,11 +82,23 @@ ginttest.g1a: libgint.a $(src-app) $(res-app)
@ echo "\033[32;1mBinary file size: "`stat -c %s $(bin)`" bytes\033[0m"
# @ sh3eb-elf-objdump -h build/ginttest.elf
#
# Resource management.
#
build/%.c.o: src/%.c $(hdr-lib) $(hdr-std)
$(cc) $(cflags) -O2 -c $< -o $@
build/%.s.o: src/%.s
$(as) -c $^ -o $@
build/%.bmp.o: resources/%.bmp
fxconv $^ -o $@
# File gint.c should not be optimized... looks like attribute((interrupt_
# handler)) doesn't like it. (It could be a gint bug also, I should check.)
build/gint.c.o: src/gint.c $(hdr-lib) $(hdr-std)
$(cc) $(cflags) -c $< -o $@
@ -93,8 +106,6 @@ build/gint.c.o: src/gint.c $(hdr-lib) $(hdr-std)
$(cc) $(cflags) -c $< -o $@
%.s.o: %.s
$(as) -c $^ -o $@
%.o: %.bmp
fxconv $^
@ -103,7 +114,8 @@ build/gint.c.o: src/gint.c $(hdr-lib) $(hdr-std)
#
clean:
@ rm -f $(obj-lib) $(obj-std) $(obj-app) $(bin) $(elf)
@ rm -f $(obj-lib) $(obj-std) $(obj-app) $(res-app)
@ rm -f $(bin) $(elf)
mrproper: clean
@ rm -f build/*
@ rm -f ginttest.g1a libc.a libgint.a

4
TODO
View File

@ -1,4 +1,6 @@
- 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)
@ -7,5 +9,7 @@
- exhaustive save for setjmp()
- registers that need to be saved when configuring gint
- check possible bug for optimization of __attribute__((interrupt_handler))
_ 7305.h
_ libc

View File

@ -5,11 +5,11 @@
#include <display.h>
#include <timer.h>
extern unsigned int gint_vbr, bgint, egint, gint_data;
extern unsigned int bgint, egint;
/*
A few procedures for displaying text in the system's vram.
*/
//---
// A few procedures for displaying text in the system's vram.
//---
extern void __Print(const char *msg, int x, int y);
@ -283,16 +283,45 @@ void btest_gint_icon(void)
void bitmap_test(void)
{
extern Image binary_symbol_start;
extern Image binary_symbol2_start;
extern Image binary_icon_start;
extern Image binary_sprites_start;
extern Image binary_swords_start;
extern Image binary_resources_bitmap_opt_start;
extern Image binary_resources_symbol_start;
extern Image binary_resources_symbol2_start;
extern Image binary_resources_sprites_start;
extern Image binary_resources_swords_start;
extern const void *vrams[4];
Image *opt = &binary_resources_bitmap_opt_start;
Image *sprites = &binary_resources_sprites_start;
Image *sybl = &binary_resources_symbol_start;
Image *sybl2 = &binary_resources_symbol2_start;
Image *sybl = &binary_symbol_start;
Image *sybl2 = &binary_symbol2_start;
enum BlendingMode blend = Blend_Or;
uint32_t a32 = 0xffffffff;
int key;
while(1)
{
dclear();
if(blend != Blend_Or) dreverse_area(0, 0, 127, 63);
dimage(opt, 0, 57, Blend_Invert);
dimage(sprites, 2 & a32, 2, blend);
dimage(sybl, 98 & a32, 4, blend);
dimage(sybl2, 98 & a32, 24, blend);
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) a32 ^= 31;
}
return;
dclear();
dreverse_area(0, 0, 127, 30);
@ -308,10 +337,8 @@ void bitmap_test(void)
dimage(sybl2, 40, 20, Blend_Or | Blend_And);
dimage(sybl2, 90, 20, Blend_Or | Blend_Invert);
dimage(&binary_icon_start, 2, 35, Blend_Or);
dreverse_area(35, 31, 127, 63);
dimage(&binary_sprites_start, 50, 31, Blend_And);
dimage(&binary_resources_sprites_start, 50, 31, Blend_And);
dupdate();
/*
@ -333,7 +360,7 @@ void bitmap_test(void)
while(getkey() != KEY_EXE);
dclear();
dimage(&binary_swords_start, 20, 20, Blend_Or);
dimage(&binary_resources_swords_start, 20, 20, Blend_Or);
dupdate();
while(getkey() != KEY_EXE);

Binary file not shown.

BIN
libc.a

Binary file not shown.

BIN
libgint.a

Binary file not shown.

BIN
resources/bitmap_opt.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 554 B

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 B

After

Width:  |  Height:  |  Size: 242 B

299
src/bopti.c Normal file
View File

@ -0,0 +1,299 @@
#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.
//---
// This pointer is set by bopti().
static int *vram;
/*
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.
*/
static void bopti_op(int offset, uint32_t operator, uint32_t image_mask,
enum BlendingMode mode)
{
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;
}
/*
bopti_grid() -- general form
bopti_grid_a32() -- when x is a multiple of 32
Draws a layer whose length is 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
*/
static void bopti_grid_a32(const uint32_t *layer, int x, int y,
int column_number, int height, enum BlendingMode mode)
{
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(row = 0; row < height; row++)
{
bopti_op(vram_offset, *layer, 0xffffffff, mode);
layer++;
vram_offset += 4;
}
vram_column_offset++;
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)
{
const uint32_t *p1, *p2;
uint32_t l1, l2;
int right_column, line;
int vram_column_offset = (y << 2) + (x >> 5);
int vram_offset = vram_column_offset;
int shift1 = 32 - (x & 31);
int shift2 = (x & 31);
uint32_t operator, and_mask;
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++)
{
and_mask = 0xffffffff;
if(right_column == 0) and_mask &= and_mask_0;
if(right_column == column_number) and_mask &= and_mask_1;
for(line = 0; line < height; line++)
{
l1 = (right_column > 0) ? (*p1) : (0);
l2 = (right_column < column_number) ? (*p2) : (0);
p1++, p2++;
operator = (l1 << shift1) | (l2 >> shift2);
bopti_op(vram_offset, operator, and_mask, mode);
vram_offset += 4;
}
vram_column_offset++;
vram_offset = vram_column_offset;
}
}
/*
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).
*/
static uint32_t bopti_rest_get(const unsigned char **data, int size)
{
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;
}
}
/*
bopti_rest() -- general form
bopti_rest_nover() -- when the bitmap does not overlap two 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
*/
static void bopti_rest_nover(const unsigned char *rest, int x, int y,
int width, int height, enum BlendingMode mode)
{
int element_size = (width > 16) ? (4) : (width > 8) ? (2) : (1);
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 = shift_base - (x & 31);
uint32_t and_mask = (0xffffffff << (32 - width)) >> (x & 31);
uint32_t operator;
for(row = 0; row < height; row++)
{
operator = bopti_rest_get(&rest, element_size);
operator <<= shift;
bopti_op(vram_offset, operator, and_mask, mode);
vram_offset += 4;
}
}
static void bopti_rest(const unsigned char *rest, int x, int y, int width,
int height, enum BlendingMode mode)
{
if((x & 31) + width <= 32)
{
bopti_rest_nover(rest, x, y, width, height, mode);
return;
}
}
/*
bopti()
Draws an image layer in the video ram.
@arg bitmap Raw layer data.
@arg x
@arg y
@arg width
@arg height
@arg mode
*/
static void bopti(const unsigned char *layer, int x, int y, int width,
int height, enum BlendingMode mode)
{
int column_number = width >> 5;
int rest_width = width & 31;
vram = display_getCurrentVRAM();
// Skipping 'column_number' columns of 'height' longs.
const unsigned char *rest = layer + ((column_number * height) << 2);
int rest_x = x + (width - rest_width);
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);
}
/*
dimage()
Displays an image in the vram.
@arg image
@arg x
@arg y
@arg mode
*/
void dimage(struct Image *image, int x, int y, enum BlendingMode mode)
{
int width = image->width;
int height = image->height;
const unsigned char *data = (const unsigned char *)&(image->data);
// 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);
switch(image->format & ImageFormat_ColorMask)
{
case ImageFormat_Mono:
if(image->format & ImageFormat_Alpha)
{
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);
}
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;
}
}

View File

@ -370,357 +370,3 @@ void dline(int x1, int y1, int x2, int y2, enum Color color)
dpixel(x2, y2, color);
}
//---
// 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.
//---
/*
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 produre 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.
*/
static void bopti_op(int offset, uint32_t operator, uint32_t image_mask,
enum BlendingMode mode)
{
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;
}
/*
bopti_grid() -- general form
bopti_grid_a32() -- when x is a multiple of 32
Draws a layer, whose width is a multiple of 32, in the vram.
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 column_number
@arg width
@arg height
@arg x
@arg y
@arg mode
*/
static void bopti_grid_a32(const uint32_t *layer, int column_number, int width,
int height, int x, int y, enum BlendingMode mode)
{
int vram_column_offset = (y << 2) + (x >> 5);
int vram_offset = vram_column_offset;
int column, line;
uint32_t operator, and_mask;
uint32_t rightest_and_mask;
if(width & 31) rightest_and_mask = ~(0xffffffff >> (width & 31));
else rightest_and_mask = 0xffffffff;
for(column = 0; column < column_number; column++)
{
for(line = 0; line < height; line++)
{
operator = *layer++;
and_mask = (column < column_number - 1) ?
(0xffffffff) : (rightest_and_mask);
bopti_op(vram_offset, operator, and_mask, mode);
vram_offset += 4;
}
vram_column_offset++;
vram_offset = vram_column_offset;
}
}
static void bopti_grid(const uint32_t *layer, int column_number, int width,
int height, int x, int y, enum BlendingMode mode)
{
const uint32_t *p1, *p2;
uint32_t l1, l2;
int right_column, line;
int vram_column_offset = (y << 2) + (x >> 5);
int vram_offset = vram_column_offset;
int shift1 = 32 - (x & 31);
int shift2 = (x & 31);
int combined_shift_last = shift1 + 32 - (width & 31);
uint32_t operator, and_mask;
uint32_t and_mask_0 = 0xffffffff >> shift2;
uint32_t and_mask_1 = (0xffffffff) << combined_shift_last;
if(!column_number) return;
if(!(x & 31))
{
bopti_grid_a32(layer, column_number, width, height, x, y,
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(line = 0; line < height; line++)
{
l1 = (right_column > 0) ? (*p1) : (0);
l2 = (right_column < column_number) ? (*p2) : (0);
p1++, p2++;
operator = (l1 << shift1) | (l2 >> shift2);
and_mask = 0xffffffff;
if(!right_column) and_mask &= and_mask_0;
if(right_column == column_number)
and_mask &= and_mask_1;
bopti_op(vram_offset, operator, and_mask, mode);
vram_offset += 4;
}
vram_column_offset++;
vram_offset = vram_column_offset;
}
}
/*
bopti_rest8() -- general form, width below 8
bopti_rest8_nover() -- when the rest does not meet two longs
bopti_rest16() -- general form, width below 16
bopti_rest16_nover() -- when the rest does not meet two longs
Draw rests of row size of 8 and 16 bits, respectively.
@arg rest Rest data, located at the end of the layer data.
@arh width
@arg height
@arg x
@arg y
@arg mode
*/
static void bopti_rest8_nover(const uint8_t *rest, int width, int height,
int x, int y, enum BlendingMode mode)
{
int vram_offset = (y << 2) + (x >> 5);
int shift = x & 31;
uint32_t operator;
uint32_t and_mask = ~(0xffffffff >> width) >> shift;
int line;
for(line = 0; line < height; line++)
{
operator = *rest++;
// Optimization possible ? Probably not.
operator <<= 24;
operator >>= shift;
bopti_op(vram_offset, operator, and_mask, mode);
vram_offset += 4;
}
}
static void bopti_rest8(const uint8_t *rest, int width, int height, int x,
int y, enum BlendingMode mode)
{
if((x & 31) + width < 32)
{
bopti_rest8_nover(rest, width, height, x, y, mode);
return;
}
int vram_offset = (y << 2) + (x >> 5);
int shift1 = (x & 31) - 24;
int shift2 = 56 - (x & 31);
uint32_t and_mask_1 = 0xffffffff >> (x & 31);
uint32_t and_mask_2 = ~(0xffffffff >> ((x & 31) + width - 32));
uint32_t operator;
int line;
for(line = 0; line < height; line++)
{
operator = *rest++;
bopti_op(vram_offset, operator >> shift1, and_mask_1, mode);
bopti_op(vram_offset + 1, operator << shift2, and_mask_2,
mode);
vram_offset += 4;
}
}
static void bopti_rest16_nover(const uint16_t *rest, int width, int height,
int x, int y, enum BlendingMode mode)
{
int vram_offset = (y << 2) + (x >> 5);
int shift = x & 31;
uint32_t operator;
uint32_t and_mask = ~(0xffffffff >> width) >> shift;
int line;
for(line = 0; line < height; line++)
{
operator = *rest++;
// As far as I know, no, we can't optimize this into a single
// shift.
operator <<= 16;
operator >>= shift;
bopti_op(vram_offset, operator, and_mask, mode);
vram_offset += 4;
}
}
static void bopti_rest16(const uint16_t *rest, int width, int height, int x,
int y, enum BlendingMode mode)
{
if((x & 31) + width < 32)
{
bopti_rest16_nover(rest, width, height, x, y, mode);
return;
}
int vram_offset = (y << 2) + (x >> 5);
int shift1 = (x & 31) - 16;
int shift2 = 48 - (x & 31);
uint32_t and_mask_1 = 0xffffffff >> (x & 31);
uint32_t and_mask_2 = ~(0xffffffff >> ((x & 31) + width - 32));
uint32_t operator;
int line;
for(line = 0; line < height; line++)
{
operator = *rest++;
bopti_op(vram_offset, operator >> shift1, and_mask_1, mode);
bopti_op(vram_offset + 1, operator << shift2, and_mask_2,
mode);
vram_offset += 4;
}
}
/*
bopti()
Draws an image layer in the video ram.
@arg bitmap Raw layer data.
@arg x
@arg y
@arg width
@arg height
@arg mode
*/
void bopti(const unsigned char *layer, int x, int y, int width, int height,
enum BlendingMode mode)
{
int column_number = width >> 5;
int rest_width = width & 31;
int grid_width = width & ~31;
if(rest_width > 16)
{
column_number++;
rest_width = 0;
grid_width = width;
}
const unsigned char *rest = layer + ((column_number * height) << 2);
int rest_x = x + (width - rest_width);
bopti_grid((const uint32_t *)layer, column_number, grid_width, height,
x, y, mode);
if(!rest_width) return;
if(rest_width <= 8)
bopti_rest8((const uint8_t *)rest, rest_width, height, rest_x,
y, mode);
else
bopti_rest16((const uint16_t *)rest, rest_width, height,
rest_x, y, mode);
}
/*
dimage()
Displays an image in the vram.
@arg image
@arg x
@arg y
@arg mode
*/
void dimage(struct Image *image, int x, int y, enum BlendingMode mode)
{
int width = image->width;
int height = image->height;
const unsigned char *data = (const unsigned char *)&(image->data);
// 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);
switch(image->format & ImageFormat_ColorMask)
{
case ImageFormat_Mono:
if(image->format & ImageFormat_Alpha)
{
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);
}
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;
}
}