diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..9be67b4 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,3 @@ +build: + script: + - make diff --git a/Makefile b/Makefile index 9fdcb10..a4f7209 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/TODO b/TODO index 8e540a7..8b252f5 100644 --- a/TODO +++ b/TODO @@ -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 diff --git a/ginttest.c b/ginttest.c index 5b5781a..c0c4a78 100644 --- a/ginttest.c +++ b/ginttest.c @@ -5,11 +5,11 @@ #include #include -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); diff --git a/ginttest.g1a b/ginttest.g1a index 4e1da82..923559a 100644 Binary files a/ginttest.g1a and b/ginttest.g1a differ diff --git a/libc.a b/libc.a index 8bf5347..5af4067 100644 Binary files a/libc.a and b/libc.a differ diff --git a/libgint.a b/libgint.a index 4425ae3..65a4c22 100644 Binary files a/libgint.a and b/libgint.a differ diff --git a/resources/bitmap_opt.bmp b/resources/bitmap_opt.bmp new file mode 100644 index 0000000..d3eb232 Binary files /dev/null and b/resources/bitmap_opt.bmp differ diff --git a/sprites.bmp b/resources/sprites.bmp similarity index 100% rename from sprites.bmp rename to resources/sprites.bmp diff --git a/swords.bmp b/resources/swords.bmp similarity index 100% rename from swords.bmp rename to resources/swords.bmp diff --git a/symbol.bmp b/resources/symbol.bmp similarity index 100% rename from symbol.bmp rename to resources/symbol.bmp diff --git a/symbol2.bmp b/resources/symbol2.bmp similarity index 73% rename from symbol2.bmp rename to resources/symbol2.bmp index 9493760..be7ac38 100644 Binary files a/symbol2.bmp and b/resources/symbol2.bmp differ diff --git a/src/bopti.c b/src/bopti.c new file mode 100644 index 0000000..19725fd --- /dev/null +++ b/src/bopti.c @@ -0,0 +1,299 @@ +#include +#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. +//--- + +// 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; + } +} diff --git a/src/display.c b/src/display.c index d004b1e..e6d2d48 100644 --- a/src/display.c +++ b/src/display.c @@ -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; - } -}