diff options
author | Mikhail Burakov <mburakov@mailbox.org> | 2020-06-29 19:02:09 +0200 |
---|---|---|
committer | Mikhail Burakov <mburakov@mailbox.org> | 2020-06-29 19:02:09 +0200 |
commit | 3261aee563ee7bb0cba7edf4e684ae9833fd9a3f (patch) | |
tree | dd354a1b6e65aa230b059dd8c3c093d30632b76a | |
parent | 3389b2ba80c60009fac91cece3241ec7b2b5b759 (diff) |
Import current pui code to git
-rw-r--r-- | convert.c | 344 | ||||
-rw-r--r-- | main.c | 190 | ||||
-rw-r--r-- | makefile | 32 | ||||
-rw-r--r-- | pui.c | 91 | ||||
-rw-r--r-- | pui.h | 26 | ||||
-rw-r--r-- | sample.bmp | bin | 0 -> 16538 bytes | |||
-rw-r--r-- | sample.ini | 28 |
7 files changed, 711 insertions, 0 deletions
diff --git a/convert.c b/convert.c new file mode 100644 index 0000000..e2191ce --- /dev/null +++ b/convert.c @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2020 Mikhail Burakov. This file is part of Pui. + * + * Pui 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 3 of the License, or + * (at your option) any later version. + * + * Pui 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. + * + * You should have received a copy of the GNU General Public License + * along with Pui. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <ctype.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <err.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> + +enum { + kNoSection = 0, + kSectionElements, + kSectionDefaultPalette, + kSectionActivePalette +}; + +struct PuiElement { + uint8_t x, y, w, h; +}; + +struct MappedFile { + void* data; + size_t size; +}; + +struct BitmapHeader { + uint16_t magic; + uint32_t size; + uint16_t reserved[2]; + uint32_t offset; +} __attribute__((packed)); + +struct DibHeader { + uint32_t size; + int32_t width; + int32_t height; + uint16_t planes; + uint16_t bpp; +} __attribute__((packed)); + +struct Bitmap { + struct MappedFile file; + struct BitmapHeader* bitmap_header; + struct DibHeader* dib_header; + uint8_t* ptr; + int index; +}; + +static void MapFile(const char* file, struct MappedFile* result) { + int fd = open(file, O_RDONLY); + if (fd == -1) err(1, "Failed to open file"); + struct stat buf; + if (fstat(fd, &buf)) err(1, "Failed to stat file"); + void* data = mmap(NULL, (size_t)buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + if (data == MAP_FAILED) err(1, "Failed to map file"); + result->data = data; + result->size = (size_t)buf.st_size; +} + +static void UnmapFile(struct MappedFile* result) { + munmap(result->data, result->size); +} + +static int ReadBitmap(struct Bitmap* bitmap) { + uint8_t* buffer = bitmap->file.data; + if (bitmap->ptr < buffer + bitmap->bitmap_header->offset) return -1; + int result = bitmap->ptr[bitmap->index >> 1] >> ((~bitmap->index & 1) << 2); + if (++bitmap->index == bitmap->dib_header->width) { + size_t stride = ((size_t)(bitmap->dib_header->width >> 1) + 3) & ~3u; + bitmap->ptr -= stride; + bitmap->index = 0; + } + return result & 0x7; +} + +static void OpenBitmap(const char* fname, struct Bitmap* bitmap) { + MapFile(fname, &bitmap->file); + uint8_t* data = bitmap->file.data; + bitmap->bitmap_header = (struct BitmapHeader*)data; + bitmap->dib_header = (struct DibHeader*)(data + sizeof(struct BitmapHeader)); + if (bitmap->bitmap_header->magic != 0x4d42) err(1, "Invalid bitmap magic"); + if (bitmap->dib_header->width < 0 || bitmap->dib_header->height < 0 || + bitmap->dib_header->width > UINT16_MAX || + bitmap->dib_header->height > UINT16_MAX || + bitmap->dib_header->planes != 1 || bitmap->dib_header->bpp != 4) + err(1, "Unsupported bitmap format"); + size_t stride = ((size_t)(bitmap->dib_header->width >> 1) + 3) & ~3u; + bitmap->ptr = data + bitmap->bitmap_header->offset + + stride * (size_t)(bitmap->dib_header->height - 1); +} + +static void Write(int item, FILE* out) { + static int current = -1; + if (current == -1) { + if (item == -1) return; + current = (item & 0xf) << 4; + } else { + if (item == -1) item = 0; + current |= item & 0xf; + fputc(current, out); + current = -1; + } +} + +static void WriteCounter(int counter, FILE* out) { + int pos = 0; + while (counter >> (++pos * 3)) + ; + while (pos-- > 0) Write(0x8 | counter >> (pos * 3), out); +} + +static void WriteSequence(int prev, int counter, FILE* out) { + switch (counter) { + case 1: + Write(prev, out); + break; + case 2: + Write(prev, out); + Write(prev, out); + break; + default: + WriteCounter(counter - 2, out); + Write(prev, out); + break; + } +} + +static void SelectScale(int32_t size, uint8_t* value, uint8_t* scale) { + if (size < 0) err(1, "Size must be non-negative"); + for (int shift = 0, mask = 0;; shift++, mask = mask << 1 | 1) { + if (size & mask) err(1, "Size is not aligned"); + if (size < 0x100 << shift) { + *value = (uint8_t)(size >> shift); + *scale = (uint8_t)shift; + return; + } + } +} + +static int IsMeaningless(int ch) { return !isgraph(ch); } +static int IsMeaningful(int ch) { return isprint(ch); } + +static void SkipGroup(struct MappedFile* file, int (*pred)(int ch)) { + char* ptr = file->data; + size_t size = file->size; + for (; size && pred(*ptr); ptr++, size--) + ; + file->data = ptr; + file->size = size; +} + +static int ChangeSection(struct MappedFile* file) { + char* begin = file->data; + SkipGroup(file, IsMeaningful); + size_t size = (size_t)((char*)file->data - begin); + static const char kElements[] = {'[', 'E', 'l', 'e', 'm', + 'e', 'n', 't', 's', ']'}; + static const char kDefaultPalette[] = {'[', 'D', 'e', 'f', 'a', 'u', + 'l', 't', 'P', 'a', 'l', 'e', + 't', 't', 'e', ']'}; + static const char kActivePalette[] = {'[', 'A', 'c', 't', 'i', 'v', 'e', 'P', + 'a', 'l', 'e', 't', 't', 'e', ']'}; + switch (size) { + case sizeof(kElements): + if (!memcmp(begin, kElements, sizeof(kElements))) return kSectionElements; + break; + case sizeof(kDefaultPalette): + if (!memcmp(begin, kDefaultPalette, sizeof(kDefaultPalette))) + return kSectionDefaultPalette; + break; + case sizeof(kActivePalette): + if (!memcmp(begin, kActivePalette, sizeof(kActivePalette))) + return kSectionActivePalette; + break; + default: + break; + } + return kNoSection; +} + +static int GetIndex(char* ptr, size_t size, const char* prefix, + size_t prefix_length) { + if (size < prefix_length + 3 || memcmp(ptr, prefix, prefix_length) || + !isdigit(ptr[prefix_length]) || !isdigit(ptr[prefix_length + 1]) || + ptr[prefix_length + 2] != '=') + return -1; + return atoi(ptr + prefix_length); +} + +static int ParseElement(struct MappedFile* file, uint8_t scale[2], + struct PuiElement* elements) { + char* ptr = file->data; + SkipGroup(file, IsMeaningful); + size_t size = (size_t)((char*)file->data - ptr); + static const char kElement[] = {'E', 'l', 'e', 'm', 'e', 'n', 't'}; + int index = GetIndex(ptr, size, kElement, sizeof(kElement)); + if (index == -1 || index > 14) return -1; + ptr += sizeof(kElement) + 3; + size -= sizeof(kElement) + 3; + int values[] = {0, 0, 0, 0}; + int *value = values, *end = values + 4; + for (; size; ptr++, size--) { + if ('0' <= *ptr && *ptr <= '9') { + *value = *value * 10 + *ptr - '0'; + } else if (*ptr == ',') { + if (++value == end) break; + } else { + break; + } + } + elements[index].x = (uint8_t)(values[0] >> scale[0]); + elements[index].y = (uint8_t)(values[1] >> scale[1]); + elements[index].w = (uint8_t)(values[2] >> scale[0]); + elements[index].h = (uint8_t)(values[3] >> scale[1]); + return index; +} + +static int ParsePalette(struct MappedFile* file, uint32_t* palette) { + char* ptr = file->data; + SkipGroup(file, IsMeaningful); + size_t size = (size_t)((char*)file->data - ptr); + static const char kColor[] = {'C', 'o', 'l', 'o', 'r'}; + int index = GetIndex(ptr, size, kColor, sizeof(kColor)); + if (index == -1 || index > 7) return -1; + ptr += sizeof(kColor) + 3; + size -= sizeof(kColor) + 3; + uint32_t value = 0; + for (; size; ptr++, size--) { + if ('0' <= *ptr && *ptr <= '9') { + value = value << 4 | (unsigned)(*ptr - '0'); + } else if ('A' <= *ptr && *ptr <= 'F') { + value = value << 4 | (unsigned)(*ptr - 'A' + 10); + } else if ('a' <= *ptr && *ptr <= 'f') { + value = value << 4 | (unsigned)(*ptr - 'a' + 10); + } else { + break; + } + } + palette[index] = (value >> 24 & 0xff) | (value >> 8 & 0xff00) | + (value << 8 & 0xff0000) | (value << 24 & 0xff000000); + return index; +} + +static void CompressDefinition(const struct MappedFile* definition, + uint8_t scale[2], FILE* out) { + struct PuiElement elements[0xf]; + memset(elements, 0, sizeof(elements)); + uint32_t palettes[2][8]; + memset(palettes, 0, sizeof(palettes)); + int section = kNoSection; + int max_element = 0, max_color = 0; + for (struct MappedFile def = *definition;;) { + SkipGroup(&def, IsMeaningless); + if (!def.size) break; + switch (*(char*)def.data) { + case '#': + SkipGroup(&def, IsMeaningful); + continue; + case '[': + section = ChangeSection(&def); + continue; + default: + break; + } + switch (section) { + case kSectionElements: { + int index = ParseElement(&def, scale, elements); + if (index > max_element) max_element = index; + break; + } + case kSectionDefaultPalette: { + int index = ParsePalette(&def, palettes[0]); + if (index > max_color) max_color = index; + break; + } + case kSectionActivePalette: { + int index = ParsePalette(&def, palettes[1]); + if (index > max_color) max_color = index; + break; + } + default: + SkipGroup(&def, IsMeaningful); + break; + } + } + int count_elements = max_element + 1; + int count_colors = max_color + 1; + uint8_t counter = (count_elements << 4 & 0xf0) | (count_colors & 0xf); + fwrite(&counter, 1, 1, out); + fwrite(elements, (size_t)count_elements * sizeof(struct PuiElement), 1, out); + fwrite(palettes[0], (size_t)count_colors * sizeof(uint32_t), 1, out); + fwrite(palettes[1], (size_t)count_colors * sizeof(uint32_t), 1, out); +} + +int main(int argc, char** argv) { + if (argc < 3) err(1, "Usage: %s [source.bmp] [definition.ini]", argv[0]); + struct Bitmap bitmap; + memset(&bitmap, 0, sizeof(bitmap)); + OpenBitmap(argv[1], &bitmap); + uint8_t whs[3], scale[2]; + SelectScale(bitmap.dib_header->width, &whs[0], &scale[0]); + SelectScale(bitmap.dib_header->height, &whs[1], &scale[1]); + struct MappedFile definition; + MapFile(argv[2], &definition); + whs[2] = (uint8_t)(scale[0] << 4 | scale[1]); + fwrite(whs, sizeof(whs), 1, stdout); + CompressDefinition(&definition, scale, stdout); + UnmapFile(&definition); + for (int counter = 0, prev = -1;;) { + int current = ReadBitmap(&bitmap); + if (current == prev || prev == -1) { + counter++; + } else { + WriteSequence(prev, counter, stdout); + if (current == -1) break; + counter = 1; + } + prev = current; + } + UnmapFile(&bitmap.file); + Write(-1, stdout); + return 0; +} @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2020 Mikhail Burakov. This file is part of Pui. + * + * Pui 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 3 of the License, or + * (at your option) any later version. + * + * Pui 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. + * + * You should have received a copy of the GNU General Public License + * along with Pui. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <string.h> + +#include <err.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> +#include <wayland-client.h> + +#include "pui.h" +#include "xdg-shell.h" + +struct Context { + const void* pui; + + struct wl_display* display; + struct wl_registry* registry; + + struct wl_shm* shm; + struct wl_compositor* compositor; + struct xdg_wm_base* wm_base; + + struct wl_surface* surface; + struct xdg_surface* xdg_surface; + struct xdg_toplevel* xdg_toplevel; +}; + +// === Wayland registry === + +static void OnRegistryGlobal(void* data, struct wl_registry* registry, + uint32_t name, const char* interface, + uint32_t version) { + (void)version; + struct Context* ctx = data; + if (!strcmp(interface, wl_compositor_interface.name)) + ctx->compositor = + wl_registry_bind(registry, name, &wl_compositor_interface, 4); + else if (!strcmp(interface, wl_shm_interface.name)) + ctx->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + else if (!strcmp(interface, xdg_wm_base_interface.name)) + ctx->wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 2); +} + +static void OnRegistryGlobalRemove(void* data, struct wl_registry* registry, + uint32_t name) { + (void)data; + (void)registry; + (void)name; +} + +static const struct wl_registry_listener g_registry_listener = { + .global = OnRegistryGlobal, .global_remove = OnRegistryGlobalRemove}; + +// === Wayland WM base listener === + +static void OnPing(void* data, struct xdg_wm_base* xdg_wm_base, + uint32_t serial) { + (void)data; + xdg_wm_base_pong(xdg_wm_base, serial); +} + +static const struct xdg_wm_base_listener g_wm_base_listener = {.ping = OnPing}; + +// === Wayland buffer === + +static void OnBufferRelease(void* data, struct wl_buffer* wl_buffer) { + (void)data; + wl_buffer_destroy(wl_buffer); +} + +static const struct wl_buffer_listener g_buffer_listener = { + .release = OnBufferRelease}; + +// === Wayland XDG surface === + +static void OnSurfaceConfigure(void* data, struct xdg_surface* xdg_surface, + uint32_t serial) { + (void)data; + xdg_surface_ack_configure(xdg_surface, serial); +} + +static const struct xdg_surface_listener g_surface_listener = { + .configure = OnSurfaceConfigure}; + +// === Helper functions === + +static const void* CreatePui(const char* filename) { + int fd = open(filename, O_RDONLY); + if (fd == -1) err(1, "Failed to open pui file"); + struct stat buf; + if (fstat(fd, &buf)) err(1, "Failed to stat pui file"); + const void* result = + mmap(NULL, (size_t)buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (result == MAP_FAILED) err(1, "Failed to mmap pui file"); + return result; +} + +static int CreateShm(int size) { + static const char kShmName[] = "/wl_shm-pui"; + int fd = shm_open(kShmName, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd == -1) err(1, "Failed to create shm"); + shm_unlink(kShmName); + if (ftruncate(fd, size)) err(1, "Failed to truncate shm"); + return fd; +} + +static void RenderPui(const void* pui, int fd) { + int width = PuiGetWidth(pui); + int height = PuiGetHeight(pui); + size_t size = (size_t)width * (size_t)height * 4; + void* buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (buffer == MAP_FAILED) err(1, "Failed to map shm"); + PuiRender(pui, buffer, width, 0); + for (int i = 0; i < width * height; i++) { + uint32_t* ptr = (uint32_t*)buffer + i; + *ptr = (*ptr << 16 & 0xff0000) | (*ptr >> 16 & 0xff) | (*ptr & 0xff00); + } + munmap(buffer, size); +} + +static struct wl_buffer* PrepareBuffer(struct Context* ctx) { + int width = PuiGetWidth(ctx->pui); + int height = PuiGetHeight(ctx->pui); + int stride = width * 4; + int size = stride * height; + int fd = CreateShm(size); + struct wl_shm_pool* pool = wl_shm_create_pool(ctx->shm, fd, size); + struct wl_buffer* buffer = wl_shm_pool_create_buffer( + pool, 0, width, height, stride, WL_SHM_FORMAT_XRGB8888); + wl_shm_pool_destroy(pool); + RenderPui(ctx->pui, fd); + close(fd); + return buffer; +} + +int main(int argc, char** argv) { + if (argc < 2) err(1, "Usage: %s [file.pui]", argv[0]); + struct Context ctx = {.pui = CreatePui(argv[1]), + .display = wl_display_connect(NULL)}; + if (!ctx.pui) err(1, "Failed to create pui"); + if (!ctx.display) err(1, "Failed to connect to wayland display"); + ctx.registry = wl_display_get_registry(ctx.display); + if (!ctx.registry) err(1, "Failed to get wayland registry"); + if (wl_registry_add_listener(ctx.registry, &g_registry_listener, &ctx)) + err(1, "Failed to add wayland registry listener"); + if (wl_display_roundtrip(ctx.display) == -1) + err(1, "Failed to roundtrip wayland display"); + + if (xdg_wm_base_add_listener(ctx.wm_base, &g_wm_base_listener, &ctx)) + err(1, "Failed to add wayland wm base listener"); + ctx.surface = wl_compositor_create_surface(ctx.compositor); + if (!ctx.surface) err(1, "Failed to create wayland surface"); + ctx.xdg_surface = xdg_wm_base_get_xdg_surface(ctx.wm_base, ctx.surface); + if (!ctx.xdg_surface) err(1, "Failed to get wayland xdg surface"); + if (xdg_surface_add_listener(ctx.xdg_surface, &g_surface_listener, &ctx)) + err(1, "Failed to add wayland xdg surface listener"); + ctx.xdg_toplevel = xdg_surface_get_toplevel(ctx.xdg_surface); + if (!ctx.xdg_toplevel) err(1, "Failed to get wayland xdg toplevel"); + xdg_toplevel_set_title(ctx.xdg_toplevel, "Pui"); + wl_surface_commit(ctx.surface); + if (wl_display_roundtrip(ctx.display) == -1) + err(1, "Failed to roundtrip wayland display"); + + struct wl_buffer* buffer = PrepareBuffer(&ctx); + wl_buffer_add_listener(buffer, &g_buffer_listener, &ctx); + wl_surface_attach(ctx.surface, buffer, 0, 0); + wl_surface_commit(ctx.surface); + + while (wl_display_dispatch(ctx.display) != -1) + ; + err(1, "Failed to dispatch wayland display"); +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..b954d99 --- /dev/null +++ b/makefile @@ -0,0 +1,32 @@ +target:=pui +objects:=main.o pui.o xdg-shell.o +protocols:=/usr/share/wayland-protocols +wl_headers:=xdg-shell.h + +CFLAGS:=-O3 -Wall -Wextra -Werror -pedantic +LDFLAGS:=-O3 -s -lwayland-client -lrt + +all: $(target) sample.pui + +sample.pui: convert sample.bmp sample.ini + $^ > $@ + +convert: convert.c + $(CC) $(CFLAGS) -s $< -o $@ + +$(target): $(objects) + $(CC) $(LDFLAGS) $^ -o $@ + +%.o: %.c *.h $(wl_headers) + $(CC) $(CFLAGS) -c $< -o $@ + +%.c: $(protocols)/*/*/%.xml + wayland-scanner private-code $< $@ + +%.h: $(protocols)/*/*/%.xml + wayland-scanner client-header $< $@ + +clean: + rm $(target) $(objects) $(wl_headers) convert sample.pui + +.PRECIOUS: $(wl_headers) @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 Mikhail Burakov. This file is part of Pui. + * + * Pui 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 3 of the License, or + * (at your option) any later version. + * + * Pui 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. + * + * You should have received a copy of the GNU General Public License + * along with Pui. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "pui.h" + +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +struct PuiHeader { + uint8_t w, h, scale, count; +}; + +struct PuiElement { + uint8_t x, y, w, h; +}; + +static_assert(sizeof(struct PuiHeader) == 4, "Invalid PuiHeader size"); +static_assert(sizeof(struct PuiElement) == 4, "Invalid PuiElement size"); + +int PuiGetWidth(const void* pui_data) { + const struct PuiHeader* pui_header = pui_data; + int sx = pui_header->scale >> 4; + return pui_header->w << sx; +} + +int PuiGetHeight(const void* pui_data) { + const struct PuiHeader* pui_header = pui_data; + int sy = pui_header->scale & 0xf; + return pui_header->h << sy; +} + +int PuiHitTest(const void* pui_data, int x, int y) { + int result = 0; + const struct PuiHeader* pui_header = pui_data; + x >>= pui_header->scale >> 4; + y >>= pui_header->scale & 0xf; + int count = pui_header->count >> 4; + for (int i = 0; i < count; ++i) { + const struct PuiElement* element = + (const struct PuiElement*)pui_data + 1 + i; + int hit = element->x <= x && x < element->x + element->w && + element->y <= y && y < element->y + element->h; + result |= hit << i; + } + return result; +} + +void PuiRender(const void* pui_data, void* buffer, int stride, int active) { + const struct PuiHeader* pui_header = pui_data; + int width = pui_header->w << (pui_header->scale >> 4); + int height = pui_header->h << (pui_header->scale & 0xf); + int elements = pui_header->count >> 4; + int colors = pui_header->count & 0xf; + const uint32_t* palettes[] = { + (const uint32_t*)pui_data + 1 + elements, + (const uint32_t*)pui_data + 1 + elements + colors, + }; + const uint8_t* bitmap_data = + (const uint8_t*)pui_data + 4 * (1 + elements + 2 * colors); + for (int idx = 0, counter = 0, offset = 0; offset < width * height; idx++) { + int value = bitmap_data[idx >> 1]; + if (~idx & 1) value = value >> 4; + if (value & 0x8) { + counter = counter << 3 | (value & 0x7); + continue; + } + for (counter = counter ? counter + 2 : 1; counter-- > 0; offset++) { + int x = offset % width; + int y = offset / width; + int palette = !!(PuiHitTest(pui_data, x, y) & active); + ((uint32_t*)buffer)[x + y * stride] = palettes[palette][value & 0x7]; + } + counter = 0; + } +} @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2020 Mikhail Burakov. This file is part of Pui. + * + * Pui 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 3 of the License, or + * (at your option) any later version. + * + * Pui 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. + * + * You should have received a copy of the GNU General Public License + * along with Pui. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef PUI_PUI_H_ +#define PUI_PUI_H_ + +int PuiGetWidth(const void* pui_data); +int PuiGetHeight(const void* pui_data); +int PuiHitTest(const void* pui_data, int x, int y); +void PuiRender(const void* pui_data, void* buffer, int stride, int active); + +#endif // PUI_PUI_H_ diff --git a/sample.bmp b/sample.bmp Binary files differnew file mode 100644 index 0000000..3188f68 --- /dev/null +++ b/sample.bmp diff --git a/sample.ini b/sample.ini new file mode 100644 index 0000000..d89a17d --- /dev/null +++ b/sample.ini @@ -0,0 +1,28 @@ +[Elements] +Element00=16,16,96,16 +Element01=16,40,96,16 +Element02=16,72,96,24 +Element03=16,104,96,24 +Element04=16,136,96,24 +Element05=16,168,96,24 +Element06=16,200,96,24 + +[DefaultPalette] +Color00=ffffd700 +Color01=62626200 +Color02=ffffd700 +Color03=80000000 +Color04=40200400 +Color05=ffffff00 +Color06=80808000 +Color07=00000000 + +[ActivePalette] +Color00=ffffd700 +Color01=62626200 +Color02=58585800 +Color03=80000000 +Color04=40200400 +Color05=ffffff00 +Color06=80808000 +Color07=00000000 |