| /* |
| * Copyright 2016 Google Inc. |
| * |
| * See file CREDITS for list of people who contributed to this |
| * project. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but without any warranty; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| |
| #include "base/graphics/bitmap.h" |
| #include "base/graphics/graphics.h" |
| #include "base/xalloc.h" |
| |
| static int graphics_load_bitmap_file_v2(DcBitmap *bitmap, void *file) |
| { |
| printf("Bitmap version 2 not supported.\n"); |
| return -1; |
| } |
| |
| static int graphics_load_bitmap_file_v3(DcBitmap *bitmap, void *file) |
| { |
| BitmapFileHeader *file_header_ptr = (BitmapFileHeader *)file; |
| uint32_t bitmap_offset; |
| memcpy(&bitmap_offset, &file_header_ptr->bitmap_offset, |
| sizeof(bitmap_offset)); |
| BitmapHeaderV3 header; |
| memcpy(&header, (uint8_t *)file + sizeof(BitmapFileHeader), |
| sizeof(header)); |
| int bpp = header.bits_per_pixel; |
| |
| // Check for things we don't support. |
| if (header.compression != 0 || bpp >= 16) { |
| printf("Non-palette bitmaps are not supported.\n"); |
| return -1; |
| } |
| if (bpp != 1 && bpp != 4 && bpp != 8) { |
| printf("Unsupported bits per pixel.\n"); |
| return -1; |
| } |
| |
| uintptr_t palette_offset = |
| sizeof(BitmapFileHeader) + sizeof(BitmapHeaderV3); |
| int palette_size = bitmap_offset - palette_offset; |
| BitmapPaletteElementV3 *palette = xmalloc(palette_size); |
| memcpy(palette, (uint8_t *)file + palette_offset, palette_size); |
| |
| int32_t width = header.width, height = header.height; |
| int extra = width % 4; |
| const int32_t padding = extra ? (4 - extra) : 0; |
| |
| int positive_height = height > 0; |
| if (!positive_height) |
| height = -height; |
| DcColor *pixels = xzalloc(height * width * sizeof(DcColor)); |
| |
| uint8_t *cur_data = (uint8_t *)file + bitmap_offset; |
| int bit = 0; |
| // Loop over all the pixels in the image. |
| uint32_t pixel = positive_height ? (width * (height - 1)) : 0; |
| for (uint32_t i = 0; i < width * height; i++) { |
| int index = 0; |
| // Extract the index value for this pixel. |
| if (bpp >= 8) { |
| // For pixels one byte or larger, glue them together |
| // one byte at a time. Pixels are big endian. |
| for (int i = 0; i < bpp / 8; i++) { |
| index <<= 8; |
| index |= *cur_data++; |
| } |
| } else { |
| // For pixels smaller than a byte, extract some bits |
| // from the right byte. Bytes are big endian. |
| const uint8_t mask = (1 << bpp) - 1; |
| bit += bpp; |
| index = (*cur_data >> (8 - bit)) & mask; |
| if (bit == 8) { |
| bit = 0; |
| cur_data++; |
| } |
| } |
| |
| pixels[pixel].red = palette[index].red; |
| pixels[pixel].green = palette[index].green; |
| pixels[pixel].blue = palette[index].blue; |
| |
| // Keep track of position. |
| pixel++; |
| if (!(pixel % width)) { |
| cur_data += padding; |
| if (positive_height) |
| pixel -= 2 * width; |
| } |
| } |
| free(palette); |
| |
| bitmap->height = height; |
| bitmap->width = width; |
| bitmap->pixels = pixels; |
| return 0; |
| } |
| |
| static int graphics_load_bitmap_file_v4(DcBitmap *bitmap, void *file) |
| { |
| printf("Bitmap version 4 not supported.\n"); |
| return -1; |
| } |
| |
| |
| int graphics_load_bitmap_file(DcBitmap *bitmap, void *file) |
| { |
| BitmapFileHeader *file_header = (BitmapFileHeader *)file; |
| |
| if (file_header->signature[0] != 'B' || |
| file_header->signature[1] != 'M') { |
| printf("Bitmap signature check failed.\n"); |
| return -1; |
| } |
| |
| if (bitmap->pixels) { |
| printf("Bitmap already contains data.\n"); |
| return -1; |
| } |
| |
| uint32_t header_size; |
| memcpy(&header_size, file_header + 1, sizeof(header_size)); |
| switch (header_size) { |
| case 12: |
| return graphics_load_bitmap_file_v2(bitmap, file); |
| case 40: |
| return graphics_load_bitmap_file_v3(bitmap, file); |
| case 108: |
| return graphics_load_bitmap_file_v4(bitmap, file); |
| default: |
| printf("Unrecognized bitmap format.\n"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| |
| int graphics_render_char(DcBitmap *bitmap, const DcFont *font, |
| const DcColor *fg, const DcColor *bg, uint8_t c) |
| { |
| int glyph_size = font->height * font->width; |
| int glyph_bytes = (glyph_size + 7) / 8; |
| uint8_t *glyph = &font->glyph_bytes[glyph_bytes * c]; |
| |
| DcColor *pixels; |
| if (bitmap->pixels) { |
| if (bitmap->height != font->height || |
| bitmap->width != font->width) { |
| printf("Font size mismatch.\n"); |
| return 1; |
| } |
| pixels = bitmap->pixels; |
| } else { |
| pixels = xmalloc(glyph_size * sizeof(DcColor)); |
| } |
| |
| int bit_idx = 7; |
| uint8_t *glyph_byte = glyph; |
| for (int i = 0; i < glyph_size; i++) { |
| const DcColor *color = |
| ((*glyph_byte >> bit_idx) & 0x1) ? fg : bg; |
| memcpy(&bitmap->pixels[i], color, sizeof(*color)); |
| if (bit_idx-- == 0) { |
| bit_idx = 7; |
| glyph_byte++; |
| } |
| } |
| |
| if (!bitmap->pixels) { |
| bitmap->pixels = pixels; |
| bitmap->height = font->height; |
| bitmap->width = font->width; |
| } |
| |
| return 0; |
| } |