328 lines
8.0 KiB
C
328 lines
8.0 KiB
C
#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;
|
|
}
|
|
|
|
int element_size = (width > 16) ? (4) : (width > 8) ? (2) : (1);
|
|
int vram_offset = (y << 2) + (x >> 5);
|
|
int row;
|
|
|
|
int shift_base = (4 - element_size) << 3;
|
|
int shift1 = (x & 31) - shift_base;
|
|
int shift2 = shift_base + 32 - (x & 31);
|
|
|
|
uint32_t and_mask_0 = 0xffffffff >> (x & 31);
|
|
uint32_t and_mask_1 = 0xffffffff << (64 - width - (x & 31));
|
|
|
|
uint32_t row_data, operator;
|
|
|
|
for(row = 0; row < height; row++)
|
|
{
|
|
row_data = bopti_rest_get(&rest, element_size);
|
|
|
|
operator = row_data >> shift1;
|
|
bopti_op(vram_offset, operator, and_mask_0, mode);
|
|
|
|
operator = row_data << shift2;
|
|
bopti_op(vram_offset + 1, operator, and_mask_1, 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
|
|
*/
|
|
|
|
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);
|
|
|
|
if(image->magic != 0xb7) return;
|
|
|
|
// 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;
|
|
}
|
|
}
|