Repository navigation
Graphics
Вся отрисовка идёт в один 16-битный буфер и делается собственными блиттерами -
ни SDL_Renderer, ни SDL_Surface в игровой логике нет. SDL получает готовый
кадр один раз за проход: Platform_Present() заливает его в текстуру и выводит.
typedef struct {
uint16_t *pixels;
int bpp;
int fmt; /* 0x80 = RGB565, 0x40 = RGB555 */
int x_pitch; /* шаг между пикселями в элементах uint16_t */
int y_pitch; /* шаг между строками */
} jb_surface;
typedef struct {
uint16_t *pixels;
int w; /* и ширина, и шаг строки источника */
int h;
} jb_sprite;Оригинал читал приёмник как массив int по фиксированным индексам:
[0] пиксели, [6] bpp, [7] формат, [9] шаг по X, [10] шаг по Y
(Blit_Core 0x00021a98). Спрайт - блок 0xc байт из
Sprite_LoadResourceBitmap 0x00021a04: пиксели, ширина, высота, причём ширина
работает и как шаг строки.
Отсечение - три глобала (0x000269e0 / 0x000269e4 / 0x000269e8) с
начальными значениями 240 / 320 / 320; в порте это jb_clip_w, jb_clip_h,
jb_clip_h_row.
| Функция | Оригинал | Смысл |
|---|---|---|
Color_Pack16 |
0x00023bb4 |
COLORREF (0x00BBGGRR) -> RGB565 |
Color_Pack16If16bpp |
0x00024030 |
то же, но 0, если приёмник не 16 bpp |
Color_Blend |
0x00023c3c |
смешивание, процент 0..100 |
Color_MaskTint |
0x00023ddc |
тинт по маске |
Две тонкости, из-за которых легко получить «почти правильную» картинку:
-
Color_Blendвзвешивает пиксель ПРИЁМНИКА, а не источника. Процент 0 отдаёт источник без изменений. Перепутанное направление даёт визуально похожий, но не тот результат на воде и на свечении. -
В
Color_MaskTintпокрытие берётся из СИНЕГО канала источника, а(r,g,b)- это цвет, которым красят. Так работает и шрифт, и тень мяча: в битмапе лежит маска, цвет приходит аргументом.
Ключ прозрачности - 0xffff означает «ключа нет» (JB_NO_KEY).
Десять штук; каждый - обёртка над ядром, отличается только тем, как считает шаги по источнику и что делает с пикселем.
| Функция | Оригинал | Что делает |
|---|---|---|
Blit_NoKey |
0x00023a3c |
копия 1:1, без ключа |
Blit |
0x00023b78 |
копия 1:1 с ключом |
Blit_Core |
0x00021a98 |
ядро: копия с ключом и явными src_x/src_y
|
Blit_Keyed |
0x000239f8 |
копия с ключом и blend
|
Blit_Glyph |
0x000239ac |
по маске, цветом (r,g,b) - шрифт, тень |
Blit_TileH |
0x00023aa8 |
одна строка приёмника, масштаб по X |
Blit_TileHV |
0x00023a7c |
масштаб по X и Y в прямоугольник w x h
|
Blit_TileH_Ofs |
0x00023b44 |
Blit_TileH + Color_Blend на каждый пиксель |
Blit_TileHV_Ofs |
0x00023ad4 |
Blit_TileHV + Color_Blend
|
Blit_TileHV_Glyph |
0x00023b08 |
Blit_TileHV + тинт по маске, без ключа |
Blit_TileH возвращает номер выбранной строки таблицы масштаба - трасса
использует это возвращаемое значение, поэтому его нельзя «упростить» до void.
Gfx_BuildScaleTables() (оригинал Gfx_CreateBackBuffer 0x00021698) строит три
таблицы шагов - для каждой целевой ширины заранее посчитано, как идти по
источнику:
| Таблица | Источник | Шаг таблицы | Ширины |
|---|---|---|---|
0x00068518 |
100 px | 200 | 1..199 |
0x00065ed0 |
64 px | 70 | 1..63 |
0x00064b48 |
44 px | 50 | 1..49 |
Первая - основная для тайлов трассы: тайл шириной 100 пикселей растягивается на любую ширину строки без делений в кадре. Отсюда же характерный «пиксельный» вид дальних строк - шаг по источнику берётся из таблицы, а не считается с плавающей точкой.
Четвёртая таблица, вертикальная, живёт в jb_track.c: Gfx_BuildTexVStep()
заполняет 299 строк по 300 элементов (g_texVStep 0x00035238), строка h
содержит h значений - шаги по вертикали для строки высотой h.
Оригинал держал битмапы в ресурсах PE и брал их LoadBitmapW по числовому id.
Порт читает те же id как файлы: id 254 -> BITMAP/254.bmp (jb_assets.c), а
jb_bmp.c разбирает .bmp в jb_sprite с конвертацией в формат приёмника.
Корень ассетов - рядом с исполняемым файлом либо JUMPYBALL_ASSETS; на Android
чтение идёт через SDL_RWops прямо из APK.
Соответствие «id ресурса -> что это» полностью выписано в jb_consts.h, от
JB_RES_FONT (254) до JB_RES_BALL (331) - см. Levels и
Track-and-camera.
Их два, и они устроены по-разному (Font_Load 0x0001f4d8):
- размер 1:
BITMAP/254.bmp, границы глиф берутся сканированием строки 0 на парные маркеры0xff00ff; - размер 2:
BITMAP/332.bmp, границы - столбцы, чёрные на всю высоту листа.
Оба заполняют одни и те же таблицы ширин и X-смещений (g_glyphWidth 0x00027298,
g_glyphX 0x00027698). Меню зовёт размер 1 (Screen_Set 0x00013678), игра -
размер 2 (Player_Respawn 0x00012f18), поэтому HUD в игре крупнее.
Устройство порта
Сборка и происхождение