initial commit (just stuffing all my test project files into a git repo)

This commit is contained in:
Gered 2015-01-04 18:43:57 -05:00
commit 05f68bc409
30 changed files with 9060 additions and 0 deletions

9
.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
.DS_Store
/Makefile
CMakeFiles
CMakeCache.txt
cmake_install.cmake
.idea
/*.log
/SoftwareRasterizer
/*.exe

21
CMakeLists.txt Normal file
View file

@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 2.8.4)
set(PROJECT_NAME SoftwareRasterizer)
set(PROJECT_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR})
project(SoftwareRasterizer)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SRC_DIR}/cmake")
find_package(SDL2 REQUIRED)
file(GLOB_RECURSE SOURCE_FILES "."
"${PROJECT_SRC_DIR}/src/*.cpp"
"${PROJECT_SRC_DIR}/src/*.c"
"${PROJECT_SRC_DIR}/src/*.h"
"${PROJECT_SRC_DIR}/include/*.h"
)
include_directories(${SDL2_INCLUDE_DIR} "${PROJECT_SRC_DIR}/include")
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
target_link_libraries(${PROJECT_NAME} ${SDL2_LIBRARY})

22
README.md Normal file
View file

@ -0,0 +1,22 @@
# Software Rasterizer
A 2D and 3D graphics software rasterizer implemented in C. Includes SDL backend to abstract OS-specifics (window management, input, etc).
This is basically a "for fun" toy project of mine. Probably not too noteworthy and definitely has been done better before by people far smarter then me!
**In early development! Pre-alpha quality code!**
2D features:
* 24 bit (RGB) and 32 bit (RGBA) surface support
* drawing primitives: pixel, line, rectangle (outline and filled)
* sprite drawing: solid, transparent, blending, color tinting
* clipping support on all drawing operations as well as "fast" variants which skip clipping
* image -> surface loading via stb_image
* TTF -> sprite font texture atlas via stb_truetype (TODO)
3D features:
* TODO

249
cmake/FindSDL2.cmake Normal file
View file

@ -0,0 +1,249 @@
# Locate SDL2 library
# This module defines
# SDL2_LIBRARY, the name of the library to link against
# SDL2_FOUND, if false, do not try to link to SDL2
# SDL2_INCLUDE_DIR, where to find SDL.h
#
# This module responds to the the flag:
# SDL2_BUILDING_LIBRARY
# If this is defined, then no SDL2_main will be linked in because
# only applications need main().
# Otherwise, it is assumed you are building an application and this
# module will attempt to locate and set the the proper link flags
# as part of the returned SDL2_LIBRARY variable.
#
# Don't forget to include SDL2main.h and SDL2main.m your project for the
# OS X framework based version. (Other versions link to -lSDL2main which
# this module will try to find on your behalf.) Also for OS X, this
# module will automatically add the -framework Cocoa on your behalf.
#
#
# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration
# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library
# (SDL2.dll, libsdl2.so, SDL2.framework, etc).
# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again.
# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value
# as appropriate. These values are used to generate the final SDL2_LIBRARY
# variable, but when these values are unset, SDL2_LIBRARY does not get created.
#
#
# $SDL2 is an environment variable that would
# correspond to the ./configure --prefix=$SDL2
# used in building SDL2.
# l.e.galup 9-20-02
#
# Modified by Eric Wing.
# Added code to assist with automated building by using environmental variables
# and providing a more controlled/consistent search behavior.
# Added new modifications to recognize OS X frameworks and
# additional Unix paths (FreeBSD, etc).
# Also corrected the header search path to follow "proper" SDL2 guidelines.
# Added a search for SDL2main which is needed by some platforms.
# Added a search for threads which is needed by some platforms.
# Added needed compile switches for MinGW.
#
# On OSX, this will prefer the Framework version (if found) over others.
# People will have to manually change the cache values of
# SDL2_LIBRARY to override this selection or set the CMake environment
# CMAKE_INCLUDE_PATH to modify the search paths.
#
# Note that the header path has changed from SDL2/SDL.h to just SDL.h
# This needed to change because "proper" SDL2 convention
# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability
# reasons because not all systems place things in SDL2/ (see FreeBSD).
#
# Ported by Johnny Patterson. This is a literal port for SDL2 of the FindSDL.cmake
# module with the minor edit of changing "SDL" to "SDL2" where necessary. This
# was not created for redistribution, and exists temporarily pending official
# SDL2 CMake modules.
#
# Note that on windows this will only search for the 32bit libraries, to search
# for 64bit change x86/i686-w64 to x64/x86_64-w64
#=============================================================================
# Copyright 2003-2009 Kitware, Inc.
#
# CMake - Cross Platform Makefile Generator
# Copyright 2000-2014 Kitware, Inc.
# Copyright 2000-2011 Insight Software Consortium
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * Neither the names of Kitware, Inc., the Insight Software Consortium,
# nor the names of their contributors may be used to endorse or promote
# products derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
FIND_PATH(SDL2_INCLUDE_DIR SDL.h
HINTS
$ENV{SDL2}
PATH_SUFFIXES include/SDL2 include SDL2
i686-w64-mingw32/include/SDL2
x86_64-w64-mingw32/include/SDL2
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local/include/SDL2
/usr/include/SDL2
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
# Lookup the 64 bit libs on x64
IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2
HINTS
$ENV{SDL2}
PATH_SUFFIXES lib64 lib
lib/x64
x86_64-w64-mingw32/lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
# On 32bit build find the 32bit libs
ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8)
FIND_LIBRARY(SDL2_LIBRARY_TEMP SDL2
HINTS
$ENV{SDL2}
PATH_SUFFIXES lib
lib/x86
i686-w64-mingw32/lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8)
IF(NOT SDL2_BUILDING_LIBRARY)
IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
# Non-OS X framework versions expect you to also dynamically link to
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
# seem to provide SDL2main for compatibility even though they don't
# necessarily need it.
# Lookup the 64 bit libs on x64
IF(CMAKE_SIZEOF_VOID_P EQUAL 8)
FIND_LIBRARY(SDL2MAIN_LIBRARY
NAMES SDL2main
HINTS
$ENV{SDL2}
PATH_SUFFIXES lib64 lib
lib/x64
x86_64-w64-mingw32/lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
# On 32bit build find the 32bit libs
ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8)
FIND_LIBRARY(SDL2MAIN_LIBRARY
NAMES SDL2main
HINTS
$ENV{SDL2}
PATH_SUFFIXES lib
lib/x86
i686-w64-mingw32/lib
PATHS
/sw
/opt/local
/opt/csw
/opt
)
ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8)
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# SDL2 may require threads on your system.
# The Apple build may not need an explicit flag because one of the
# frameworks may already provide it.
# But for non-OSX systems, I will use the CMake Threads package.
IF(NOT APPLE)
FIND_PACKAGE(Threads)
ENDIF(NOT APPLE)
# MinGW needs an additional library, mwindows
# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows
# (Actually on second look, I think it only needs one of the m* libraries.)
IF(MINGW)
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
ENDIF(MINGW)
SET(SDL2_FOUND "NO")
IF(SDL2_LIBRARY_TEMP)
# For SDL2main
IF(NOT SDL2_BUILDING_LIBRARY)
IF(SDL2MAIN_LIBRARY)
SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(SDL2MAIN_LIBRARY)
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
# CMake doesn't display the -framework Cocoa string in the UI even
# though it actually is there if I modify a pre-used variable.
# I think it has something to do with the CACHE STRING.
# So I use a temporary variable until the end so I can set the
# "real" variable in one-shot.
IF(APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa")
ENDIF(APPLE)
# For threads, as mentioned Apple doesn't need this.
# In fact, there seems to be a problem if I used the Threads package
# and try using this line, so I'm just skipping it entirely for OS X.
IF(NOT APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
ENDIF(NOT APPLE)
# For MinGW library
IF(MINGW)
SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(MINGW)
# Set the final string here so the GUI reflects the final state.
SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found")
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "")
SET(SDL2_FOUND "YES")
ENDIF(SDL2_LIBRARY_TEMP)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR)

15
include/assets/image.h Normal file
View file

@ -0,0 +1,15 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "../fbgfx/surface.h"
#include "../types.h"
SURFACE* image_load_file(const char *filename);
SURFACE* image_load_memory(const void *image_file_bin, uint32_t size);
#ifdef __cplusplus
}
#endif

17
include/fbgfx/clipping.h Normal file
View file

@ -0,0 +1,17 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "../types.h"
#include "../math/rect.h"
bool point_in_bounds(const RECT *clip_region, int x, int y);
bool line_in_bounds(const RECT *clip_region, int x1, int y1, int x2, int y2);
bool clamp_to_region(const RECT *clip_region, int *x1, int *y1, int *x2, int *y2);
bool clip_blit_region(const RECT *dest_clip_region, RECT *source_blit_region, int *dest_x, int *dest_y);
#ifdef __cplusplus
}
#endif

179
include/fbgfx/color.h Normal file
View file

@ -0,0 +1,179 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "../types.h"
typedef uint32_t COLOR;
typedef uint8_t COLOR_COMPONENT;
static const COLOR_COMPONENT TRANSPARENT = 0;
static const COLOR_COMPONENT OPAQUE = 255;
static inline COLOR_COMPONENT color_get_a(COLOR color) {
return (color & 0xff000000) >> 24;
}
static inline COLOR_COMPONENT color_get_r(COLOR color) {
return (color & 0x00ff0000) >> 16;
}
static inline COLOR_COMPONENT color_get_g(COLOR color) {
return (color & 0x0000ff00) >> 8;
}
static inline COLOR_COMPONENT color_get_b(COLOR color) {
return (color & 0x000000ff);
}
static inline void color_extract(COLOR color, COLOR_COMPONENT *r, COLOR_COMPONENT *g, COLOR_COMPONENT *b, COLOR_COMPONENT *a) {
if (a)
*a = color_get_a(color);
if (r)
*r = color_get_r(color);
if (g)
*g = color_get_g(color);
if (b)
*b = color_get_b(color);
}
static inline COLOR color_create_rgba(COLOR_COMPONENT r, COLOR_COMPONENT g, COLOR_COMPONENT b, COLOR_COMPONENT a) {
return (a << 24) | (r << 16) | (g << 8) | b;
}
static inline COLOR color_create_rgb(COLOR_COMPONENT r, COLOR_COMPONENT g, COLOR_COMPONENT b) {
return color_create_rgba(r, g, b, OPAQUE);
}
/* floating-point color components */
typedef float COLORF_COMPONENT;
static const COLORF_COMPONENT TRANSPARENT_F = 0.0f;
static const COLORF_COMPONENT OPAQUE_F = 1.0f;
static inline COLORF_COMPONENT colorf_get_a(COLOR color) {
return (float)((color & 0xff000000) >> 24) / 255;
}
static inline COLORF_COMPONENT colorf_get_r(COLOR color) {
return (float)((color & 0x00ff0000) >> 16) / 255;
}
static inline COLORF_COMPONENT colorf_get_g(COLOR color) {
return (float)((color & 0x0000ff00) >> 8) / 255;
}
static inline COLORF_COMPONENT colorf_get_b(COLOR color) {
return (float)(color & 0x000000ff) / 255;
}
static inline void colorf_extract(COLOR color, COLORF_COMPONENT *r, COLORF_COMPONENT *g, COLORF_COMPONENT *b, COLORF_COMPONENT *a) {
if (a)
*a = colorf_get_a(color);
if (r)
*r = colorf_get_r(color);
if (g)
*g = colorf_get_g(color);
if (b)
*b = colorf_get_b(color);
}
static inline COLOR colorf_create_rgba(COLORF_COMPONENT r, COLORF_COMPONENT g, COLORF_COMPONENT b, COLORF_COMPONENT a) {
return ((COLOR_COMPONENT)(a * 255) << 24) |
((COLOR_COMPONENT)(r * 255) << 16) |
((COLOR_COMPONENT)(g * 255) << 8) |
(COLOR_COMPONENT)(b * 255);
}
static inline COLOR colorf_create_rgb(COLORF_COMPONENT r, COLORF_COMPONENT g, COLORF_COMPONENT b, COLORF_COMPONENT a) {
return colorf_create_rgba(r, g, b, OPAQUE_F);
}
static inline COLOR color_blend(COLOR source, COLOR destination) {
int sR = color_get_r(source);
int sG = color_get_g(source);
int sB = color_get_b(source);
int sA = color_get_a(source);
int dR = color_get_r(destination);
int dG = color_get_g(destination);
int dB = color_get_b(destination);
int dA = color_get_a(destination);
// this is equivalent to "glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)"
return color_create_rgba(
(COLOR_COMPONENT)(((sR * sA) + (dR * (255 - sA))) / 255),
(COLOR_COMPONENT)(((sG * sA) + (dG * (255 - sA))) / 255),
(COLOR_COMPONENT)(((sB * sA) + (dB * (255 - sA))) / 255),
(COLOR_COMPONENT)(((sA * sA) + (dA * (255 - sA))) / 255)
);
}
static inline COLOR color_tint(COLOR color, COLOR tint) {
int cR = color_get_r(color);
int cG = color_get_g(color);
int cB = color_get_b(color);
int cA = color_get_a(color);
int tR = color_get_r(tint);
int tG = color_get_g(tint);
int tB = color_get_b(tint);
int tA = color_get_a(tint);
return color_create_rgba(
(COLOR_COMPONENT)((cR * tR) / 255),
(COLOR_COMPONENT)((cG * tG) / 255),
(COLOR_COMPONENT)((cB * tB) / 255),
(COLOR_COMPONENT)((cA * tA) / 255)
);
}
static inline COLOR color_tint_alpha(COLOR_COMPONENT alpha, COLOR tint) {
return color_create_rgba(
color_get_r(tint),
color_get_g(tint),
color_get_b(tint),
(COLOR_COMPONENT)(((uint32_t)alpha * color_get_a(tint)) / 255)
);
}
static inline void color_convert_rgba_to_rgb(COLOR rgba, COLOR_COMPONENT *r, COLOR_COMPONENT *g, COLOR_COMPONENT *b) {
COLOR_COMPONENT a = color_get_a(rgba);
*r = (COLOR_COMPONENT)(((uint32_t)color_get_r(rgba) * a) / 255);
*g = (COLOR_COMPONENT)(((uint32_t)color_get_g(rgba) * a) / 255);
*b = (COLOR_COMPONENT)(((uint32_t)color_get_b(rgba) * a) / 255);
}
/* -- */
#define COLOR_WHITE (color_create_rgb(255, 255, 255))
#define COLOR_RED (color_create_rgb(255, 0, 0))
#define COLOR_GREEN (color_create_rgb(0, 255, 0))
#define COLOR_BLUE (color_create_rgb(0, 0, 255))
#define COLOR_YELLOW (color_create_rgb(255, 255, 0))
#define COLOR_CYAN (color_create_rgb(0, 255, 255))
#define COLOR_MAGENTA (color_create_rgb(255, 0, 255))
#define COLOR_BLACK (color_create_rgb(0, 0, 0))
#define COLOR_DOS_BLACK (color_create_rgb(0, 0, 0))
#define COLOR_DOS_BLUE (color_create_rgb(0, 0, 170))
#define COLOR_DOS_GREEN (color_create_rgb(0, 170, 0))
#define COLOR_DOS_CYAN (color_create_rgb(0, 170, 170))
#define COLOR_DOS_RED (color_create_rgb(170, 0, 0))
#define COLOR_DOS_MAGENTA (color_create_rgb(170, 0, 170))
#define COLOR_DOS_BROWN (color_create_rgb(170, 85, 0))
#define COLOR_DOS_WHITE (color_create_rgb(170, 170, 170))
#define COLOR_DOS_GRAY (color_create_rgb(85, 85, 85))
#define COLOR_DOS_LIGHT_BLUE (color_create_rgb(85, 85, 255))
#define COLOR_DOS_LIGHT_GREEN (color_create_rgb(85, 255, 64))
#define COLOR_DOS_LIGHT_CYAN (color_create_rgb(85, 255, 255))
#define COLOR_DOS_LIGHT_RED (color_create_rgb(255, 85, 85))
#define COLOR_DOS_LIGHT_MAGENTA (color_create_rgb(255, 85, 255))
#define COLOR_DOS_YELLOW (color_create_rgb(255, 255, 85))
#define COLOR_DOS_BRIGHT_WHITE (color_create_rgb(255, 255, 255))
#ifdef __cplusplus
}
#endif

188
include/fbgfx/surface.h Normal file
View file

@ -0,0 +1,188 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "../types.h"
#include "../math/rect.h"
#include "color.h"
typedef enum {
SURFACE_FORMAT_ALPHA = 8,
SURFACE_FORMAT_RGB = 24,
SURFACE_FORMAT_RGBA = 32
} SURFACE_FORMAT;
typedef enum {
SURFACE_FLAGS_NONE = 0,
SURFACE_FLAGS_SIDEWAYS_BUFFER = 1
} SURFACE_FLAGS;
typedef struct SURFACE {
SURFACE_FLAGS flags;
COLOR_COMPONENT *pixels;
bool wrapped_buffer;
SURFACE_FORMAT format;
int bytes_per_pixel;
int width;
int height;
int x_inc;
int y_inc;
RECT clip_region;
} SURFACE;
extern SURFACE *top_screen_surface;
extern SURFACE *bottom_screen_surface;
void surface_init();
void surface_exit();
void surface_swap_framebuffers();
SURFACE* surface_create(int width, int height, SURFACE_FORMAT format, SURFACE_FLAGS flags);
SURFACE* surface_wrap_existing(void *framebuffer, int width, int height, SURFACE_FORMAT format, SURFACE_FLAGS flags);
void surface_resize(SURFACE *surface, int width, int height);
void surface_destroy(SURFACE *surface);
void surface_convert_rgb_to_bgr(SURFACE *surface);
void surface_clear(SURFACE *surface, COLOR color);
void surface_set_pixel(SURFACE *surface, int x, int y, COLOR color);
void surface_set_pixel_fast(SURFACE *surface, int x, int y, COLOR color);
COLOR surface_get_pixel(const SURFACE *surface, int x, int y);
COLOR surface_get_pixel_fast(SURFACE *surface, int x, int y);
void surface_line(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color);
void surface_line_fast(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color);
void surface_hline(SURFACE *surface, int x1, int x2, int y, COLOR color);
void surface_hline_fast(SURFACE *surface, int x1, int x2, int y, COLOR color);
void surface_vline(SURFACE *surface, int x, int y1, int y2, COLOR color);
void surface_vline_fast(SURFACE *surface, int x, int y1, int y2, COLOR color);
void surface_rect(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color);
void surface_rect_fast(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color);
void surface_rect_filled(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color);
void surface_rect_filled_fast(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color);
void surface_blit_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y);
void surface_blit_blend_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y);
void surface_blit_trans_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y);
void surface_blit_tint_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, COLOR tint);
void surface_blit_blend_tint_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, COLOR tint);
#define GET_PIXEL_FAST(fmt, ...) surface_get_pixel_ ## fmt ## _fast(__VA_ARGS__)
#define SET_PIXEL_FAST(fmt, ...) surface_set_pixel_ ## fmt ## _fast(__VA_ARGS__)
#define GET_PIXEL_IDX_FAST(fmt, ...) surface_get_pixel_idx_ ## fmt ## _fast(__VA_ARGS__)
#define SET_PIXEL_IDX_FAST(fmt, ...) surface_set_pixel_idx_ ## fmt ## _fast(__VA_ARGS__)
////////////////
static inline uint32_t surface_get_buffer_size(const SURFACE *surface) {
return (uint32_t)(surface->width * surface->height) * surface->bytes_per_pixel;
}
static inline uint32_t surface_get_index_of(const SURFACE *surface, int x, int y) {
if (surface->flags & SURFACE_FLAGS_SIDEWAYS_BUFFER)
return (uint32_t)((x * surface->height) + (surface->height - y - 1)) * surface->bytes_per_pixel;
else
return (uint32_t)(x + (y * surface->width)) * surface->bytes_per_pixel;
}
static inline COLOR_COMPONENT* surface_get_pointer_to(const SURFACE *surface, int x, int y) {
return surface->pixels + surface_get_index_of(surface, x, y);
}
////////////////
static inline void surface_set_pixel_idx_alpha_fast(SURFACE *surface, uint32_t index, COLOR color) {
COLOR_COMPONENT alpha = color_get_a(color);
COLOR_COMPONENT *p = (surface->pixels + index);
*p = alpha;
}
static inline void surface_set_pixel_idx_rgb_fast(SURFACE *surface, uint32_t index, COLOR color) {
COLOR_COMPONENT *p = (surface->pixels + index);
color_convert_rgba_to_rgb(color, (p + 2), (p + 1), p);
}
static inline void surface_set_pixel_idx_rgba_fast(SURFACE *surface, uint32_t index, COLOR color) {
COLOR_COMPONENT *p = (surface->pixels + index);
color_extract(color, (p + 2), (p + 1), p, (p + 3));
}
static inline COLOR surface_get_pixel_idx_alpha_fast(const SURFACE *surface, uint32_t index) {
COLOR_COMPONENT *p = (surface->pixels + index);
return color_create_rgba(0, 0, 0, *p);
}
static inline COLOR surface_get_pixel_idx_rgb_fast(const SURFACE *surface, uint32_t index) {
COLOR_COMPONENT *p = (surface->pixels + index);
COLOR_COMPONENT r = p[2];
COLOR_COMPONENT g = p[1];
COLOR_COMPONENT b = p[0];
return color_create_rgb(r, g, b);
}
static inline COLOR surface_get_pixel_idx_rgba_fast(const SURFACE *surface, uint32_t index) {
COLOR_COMPONENT *p = (surface->pixels + index);
COLOR_COMPONENT a = p[3];
COLOR_COMPONENT r = p[2];
COLOR_COMPONENT g = p[1];
COLOR_COMPONENT b = p[0];
return color_create_rgba(r, g, b, a);
}
static inline void surface_set_pixel_alpha_fast(SURFACE *surface, int x, int y, COLOR color) {
uint32_t index = surface_get_index_of(surface, x, y);
surface_set_pixel_idx_alpha_fast(surface, index, color);
}
static inline void surface_set_pixel_rgb_fast(SURFACE *surface, int x, int y, COLOR color) {
uint32_t index = surface_get_index_of(surface, x, y);
surface_set_pixel_idx_rgb_fast(surface, index, color);
}
static inline void surface_set_pixel_rgba_fast(SURFACE *surface, int x, int y, COLOR color) {
uint32_t index = surface_get_index_of(surface, x, y);
surface_set_pixel_idx_rgba_fast(surface, index, color);
}
static inline COLOR surface_get_pixel_alpha_fast(const SURFACE *surface, int x, int y) {
uint32_t index = surface_get_index_of(surface, x, y);
return surface_get_pixel_idx_alpha_fast(surface, index);
}
static inline COLOR surface_get_pixel_rgb_fast(const SURFACE *surface, int x, int y) {
uint32_t index = surface_get_index_of(surface, x, y);
return surface_get_pixel_idx_rgb_fast(surface, index);
}
static inline COLOR surface_get_pixel_rgba_fast(const SURFACE *surface, int x, int y) {
uint32_t index = surface_get_index_of(surface, x, y);
return surface_get_pixel_idx_rgba_fast(surface, index);
}
static inline void surface_blit(SURFACE *source, SURFACE *destination, int x, int y) {
surface_blit_region(source, destination, 0, 0, source->width, source->height, x, y);
}
static inline void surface_blit_blend(SURFACE *source, SURFACE *destination, int x, int y) {
surface_blit_blend_region(source, destination, 0, 0, source->width, source->height, x, y);
}
static inline void surface_blit_trans(SURFACE *source, SURFACE *destination, int x, int y) {
surface_blit_trans_region(source, destination, 0, 0, source->width, source->height, x, y);
}
static inline void surface_blit_tint(SURFACE *source, SURFACE *destination, int x, int y, COLOR tint) {
surface_blit_tint_region(source, destination, 0, 0, source->width, source->height, x, y, tint);
}
static inline void surface_blit_blend_tint(SURFACE *source, SURFACE *destination, int x, int y, COLOR tint) {
surface_blit_blend_tint_region(source, destination, 0, 0, source->width, source->height, x, y, tint);
}
#ifdef __cplusplus
}
#endif

39
include/math/rect.h Normal file
View file

@ -0,0 +1,39 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
int x;
int y;
int width;
int height;
} RECT;
static inline RECT rect_create(int x, int y, int width, int height) {
RECT rect;
rect.x = x;
rect.y = y;
rect.width = width;
rect.height = height;
return rect;
}
static inline int rect_get_right(const RECT *rect) {
if (rect->width > 0)
return rect->x + rect->width - 1;
else
return rect->x;
}
static inline int rect_get_bottom(const RECT *rect) {
if (rect->height > 0)
return rect->y + rect->height - 1;
else
return rect->y;
}
#ifdef __cplusplus
}
#endif

13
include/types.h Normal file
View file

@ -0,0 +1,13 @@
#pragma once
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#define SWAP(T, a, b) \
do { \
T tmp = a; \
a = b; \
b = tmp; \
} while (0)

63
src/assets/image.c Normal file
View file

@ -0,0 +1,63 @@
#include "assets/image.h"
#include "../vendor/stb_image.h"
#include "fbgfx/surface.h"
#include "types.h"
#include <stdio.h>
SURFACE* create_surface_from_pixels(uint8_t *pixels, int width, int height, int bpp) {
SURFACE *surface = NULL;
SURFACE_FORMAT format;
switch (bpp) {
case 1: format = SURFACE_FORMAT_ALPHA; break;
case 3: format = SURFACE_FORMAT_RGB; break;
case 4: format = SURFACE_FORMAT_RGBA; break;
default:
stbi_image_free(pixels);
return NULL;
}
surface = surface_wrap_existing(pixels, width, height, format, 0);
if (!surface) {
stbi_image_free(pixels);
return NULL;
}
// HACK: this one little time, we're going go manually toggle this setting in a way we shouldn't be!
// (all that stbi_image_free() does is call free(), so it's fine if we leave cleanup of the
// stb_image pixel buffer to surface_destroy() ...)
surface->wrapped_buffer = false;
surface_convert_rgb_to_bgr(surface);
return surface;
}
SURFACE* image_load_file(const char *filename) {
if (!filename)
return NULL;
int width;
int height;
int bpp;
uint8_t *pixels = stbi_load(filename, &width, &height, &bpp, 0);
if (!pixels)
return NULL;
else
return create_surface_from_pixels(pixels, width, height, bpp);
}
SURFACE* image_load_memory(const void *image_file_bin, uint32_t size) {
if (!image_file_bin || !size)
return NULL;
int width;
int height;
int bpp;
uint8_t *pixels = stbi_load_from_memory(image_file_bin, size, &width, &height, &bpp, 0);
if (!pixels)
return NULL;
else
return create_surface_from_pixels(pixels, width, height, bpp);
}

115
src/fbgfx/clipping.c Normal file
View file

@ -0,0 +1,115 @@
#include "fbgfx/clipping.h"
bool point_in_bounds(const RECT *clip_region, int x, int y) {
return (
x >= clip_region->x &&
y >= clip_region->y &&
x <= rect_get_right(clip_region) &&
y <= rect_get_bottom(clip_region)
);
}
bool line_in_bounds(const RECT *clip_region, int x1, int y1, int x2, int y2) {
int clip_region_right = rect_get_right(clip_region);
int clip_region_bottom = rect_get_bottom(clip_region);
if (y1 < clip_region->y &&
y2 < clip_region->y)
return false;
if (y1 > clip_region_bottom &&
y2 > clip_region_bottom)
return false;
if (x1 < clip_region->x &&
x2 < clip_region->x)
return false;
if (x1 > clip_region_right &&
x2 > clip_region_right)
return false;
return true;
}
bool clamp_to_region(const RECT *clip_region, int *x1, int *y1, int *x2, int *y2) {
int clip_region_right = rect_get_right(clip_region);
int clip_region_bottom = rect_get_bottom(clip_region);
if (*y1 < clip_region->y &&
*y2 < clip_region->y)
return false;
if (*y1 > clip_region_bottom &&
*y2 > clip_region_bottom)
return false;
if (*x1 < clip_region->x &&
*x2 < clip_region->x)
return false;
if (*x1 > clip_region_right &&
*x2 > clip_region_right)
return false;
if (*y1 < clip_region->y)
*y1 = clip_region->y;
if (*y1 > clip_region_bottom)
*y1 = clip_region_bottom;
if (*y2 < clip_region->y)
*y2 = clip_region->y;
if (*y2 > clip_region_bottom)
*y2 = clip_region_bottom;
if (*x1 < clip_region->x)
*x1 = clip_region->x;
if (*x1 > clip_region_right)
*x1 = clip_region_right;
if (*x2 < clip_region->x)
*x2 = clip_region->x;
if (*x2 > clip_region_right)
*x2 = clip_region_right;
return true;
}
bool clip_blit_region(const RECT *dest_clip_region, RECT *source_blit_region, int *dest_x, int *dest_y) {
// check for "completely out of bounds" scenarios first to return early
if (*dest_x > rect_get_right(dest_clip_region))
return false;
if (*dest_x + source_blit_region->width < dest_clip_region->x)
return false;
if (*dest_y > rect_get_bottom(dest_clip_region))
return false;
if (*dest_y + source_blit_region->height < dest_clip_region->y)
return false;
// destination region is fully / partially within the clip region
// off the left side?
if (*dest_x < dest_clip_region->x) {
int offset = dest_clip_region->x - *dest_x;
source_blit_region->x += offset;
source_blit_region->width -= offset;
*dest_x = dest_clip_region->x;
}
// off the right side?
if (*dest_x > (dest_clip_region->width - source_blit_region->width)) {
int offset = *dest_x + source_blit_region->width - dest_clip_region->width;
source_blit_region->width -= offset;
}
// off the top edge?
if (*dest_y < dest_clip_region->y) {
int offset = dest_clip_region->y - *dest_y;
source_blit_region->y += offset;
source_blit_region->height -= offset;
*dest_y = dest_clip_region->y;
}
// off the bottom edge?
if (*dest_y > (dest_clip_region->height - source_blit_region->height)) {
int offset = *dest_y + source_blit_region->height - dest_clip_region->height;
source_blit_region->height -= offset;
}
return true;
}

161
src/fbgfx/surface.c Normal file
View file

@ -0,0 +1,161 @@
#include "fbgfx/surface.h"
#include "types.h"
#include <stdlib.h>
#include <string.h>
SURFACE *top_screen_surface = NULL;
SURFACE *bottom_screen_surface = NULL;
void surface_init() {
if (top_screen_surface || bottom_screen_surface)
return; // TODO: assert
/*
top_screen_surface = surface_wrap_existing(
gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL),
400, 240,
SURFACE_FORMAT_RGB,
SURFACE_FLAGS_SIDEWAYS_BUFFER
);
bottom_screen_surface = surface_wrap_existing(
gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL),
320, 240,
SURFACE_FORMAT_RGB,
SURFACE_FLAGS_SIDEWAYS_BUFFER
);
*/
}
void surface_exit() {
if (top_screen_surface)
surface_destroy(top_screen_surface);
if (bottom_screen_surface)
surface_destroy(bottom_screen_surface);
top_screen_surface = NULL;
bottom_screen_surface = NULL;
}
void surface_swap_framebuffers() {
/*
top_screen_surface->pixels = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL);
bottom_screen_surface->pixels = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, NULL, NULL);
*/
}
void setup_initial_surface_properties(SURFACE *surface, int width, int height, SURFACE_FORMAT format, SURFACE_FLAGS flags) {
surface->width = width;
surface->height = height;
surface->format = format;
surface->flags = flags;
surface->bytes_per_pixel = surface->format / 8;
surface->clip_region = rect_create(0, 0, surface->width, surface->height);
// lazy calculation for the win
uint32_t reference_index = surface_get_index_of(surface, 0, 0);
surface->x_inc = surface_get_index_of(surface, 1, 0) - reference_index;
surface->y_inc = surface_get_index_of(surface, 0, 1) - reference_index;
}
SURFACE* surface_create(int width, int height, SURFACE_FORMAT format, SURFACE_FLAGS flags) {
SURFACE *surface = malloc(sizeof(SURFACE));
if (surface == NULL)
return NULL;
setup_initial_surface_properties(surface, width, height, format, flags);
size_t pixels_buffer_size = surface_get_buffer_size(surface);
surface->pixels = malloc(pixels_buffer_size);
if (surface->pixels == NULL) {
free(surface);
return NULL;
}
surface->wrapped_buffer = false;
memset(surface->pixels, 0, pixels_buffer_size);
return surface;
}
SURFACE* surface_wrap_existing(void *framebuffer, int width, int height, SURFACE_FORMAT format, SURFACE_FLAGS flags) {
SURFACE *surface = malloc(sizeof(SURFACE));
if (surface == NULL)
return NULL;
setup_initial_surface_properties(surface, width, height, format, flags);
surface->pixels = (COLOR_COMPONENT*)framebuffer;
surface->wrapped_buffer = true;
// note: not clearing framebuffer. since we're using existing memory, maybe the
// calling code wants it the way it is now? don't want to assume anything!
return surface;
}
void surface_resize(SURFACE *surface, int width, int height) {
if (!surface)
return; // TODO: assert
if (surface->wrapped_buffer)
return; // TODO: assert
setup_initial_surface_properties(surface, width, height, surface->format, surface->flags);
size_t new_pixels_buffer_size = surface_get_buffer_size(surface);
COLOR_COMPONENT *pixels = malloc(new_pixels_buffer_size);
if (pixels == NULL)
return; // TODO: assert
free(surface->pixels);
surface->pixels = pixels;
memset(surface->pixels, 0, new_pixels_buffer_size);
}
void surface_destroy(SURFACE *surface) {
if (!surface)
return; // TODO: assert
if (!surface->wrapped_buffer)
free(surface->pixels);
free(surface);
}
//////////////
void surface_clear(SURFACE *surface, COLOR color) {
size_t pixels_buffer_size = surface_get_buffer_size(surface);
COLOR_COMPONENT *p = surface->pixels;
COLOR_COMPONENT *end = p + pixels_buffer_size;
COLOR_COMPONENT r, g, b, a;
color_extract(color, &r, &g, &b, &a);
switch (surface->format) {
case SURFACE_FORMAT_ALPHA:
memset(surface->pixels, a, pixels_buffer_size);
break;
case SURFACE_FORMAT_RGB:
for (; p < end; p += 3) {
p[0] = b;
p[1] = g;
p[2] = r;
}
break;
case SURFACE_FORMAT_RGBA:
for (; p < end; p += 4) {
p[0] = b;
p[1] = g;
p[2] = r;
p[3] = a;
}
break;
}
}

192
src/fbgfx/surface_blit.c Normal file
View file

@ -0,0 +1,192 @@
#include <math/rect.h>
#include <fbgfx/surface.h>
#include "fbgfx/surface.h"
#include "fbgfx/clipping.h"
#include "math/rect.h"
#include "types.h"
typedef struct {
COLOR tint;
} BLIT_PARAMS;
#define BLIT_SOLID_PIXEL(src, dst, src_index, dst_index, params, src_fmt, dst_fmt) \
source_pixel = GET_PIXEL_IDX_FAST(src_fmt, src, src_index); \
SET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index, source_pixel);
#define BLIT_BLEND_PIXEL(src, dst, src_index, dst_index, params, src_fmt, dst_fmt) \
source_pixel = GET_PIXEL_IDX_FAST(src_fmt, src, src_index); \
dest_pixel = GET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index); \
SET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index, color_blend(source_pixel, dest_pixel));
#define BLIT_TRANS_PIXEL(src, dst, src_index, dst_index, params, src_fmt, dst_fmt) \
source_pixel = GET_PIXEL_IDX_FAST(src_fmt, src, src_index); \
if (color_get_a(source_pixel) > 0) \
SET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index, source_pixel);
#define BLIT_TINT_PIXEL(src, dst, src_index, dst_index, params, src_fmt, dst_fmt) \
source_pixel = GET_PIXEL_IDX_FAST(src_fmt, src, src_index); \
SET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index, color_tint(source_pixel, params.tint));
#define BLIT_TINTALPHA_PIXEL(src, dst, src_index, dst_index, params, src_fmt, dst_fmt) \
source_pixel = GET_PIXEL_IDX_FAST(src_fmt, src, src_index); \
source_pixel = color_tint_alpha(color_get_a(source_pixel), params.tint); \
SET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index, source_pixel);
#define BLIT_BLEND_TINT_PIXEL(src, dst, src_index, dst_index, params, src_fmt, dst_fmt) \
source_pixel = GET_PIXEL_IDX_FAST(src_fmt, src, src_index); \
dest_pixel = GET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index); \
source_pixel = color_tint(source_pixel, params.tint); \
SET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index, color_blend(source_pixel, dest_pixel));
#define BLIT_BLEND_TINTALPHA_PIXEL(src, dst, src_index, dst_index, params, src_fmt, dst_fmt) \
source_pixel = GET_PIXEL_IDX_FAST(src_fmt, src, src_index); \
dest_pixel = GET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index); \
source_pixel = color_tint_alpha(color_get_a(source_pixel), params.tint); \
SET_PIXEL_IDX_FAST(dst_fmt, dst, dst_index, color_blend(source_pixel, dest_pixel));
#define BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt) \
blit_pixel(src, dst, src_x_index, dst_x_index, params, src_fmt, dst_fmt); \
src_x_index += src->x_inc; \
dst_x_index += dst->x_inc; \
#define BLIT(src, dst, src_region, dst_x, dst_y, blit_pixel, params, src_fmt, dst_fmt) \
do { \
uint32_t src_x_index; \
uint32_t src_y_index; \
uint32_t dst_x_index; \
uint32_t dst_y_index; \
COLOR source_pixel; \
COLOR dest_pixel; \
\
src_y_index = surface_get_index_of(src, src_region.x, src_region.y); \
dst_y_index = surface_get_index_of(dst, dst_x, dst_y); \
\
int x; \
int y; \
for (y = 0; y < src_region.height; ++y) { \
src_x_index = src_y_index; \
dst_x_index = dst_y_index; \
\
for (x = 0; x < src_region.width; ++x) { \
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
} \
\
src_y_index += src->y_inc; \
dst_y_index += dst->y_inc; \
} \
} while(0)
#define BLIT_UNROLLED8(src, dst, src_region, dst_x, dst_y, blit_pixel, params, src_fmt, dst_fmt) \
do { \
uint32_t src_x_index; \
uint32_t src_y_index; \
uint32_t dst_x_index; \
uint32_t dst_y_index; \
COLOR source_pixel; \
COLOR dest_pixel; \
\
src_y_index = surface_get_index_of(src, src_region.x, src_region.y); \
dst_y_index = surface_get_index_of(dst, dst_x, dst_y); \
\
int x; \
int y; \
for (y = 0; y < src_region.height; ++y) { \
src_x_index = src_y_index; \
dst_x_index = dst_y_index; \
\
for (x = 0; x < src_region.width; x += 8) { \
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
BLIT_INNER_LOOP(src, dst, blit_pixel, params, src_fmt, dst_fmt); \
} \
\
src_y_index += src->y_inc; \
dst_y_index += dst->y_inc; \
} \
} while(0)
#define BLIT_FMT(src_fmt, dst_fmt) ((src_fmt << 16) | dst_fmt)
#define SELECT_BLIT(src, dst, src_region, dst_x, dst_y, blit, blit_pixel, params) \
switch (BLIT_FMT(src->format, dst->format)) { \
case BLIT_FMT(SURFACE_FORMAT_ALPHA, SURFACE_FORMAT_ALPHA): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, alpha, alpha); break; \
case BLIT_FMT(SURFACE_FORMAT_ALPHA, SURFACE_FORMAT_RGB): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, alpha, rgb); break; \
case BLIT_FMT(SURFACE_FORMAT_ALPHA, SURFACE_FORMAT_RGBA): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, alpha, rgba); break; \
case BLIT_FMT(SURFACE_FORMAT_RGB, SURFACE_FORMAT_ALPHA): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, rgb, alpha); break; \
case BLIT_FMT(SURFACE_FORMAT_RGB, SURFACE_FORMAT_RGB): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, rgb, rgb); break; \
case BLIT_FMT(SURFACE_FORMAT_RGB, SURFACE_FORMAT_RGBA): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, rgb, rgba); break; \
case BLIT_FMT(SURFACE_FORMAT_RGBA, SURFACE_FORMAT_ALPHA): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, rgba, alpha); break; \
case BLIT_FMT(SURFACE_FORMAT_RGBA, SURFACE_FORMAT_RGB): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, rgba, rgb); break; \
case BLIT_FMT(SURFACE_FORMAT_RGBA, SURFACE_FORMAT_RGBA): blit(src, dst, src_region, dst_x, dst_y, blit_pixel, params, rgba, rgba); break; \
}
#define DO_BLIT(src, dst, src_region, dst_x, dst_y, blit_pixel, params) \
if (src_region.width % 8 == 0) { \
SELECT_BLIT(src, dst, src_region, dst_x, dst_y, BLIT_UNROLLED8, blit_pixel, params); \
} else { \
SELECT_BLIT(src, dst, src_region, dst_x, dst_y, BLIT, blit_pixel, params); \
}
void surface_blit_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y) {
RECT source_region = rect_create(source_x, source_y, source_width, source_height);
bool on_screen = clip_blit_region(&destination->clip_region, &source_region, &dest_x, &dest_y);
if (!on_screen)
return;
DO_BLIT(source, destination, source_region, dest_x, dest_y, BLIT_SOLID_PIXEL, NULL);
}
void surface_blit_blend_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y) {
RECT source_region = rect_create(source_x, source_y, source_width, source_height);
bool on_screen = clip_blit_region(&destination->clip_region, &source_region, &dest_x, &dest_y);
if (!on_screen)
return;
DO_BLIT(source, destination, source_region, dest_x, dest_y, BLIT_BLEND_PIXEL, NULL);
}
void surface_blit_trans_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y) {
RECT source_region = rect_create(source_x, source_y, source_width, source_height);
bool on_screen = clip_blit_region(&destination->clip_region, &source_region, &dest_x, &dest_y);
if (!on_screen)
return;
DO_BLIT(source, destination, source_region, dest_x, dest_y, BLIT_TRANS_PIXEL, NULL);
}
void surface_blit_tint_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, COLOR tint) {
RECT source_region = rect_create(source_x, source_y, source_width, source_height);
bool on_screen = clip_blit_region(&destination->clip_region, &source_region, &dest_x, &dest_y);
if (!on_screen)
return;
BLIT_PARAMS params;
params.tint = tint;
if (source->format == SURFACE_FORMAT_ALPHA) {
DO_BLIT(source, destination, source_region, dest_x, dest_y, BLIT_TINTALPHA_PIXEL, params);
} else {
DO_BLIT(source, destination, source_region, dest_x, dest_y, BLIT_TINT_PIXEL, params);
}
}
void surface_blit_blend_tint_region(SURFACE *source, SURFACE *destination, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, COLOR tint) {
RECT source_region = rect_create(source_x, source_y, source_width, source_height);
bool on_screen = clip_blit_region(&destination->clip_region, &source_region, &dest_x, &dest_y);
if (!on_screen)
return;
BLIT_PARAMS params;
params.tint = tint;
if (source->format == SURFACE_FORMAT_ALPHA) {
DO_BLIT(source, destination, source_region, dest_x, dest_y, BLIT_BLEND_TINTALPHA_PIXEL, params);
} else {
DO_BLIT(source, destination, source_region, dest_x, dest_y, BLIT_BLEND_TINT_PIXEL, params);
}
}

View file

@ -0,0 +1,16 @@
#include "fbgfx/surface.h"
#include "types.h"
void surface_convert_rgb_to_bgr(SURFACE *surface) {
if (surface->format != SURFACE_FORMAT_RGB && surface->format != SURFACE_FORMAT_RGBA)
return;
uint32_t buffer_size = surface_get_buffer_size(surface);
COLOR_COMPONENT *p = surface->pixels;
COLOR_COMPONENT *end = p + buffer_size;
for (; p < end; p += surface->bytes_per_pixel) {
SWAP(COLOR_COMPONENT, p[0], p[2]);
}
}

View file

@ -0,0 +1,280 @@
#include "fbgfx/surface.h"
#include "fbgfx/clipping.h"
#include "math/rect.h"
#include "types.h"
#include <stdlib.h>
#define SIGN(x) ((x < 0) ? -1 : ((x > 0) ? 1 : 0))
/**
* PIXEL PLOTTING
*/
static inline void set_pixel_fast(SURFACE *surface, int x, int y, COLOR color) {
switch (surface->format) {
case SURFACE_FORMAT_ALPHA: surface_set_pixel_alpha_fast(surface, x, y, color); break;
case SURFACE_FORMAT_RGB: surface_set_pixel_rgb_fast(surface, x, y, color); break;
case SURFACE_FORMAT_RGBA: surface_set_pixel_rgba_fast(surface, x, y, color); break;
}
}
static inline COLOR get_pixel_fast(const SURFACE *surface, int x, int y) {
switch (surface->format) {
case SURFACE_FORMAT_ALPHA: return surface_get_pixel_alpha_fast(surface, x, y);
case SURFACE_FORMAT_RGB: return surface_get_pixel_rgb_fast(surface, x, y);
case SURFACE_FORMAT_RGBA: return surface_get_pixel_rgba_fast(surface, x, y);
}
}
void surface_set_pixel(SURFACE *surface, int x, int y, COLOR color) {
if (!point_in_bounds(&surface->clip_region, x, y))
return;
set_pixel_fast(surface, x, y, color);
}
void surface_set_pixel_fast(SURFACE *surface, int x, int y, COLOR color) {
set_pixel_fast(surface, x, y, color);
}
COLOR surface_get_pixel(const SURFACE *surface, int x, int y) {
if (!point_in_bounds(&surface->clip_region, x, y))
return 0;
return get_pixel_fast(surface, x, y);
}
COLOR surface_get_pixel_fast(SURFACE *surface, int x, int y) {
return get_pixel_fast(surface, x, y);
}
/**
* LINE DRAWING
*/
#define DRAW_LINE(surface, x1, y1, x2, y2, color, draw_pixel) \
do { \
int delta_x = x2 - x1; \
int delta_y = y2 - y1; \
int delta_x_abs = abs(delta_x); \
int delta_y_abs = abs(delta_y); \
int delta_x_sign = SIGN(delta_x); \
int delta_y_sign = SIGN(delta_y); \
int x = delta_y_abs / 2; \
int y = delta_x_abs / 2; \
int px = x1; \
int py = y1; \
\
draw_pixel(surface, px, py, color); \
\
if (delta_x_abs >= delta_y_abs) { \
int i; \
for(i = 0; i < delta_x_abs; ++i) { \
y += delta_y_abs; \
\
if (y >= delta_x_abs) { \
y -= delta_x_abs; \
py += delta_y_sign; \
} \
\
px += delta_x_sign; \
draw_pixel(surface, px, py, color); \
} \
\
} else { \
int i; \
for(i = 0; i < delta_y_abs; ++i) { \
x += delta_x_abs; \
\
if (x >= delta_y_abs) { \
x -= delta_y_abs; \
px += delta_x_sign; \
} \
\
py += delta_y_sign; \
draw_pixel(surface, px, py, color); \
} \
} \
} while (0)
void surface_line_fast(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color) {
switch (surface->format) {
case SURFACE_FORMAT_ALPHA: DRAW_LINE(surface, x1, y1, x2, y2, color, surface_set_pixel_alpha_fast); break;
case SURFACE_FORMAT_RGB: DRAW_LINE(surface, x1, y1, x2, y2, color, surface_set_pixel_rgb_fast); break;
case SURFACE_FORMAT_RGBA: DRAW_LINE(surface, x1, y1, x2, y2, color, surface_set_pixel_rgba_fast); break;
}
}
void surface_line(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color) {
if (!line_in_bounds(&surface->clip_region, x1, y1, x2, y2))
return;
DRAW_LINE(surface, x1, y1, x2, y2, color, surface_set_pixel);
}
#define DRAW_FAST_HLINE(surface, x1, x2, y, color, fmt) \
do { \
if (x2 < x1) \
SWAP(int, x1, x2); \
\
int x; \
uint32_t i = surface_get_index_of(surface, x1, y); \
for (x = x1; x <= x2; ++x, i += surface->x_inc) \
SET_PIXEL_IDX_FAST(fmt, surface, i, color); \
} while(0)
void surface_hline_fast(SURFACE *surface, int x1, int x2, int y, COLOR color) {
switch (surface->format) {
case SURFACE_FORMAT_ALPHA: DRAW_FAST_HLINE(surface, x1, x2, y, color, alpha); break;
case SURFACE_FORMAT_RGB: DRAW_FAST_HLINE(surface, x1, x2, y, color, rgb); break;
case SURFACE_FORMAT_RGBA: DRAW_FAST_HLINE(surface, x1, x2, y, color, rgba); break;
}
}
void surface_hline(SURFACE *surface, int x1, int x2, int y, COLOR color) {
if (!clamp_to_region(&surface->clip_region, &x1, &y, &x2, &y))
return;
surface_hline_fast(surface, x1, x2, y, color);
}
#define DRAW_FAST_VLINE(surface, x, y1, y2, color, fmt) \
do { \
if (y2 < y1) \
SWAP(int, y1, y2); \
\
int y; \
uint32_t i = surface_get_index_of(surface, x, y1); \
for (y = y1; y <= y2; ++y, i += surface->y_inc) \
SET_PIXEL_IDX_FAST(fmt, surface, i, color); \
} while (0)
void surface_vline_fast(SURFACE *surface, int x, int y1, int y2, COLOR color) {
switch (surface->format) {
case SURFACE_FORMAT_ALPHA: DRAW_FAST_VLINE(surface, x, y1, y2, color, alpha); break;
case SURFACE_FORMAT_RGB: DRAW_FAST_VLINE(surface, x, y1, y2, color, rgb); break;
case SURFACE_FORMAT_RGBA: DRAW_FAST_VLINE(surface, x, y1, y2, color, rgba); break;
}
}
void surface_vline(SURFACE *surface, int x, int y1, int y2, COLOR color) {
if (!clamp_to_region(&surface->clip_region, &x, &y1, &x, &y2))
return;
surface_vline_fast(surface, x, y1, y2, color);
}
/**
* RECTANGLE DRAWING
*/
void surface_rect(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color) {
if (!line_in_bounds(&surface->clip_region, x1, y1, x2, y2))
return;
if (x2 < x1)
SWAP(int, x1, x2);
if (y2 < y1)
SWAP(int, y1, y2);
int x;
for (x = x1; x <= x2; ++x) {
surface_set_pixel(surface, x, y1, color);
surface_set_pixel(surface, x, y2, color);
}
int y;
for (y = y1; y <= y2; ++y) {
surface_set_pixel(surface, x1, y, color);
surface_set_pixel(surface, x2, y, color);
}
}
#define DRAW_FAST_RECT(surface, x1, y1, x2, y2, color, fmt) \
do { \
if (x2 < x1) \
SWAP(int, x1, x2); \
if (y2 < y1) \
SWAP(int, y1, y2); \
\
uint32_t a; \
uint32_t b; \
\
int x = x1; \
a = surface_get_index_of(surface, x, y1); \
b = surface_get_index_of(surface, x, y2); \
for (; x <= x2; ++x) { \
SET_PIXEL_IDX_FAST(fmt, surface, a, color); \
SET_PIXEL_IDX_FAST(fmt, surface, b, color); \
a += surface->x_inc; \
b += surface->x_inc; \
} \
\
int y = y1; \
a = surface_get_index_of(surface, x1, y); \
b = surface_get_index_of(surface, x2, y); \
for (; y <= y2; ++y) { \
SET_PIXEL_IDX_FAST(fmt, surface, a, color); \
SET_PIXEL_IDX_FAST(fmt, surface, b, color); \
a += surface->y_inc; \
b += surface->y_inc; \
} \
} while (0)
void surface_rect_fast(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color) {
switch (surface->format) {
case SURFACE_FORMAT_ALPHA: DRAW_FAST_RECT(surface, x1, y1, x2, y2, color, alpha); break;
case SURFACE_FORMAT_RGB: DRAW_FAST_RECT(surface, x1, y1, x2, y2, color, rgb); break;
case SURFACE_FORMAT_RGBA: DRAW_FAST_RECT(surface, x1, y1, x2, y2, color, rgba); break;
}
}
/**
* FILLED RECTANGLE DRAWING
*/
#define DRAW_FAST_RECT_FILLED(surface, x1, y1, x2, y2, color, fmt) \
do { \
if (x2 < x1) \
SWAP(int, x1, x2); \
if (y2 < y1) \
SWAP(int, y1, y2); \
\
uint32_t x_index; \
uint32_t y_index; \
int x; \
int y; \
\
y_index = surface_get_index_of(surface, x1, y1); \
for (y = y1; y <= y2; ++y) { \
x_index = y_index; \
for (x = x1; x <= x2; ++x) { \
SET_PIXEL_IDX_FAST(fmt, surface, x_index, color); \
x_index += surface->x_inc; \
} \
\
y_index += surface->y_inc; \
} \
} while (0)
void surface_rect_filled_fast(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color) {
switch (surface->format) {
case SURFACE_FORMAT_ALPHA: DRAW_FAST_RECT_FILLED(surface, x1, y1, x2, y2, color, alpha); break;
case SURFACE_FORMAT_RGB: DRAW_FAST_RECT_FILLED(surface, x1, y1, x2, y2, color, rgb); break;
case SURFACE_FORMAT_RGBA: DRAW_FAST_RECT_FILLED(surface, x1, y1, x2, y2, color, rgba); break;
}
}
void surface_rect_filled(SURFACE *surface, int x1, int y1, int x2, int y2, COLOR color) {
if (!clamp_to_region(&surface->clip_region, &x1, &y1, &x2, &y2))
return;
surface_rect_filled_fast(surface, x1, y1, x2, y2, color);
}

69
src/main.cpp Normal file
View file

@ -0,0 +1,69 @@
#include <stdio.h>
#include <time.h>
#include <fbgfx/surface.h>
#include "vm/vm.h"
#include "fbgfx/surface.h"
#include "fbgfx/clipping.h"
#include "assets/image.h"
#include "vm/vm_window.h"
static inline double get_seconds() {
return (double)clock() / CLOCKS_PER_SEC;
}
int main(int argc, char** argv) {
log_init();
input_init();
WINDOW *window = window_init("Rasterizer Test", 1280, 960, 320, 240);
if (!window)
return 1;
SURFACE *sprite = image_load_file("/Users/gered/resources/grass01.bmp");
SURFACE *sprite2 = surface_create(16, 16, SURFACE_FORMAT_ALPHA, SURFACE_FLAGS_NONE);
surface_clear(sprite2, color_create_rgba(0, 0, 0, 0));
surface_rect_filled(sprite2, 4, 4, 11, 11, color_create_rgba(0, 0, 0, 128));
surface_line(sprite2, 0, 0, 15, 15, color_create_rgba(0, 0, 0, 255));
double before;
double after;
int i;
before = get_seconds();
for (i = 0; i < 500000; ++i) {
surface_blit(sprite, window->surface, 0, 0);
//surface_blit_blend(sprite, window->surface, 0, 0);
}
after = get_seconds();
printf("time taken: %f\n", after - before);
while (true) {
if (!window_do_events(window))
break;
if (input_is_key_pressed(KSYM_ESCAPE))
break;
//surface_clear(window->surface, color_create_rgba(192, 192, 192, 255));
int x, y;
for (y = 0; y < window->surface->height; y += 16) {
for (x = 0; x < window->surface->width; x += 16) {
surface_blit(sprite, window->surface, x, y);
}
}
surface_line(window->surface, window->viewport_width / 2, window->viewport_height / 2, input_get_mouse_x(), input_get_mouse_y(), COLOR_YELLOW);
//surface_blit_tint(sprite, window->surface, INPUT_GetMouseX(), INPUT_GetMouseY(), COLOR_DOS_BROWN);
surface_blit_blend(sprite2, window->surface, 0, 0);
surface_blit_blend_tint(sprite2, window->surface, 16, 0, color_create_rgba(255, 0, 0, 64));
window_render(window);
}
window_destroy(window);
input_destroy();
log_end();
return 0;
}

8
src/vendor/stb_image.c vendored Normal file
View file

@ -0,0 +1,8 @@
/*
* This source file obviously only exists to provide a spot
* for the stb_image implementation to exist.
*/
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

4687
src/vendor/stb_image.h vendored Normal file

File diff suppressed because it is too large Load diff

8
src/vendor/stb_truetype.c vendored Normal file
View file

@ -0,0 +1,8 @@
/*
* This source file obviously only exists to provide a spot
* for the stb_typetype implementation to exist.
*/
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"

2100
src/vendor/stb_truetype.h vendored Normal file

File diff suppressed because it is too large Load diff

14
src/vm/vm.h Normal file
View file

@ -0,0 +1,14 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "vm_stdinc.h"
#include "vm_log.h"
#include "vm_window.h"
#include "vm_input.h"
#ifdef __cplusplus
}
#endif

123
src/vm/vm_input.c Normal file
View file

@ -0,0 +1,123 @@
#include "vm_input.h"
#include "vm_log.h"
#include <string.h>
const int MAX_KEYS = SDL_NUM_SCANCODES;
const int MAX_MOUSE_BUTTONS = 3;
struct {
bool keys[MAX_KEYS];
bool locked_keys[MAX_KEYS];
bool mouse_buttons[MAX_MOUSE_BUTTONS];
bool locked_mouse_buttons[MAX_MOUSE_BUTTONS];
uint mouse_x;
uint mouse_y;
int mouse_delta_x;
int mouse_delta_y;
} input_state;
bool input_init() {
LOG_INFO("input_init\n");
memset(input_state.keys, 0, sizeof(bool) * MAX_KEYS);
memset(input_state.locked_keys, 0, sizeof(bool) * MAX_KEYS);
memset(input_state.mouse_buttons, 0, sizeof(bool) * MAX_MOUSE_BUTTONS);
memset(input_state.locked_mouse_buttons, 0, sizeof(bool) * MAX_MOUSE_BUTTONS);
input_state.mouse_x = 0;
input_state.mouse_y = 0;
input_state.mouse_delta_x = 0;
input_state.mouse_delta_y = 0;
return true;
}
void input_destroy() {
LOG_INFO("input_destroy\n");
}
void input_on_key_event(SDL_Event *event) {
if (event->type == SDL_KEYUP) {
int key = event->key.keysym.scancode;
input_state.keys[key] = false;
input_state.locked_keys[key] = false;
} else if (event->type == SDL_KEYDOWN) {
int key = event->key.keysym.scancode;
input_state.keys[key] = !(input_state.locked_keys[key]);
}
}
void input_on_mouse_event(SDL_Event *event) {
int button;
switch (event->type) {
case SDL_MOUSEMOTION:
input_state.mouse_x = event->motion.x;
input_state.mouse_y = event->motion.y;
input_state.mouse_delta_x = event->motion.xrel;
input_state.mouse_delta_y = event->motion.yrel;
break;
case SDL_MOUSEBUTTONUP:
button = event->button.button;
input_state.mouse_buttons[button] = false;
input_state.locked_mouse_buttons[button] = false;
break;
case SDL_MOUSEBUTTONDOWN:
button = event->button.button;
input_state.mouse_buttons[button] = !(input_state.locked_mouse_buttons[button]);
break;
}
}
bool input_is_key_down(KEYS key) {
SDL_Scancode scancode = SDL_GetScancodeFromKey(key);
return input_state.keys[scancode] && !input_state.locked_keys[scancode];
}
bool input_is_key_pressed(KEYS key) {
SDL_Scancode scancode = SDL_GetScancodeFromKey(key);
if (input_state.keys[scancode] && !input_state.locked_keys[scancode]) {
input_state.locked_keys[scancode] = true;
return true;
} else
return false;
}
void input_lock_key(KEYS key) {
SDL_Scancode scancode = SDL_GetScancodeFromKey(key);
input_state.locked_keys[scancode] = true;
}
bool input_is_mouse_button_down(MOUSE_BUTTONS button) {
return input_state.mouse_buttons[button] && !input_state.locked_mouse_buttons[button];
}
bool input_is_mouse_button_pressed(MOUSE_BUTTONS button) {
if (input_state.mouse_buttons[button] && !input_state.locked_mouse_buttons[button]) {
input_state.locked_mouse_buttons[button] = true;
return true;
} else
return false;
}
void input_lock_mouse_button(MOUSE_BUTTONS button) {
input_state.locked_mouse_buttons[button] = true;
}
int input_get_mouse_delta_x() {
return input_state.mouse_delta_x;
}
int input_get_mouse_delta_y() {
return input_state.mouse_delta_y;
}
uint input_get_mouse_x() {
return input_state.mouse_x;
}
uint input_get_mouse_y() {
return input_state.mouse_y;
}

30
src/vm/vm_input.h Normal file
View file

@ -0,0 +1,30 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "vm_stdinc.h"
#include "vm_input_codes.h"
#include "SDL.h"
bool input_init();
void input_destroy();
void input_on_key_event(SDL_Event *event);
void input_on_mouse_event(SDL_Event *event);
bool input_is_key_down(KEYS key);
bool input_is_key_pressed(KEYS key);
void input_lock_key(KEYS key);
bool input_is_mouse_button_down(MOUSE_BUTTONS button);
bool input_is_mouse_button_pressed(MOUSE_BUTTONS button);
void input_lock_mouse_button(MOUSE_BUTTONS button);
int input_get_mouse_delta_x();
int input_get_mouse_delta_y();
uint input_get_mouse_x();
uint input_get_mouse_y();
#ifdef __cplusplus
}
#endif

107
src/vm/vm_input_codes.h Normal file
View file

@ -0,0 +1,107 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "SDL.h"
typedef enum {
MOUSE_LEFT = 1,
MOUSE_MIDDLE = 2,
MOUSE_RIGHT = 3
} MOUSE_BUTTONS;
// the idea behind this enum is that on other platforms (that may or may not use SDL)
// we could just remap to the platform's API, but use the same key constants
typedef enum {
KSYM_UNKNOWN = 0,
KSYM_ESCAPE = SDLK_ESCAPE,
KSYM_BACKSPACE = SDLK_BACKSPACE,
KSYM_END = SDLK_END,
KSYM_SOFT_LEFT = 0,
KSYM_SOFT_RIGHT = 0,
KSYM_HOME = SDLK_HOME,
KSYM_BACK = 0,
KSYM_CALL = 0,
KSYM_ENDCALL = 0,
KSYM_0 = SDLK_0,
KSYM_1 = SDLK_1,
KSYM_2 = SDLK_2,
KSYM_3 = SDLK_3,
KSYM_4 = SDLK_4,
KSYM_5 = SDLK_5,
KSYM_6 = SDLK_6,
KSYM_7 = SDLK_7,
KSYM_8 = SDLK_8,
KSYM_9 = SDLK_9,
KSYM_ASTERISK = SDLK_ASTERISK,
KSYM_HASH = SDLK_HASH,
KSYM_UP = SDLK_UP,
KSYM_DOWN = SDLK_DOWN,
KSYM_LEFT = SDLK_LEFT,
KSYM_RIGHT = SDLK_RIGHT,
KSYM_DPAD_CENTER = 0,
KSYM_VOLUME_UP = 0,
KSYM_VOLUME_DOWN = 0,
KSYM_CAMERA = 0,
KSYM_CLEAR = 0,
KSYM_A = SDLK_a,
KSYM_B = SDLK_b,
KSYM_C = SDLK_c,
KSYM_D = SDLK_d,
KSYM_E = SDLK_e,
KSYM_F = SDLK_f,
KSYM_G = SDLK_g,
KSYM_H = SDLK_h,
KSYM_I = SDLK_i,
KSYM_J = SDLK_j,
KSYM_K = SDLK_k,
KSYM_L = SDLK_l,
KSYM_M = SDLK_m,
KSYM_N = SDLK_n,
KSYM_O = SDLK_o,
KSYM_P = SDLK_p,
KSYM_Q = SDLK_q,
KSYM_R = SDLK_r,
KSYM_S = SDLK_s,
KSYM_T = SDLK_t,
KSYM_U = SDLK_u,
KSYM_V = SDLK_v,
KSYM_W = SDLK_w,
KSYM_X = SDLK_x,
KSYM_Y = SDLK_y,
KSYM_Z = SDLK_z,
KSYM_COMMA = SDLK_COMMA,
KSYM_PERIOD = SDLK_PERIOD,
KSYM_LALT = SDLK_LALT,
KSYM_RALT = SDLK_RALT,
KSYM_RCTRL = SDLK_RCTRL,
KSYM_LCTRL = SDLK_LCTRL,
KSYM_LSHIFT = SDLK_LSHIFT,
KSYM_RSHIFT = SDLK_RSHIFT,
KSYM_TAB = SDLK_TAB,
KSYM_SPACE = SDLK_SPACE,
KSYM_SYM = 0,
KSYM_RETURN = SDLK_RETURN,
KSYM_DELETE = SDLK_DELETE,
KSYM_MINUS = SDLK_MINUS,
KSYM_EQUALS = SDLK_EQUALS,
KSYM_LEFTBRACKET = SDLK_LEFTBRACKET,
KSYM_RIGHTBRACKET = SDLK_RIGHTBRACKET,
KSYM_BACKSLASH = SDLK_BACKSLASH,
KSYM_SEMICOLON = SDLK_SEMICOLON,
KSYM_QUOTE = SDLK_QUOTE,
KSYM_SLASH = SDLK_SLASH,
KSYM_AT = SDLK_AT,
KSYM_PLUS = SDLK_PLUS,
KSYM_MENU = 0,
KSYM_SEARCH = 0,
KSYM_PAGEUP = SDLK_PAGEUP,
KSYM_PAGEDOWN = SDLK_PAGEDOWN
} KEYS;
#ifdef __cplusplus
}
#endif

73
src/vm/vm_log.c Normal file
View file

@ -0,0 +1,73 @@
#include "vm_log.h"
#include <stdarg.h>
#include <time.h>
#include <stdio.h>
#include <sys/timeb.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define WIN32_EXTRA_LEAN
#include <windows.h>
#endif
FILE *fp_log = NULL;
const char *log_filename = "run.log";
const size_t LOG_BUFFER_SIZE = 8096;
char _temp_buffer[LOG_BUFFER_SIZE];
char _buffer[LOG_BUFFER_SIZE];
void log_init() {
fp_log = fopen(log_filename, "w");
if (!fp_log)
{
snprintf(_buffer, LOG_BUFFER_SIZE, "Log file \"%s\" could not be opened. Further LOG_Write() calls will only appear in STDOUT.\n", log_filename);
printf("%s", _buffer);
#ifdef _MSC_VER
OutputDebugStringA(_buffer);
#endif
}
}
void log_end() {
if (fp_log)
fclose(fp_log);
}
void log_write(const char *tag, const char *format, ...) {
static char timestamp[15];
int hour;
int minute;
int second;
unsigned int milliseconds;
struct timeb seconds;
ftime(&seconds);
struct tm *currentDate = localtime(&seconds.time);
hour = currentDate->tm_hour;
minute = currentDate->tm_min;
second = currentDate->tm_sec;
milliseconds = seconds.millitm;
// Prepend the date/time
snprintf(timestamp, 15, "[%02d:%02d:%02d,%03d]", hour, minute, second, milliseconds);
// now the user-provided format string and arguments...
va_list args;
va_start(args, format);
vsnprintf(_temp_buffer, LOG_BUFFER_SIZE, format, args);
va_end(args);
// write it all out
snprintf(_buffer, LOG_BUFFER_SIZE, "%s [%s] %s", timestamp, tag, _temp_buffer);
if (fp_log)
fprintf(fp_log, "%s", _buffer);
printf("%s", _buffer);
#ifdef _MSC_VER
OutputDebugStringA(_buffer);
#endif
}

24
src/vm/vm_log.h Normal file
View file

@ -0,0 +1,24 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "vm_stdinc.h"
void log_init();
void log_end();
void log_write(const char *tag, const char *format, ...);
#define LOG_INFO(...) (log_write("INFO", __VA_ARGS__))
#define LOG_WARN(...) (log_write("WARN", __VA_ARGS__))
#define LOG_ERROR(...) (log_write("ERROR", __VA_ARGS__))
#ifdef DEBUG
#define LOG_DEBUG(...) (log_write("DEBUG", __VA_ARGS__))
#else
#define LOG_DEBUG(...)
#endif
#ifdef __cplusplus
}
#endif

18
src/vm/vm_stdinc.h Normal file
View file

@ -0,0 +1,18 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
typedef unsigned long ulong;
typedef unsigned int uint;
typedef unsigned short ushort;
typedef unsigned char uchar;
#ifdef __cplusplus
}
#endif

190
src/vm/vm_window.c Normal file
View file

@ -0,0 +1,190 @@
#include "vm_window.h"
#include "vm_log.h"
#include "vm_input.h"
#include "fbgfx/surface.h"
static SDL_Texture*create_texture(SDL_Renderer *renderer, int width, int height) {
return SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGR24, SDL_TEXTUREACCESS_STREAMING, width, height);
}
static bool resize_texture(SDL_Renderer *renderer, SDL_Texture **texture, int width, int height) {
if (*texture)
SDL_DestroyTexture(*texture);
*texture = create_texture(renderer, width, height);
if (*texture == NULL) {
LOG_ERROR("Failed to resize texture to %dx%d\n", width, height);
return false;
}
return true;
}
static bool resize_window(WINDOW *window, int width, int height) {
if (window->fixed_viewport)
return true;
LOG_INFO("Resizing window to %dx%d\n", width, height);
window->width = width;
window->height = height;
bool result = resize_texture(window->renderer, &window->texture, width, height);
if (result)
surface_resize(window->surface, width, height);
return result;
}
WINDOW * window_init(const char *title, int width, int height, int viewport_width, int viewport_height) {
LOG_INFO("window_init(%s, %d, %d, %d, %d)\n", title, width, height, viewport_width, viewport_height);
WINDOW *window = malloc(sizeof(WINDOW));
window->surface = NULL;
LOG_INFO("Initializing SDL.\n");
if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
LOG_ERROR("SDL_Init error: %s\n", SDL_GetError());
window_destroy(window);
return NULL;
}
window->initialized = true;
window->width = width;
window->height = height;
LOG_INFO("Creating a %dx%d window.\n", window->width, window->height);
window->window = SDL_CreateWindow(title,
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
window->width, window->height,
SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
if (window->window == NULL) {
LOG_ERROR("SDL_CreateWindow error: %s\n", SDL_GetError());
window_destroy(window);
return NULL;
}
LOG_INFO("Creating an SDL renderer.\n");
window->renderer = SDL_CreateRenderer(window->window, -1, 0);
if (window->renderer == NULL) {
LOG_ERROR("SDL_CreateRenderer error: %s\n", SDL_GetError());
window_destroy(window);
return NULL;
}
if (viewport_width != 0 && viewport_height != 0) {
LOG_INFO("Using fixed viewport of %dx%d\n", viewport_width, viewport_height);
window->fixed_viewport = true;
window->viewport_width = viewport_width;
window->viewport_height = viewport_height;
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
SDL_RenderSetLogicalSize(window->renderer, window->viewport_width, window->viewport_height);
} else {
LOG_INFO("Using automatically sized viewport (always match window dimensions)\n");
window->fixed_viewport = false;
window->viewport_width = width;
window->viewport_height = height;
}
LOG_INFO("Creating a %dx%d texture for the 2D framebuffer.\n", window->viewport_width, window->viewport_height);
window->texture = create_texture(window->renderer, window->viewport_width, window->viewport_height);
if (window->texture == NULL) {
LOG_ERROR("SDL_CreateTexture error: %s\n", SDL_GetError());
window_destroy(window);
return NULL;
}
window->surface = surface_create(window->viewport_width, window->viewport_height, SURFACE_FORMAT_RGB, SURFACE_FLAGS_NONE);
return window;
}
bool window_do_events(WINDOW *window) {
bool quit = false;
SDL_Event e;
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_QUIT:
LOG_INFO("Event: SDL_QUIT\n");
quit = true;
break;
case SDL_WINDOWEVENT:
if (e.window.event == SDL_WINDOWEVENT_RESIZED) {
LOG_INFO("Event: SDL_WINDOWEVENT_RESIZED\n");
if (!resize_window(window, e.window.data1, e.window.data2))
quit = true;
}
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
input_on_key_event(&e);
break;
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
input_on_mouse_event(&e);
break;
}
}
return !quit;
}
void window_render(WINDOW *window) {
// HACK: there seems to be either some weird behaviour i'm just not understanding or a bug somewhere in
// either SDL or my own code that sets up these window / surface dimensions...
// basically, the "pass a NULL SDL_Rect to specify the entire surface region" does not seem to
// work as expected. if I use NULL's for all those arguments below, then I get a full-width
// but half-ish height (yes, not exactly half... seems random...) texture being displayed.
// eeerrrrggghh! wtf!?
SDL_Rect src_region;
src_region.x = 0;
src_region.y = 0;
src_region.w = window->surface->width;
src_region.h = window->surface->height;
SDL_Rect dest_region;
dest_region.x = 0;
dest_region.y = 0;
dest_region.w = window->width;
dest_region.h = window->height;
SDL_UpdateTexture(window->texture, NULL, (void*)window->surface->pixels, window->surface->width * window->surface->bytes_per_pixel);
SDL_RenderClear(window->renderer);
// SDL_RenderCopy(window->renderer, window->texture, &src_region, &dest_region);
SDL_RenderCopy(window->renderer, window->texture, NULL, NULL);
SDL_RenderPresent(window->renderer);
}
void window_destroy(WINDOW *window) {
LOG_INFO("window_destroy\n");
surface_destroy(window->surface);
if (window->texture) {
LOG_INFO("Destroying 2D framebuffer texture.\n");
SDL_DestroyTexture(window->texture);
}
if (window->renderer) {
LOG_INFO("Destroying SDL renderer.\n");
SDL_DestroyRenderer(window->renderer);
}
if (window->window) {
LOG_INFO("Destroying window.\n");
SDL_DestroyWindow(window->window);
}
if (window->initialized) {
LOG_INFO("SDL_Quit\n");
SDL_Quit();
}
free(window);
}

30
src/vm/vm_window.h Normal file
View file

@ -0,0 +1,30 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "vm_stdinc.h"
#include "SDL.h"
typedef struct {
bool initialized;
SDL_Window *window;
int width;
int height;
SDL_Renderer *renderer;
SDL_Texture *texture;
bool fixed_viewport;
int viewport_width;
int viewport_height;
struct SURFACE *surface;
} WINDOW;
WINDOW * window_init(const char *title, int width, int height, int viewport_width, int viewport_height);
bool window_do_events(WINDOW *window);
void window_render(WINDOW *window);
void window_destroy(WINDOW *window);
#ifdef __cplusplus
}
#endif