#include /* bopti_op() Operates on a vram long. The operator will often not contain 32 bits of 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_mono(int offset, uint32_t operator, uint32_t op_mask) { 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 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 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_count) { int vram_column_offset = (y << 2) + (x >> 5); int vram_offset = vram_column_offset; int column, row; for(column = 0; column < column_count; column++) { for(row = 0; row < height; row++) { (*op)(vram_offset, *layer, 0xffffffff); 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_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; 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; // 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_count; right_column++) { and_mask = 0xffffffff; if(right_column == 0) and_mask &= and_mask_0; 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_count) ? (*p2) : (0); p1++, p2++; operator = (l1 << shift1) | (l2 >> shift2); (*op)(vram_offset, operator, and_mask); vram_offset += 4; } vram_column_offset++; vram_offset = vram_column_offset; } } /* 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_end_get1(const unsigned char **data) { 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 end does not overlap two vram longs 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_end_nover(const unsigned char *end, int x, int y, int width) { 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 = (width > 8) ? 16 : 24; 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 = (*get)(&end); operator <<= shift; (*op)(vram_offset, operator, and_mask); vram_offset += 4; } } static void bopti_end(const unsigned char *end, int x, int y, int width) { if((x & 31) + width <= 32) { bopti_end_nover(end, x, y, width); return; } 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 = (width > 8) ? 16 : 24; 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 = (*get)(&end); operator = row_data >> shift1; (*op)(vram_offset, operator, and_mask_0); operator = row_data << shift2; (*op)(vram_offset + 1, operator, and_mask_1); vram_offset += 4; } } //--- // Wrappers and various functions. //--- /* bopti() Draws a layer in the video ram. */ static void bopti(const unsigned char *layer, int x, int y, int columns, int end_size) { const unsigned char *end = layer + ((columns * height) << 2); int end_x = x + (columns << 5); bopti_grid((const uint32_t *)layer, x, y, columns); if(end_size) bopti_end(end, end_x, y, end_size); } /* 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; // 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; }