#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; } 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; } }