Войти
ФлеймФорумПрограммирование

[Design by Committee] Блиттер

Страницы: 1 2 38 9 Следующая »
#0
3:58, 16 сен 2016

Итак, понеслась!
Не уверен, что оно кому-то нужно, но как минимум будет упражнение на дизайн и реализацию API.
Вслух и с выражением.

Видение:
Кодится и оформляется софтварный блиттер.
В виде stb-style библиотеки. Одним файлом (заголовок; реализация инстанцируется #define'ом). Минимум зависимостей. Public domain.
Пишется на общем подмножестве C и C++ (его иногда называют "clean C").
Видимо только 32-битный RGBA.

Библиотека подразумевается низкоуровневой, соответственно предполагается, что верхний уровень если захочет - сам обернёт как ему удобнее. Следствие: нет торчащих наружу вспомогательных структур, API принимает встроенные типы C.

Минимально видится интерфейс, состоящий из ровно одной функции:

// Blits 32-bit RGBA image from scr to dst.
// (w,h,stride) describe image surface.
// NOTE: if src and dst overlap, the resulting image is unspecified.
// color if present modulates src (as in GL_MODULATE).
// color==NULL is equivalent to {255,255,255,255}, i. e. non-modulated blit.
// mode:
//   0 - copy
//   1 - alpha-blend
//   2 - gamma-corrected alpha-blend
void blit(
    int32_t src_w,int32_t src_h,int32_t src_stride,
    const uint8_t *src_pixels,
    int32_t dst_w,int32_t dst_h,int32_t dst_stride,
    uint8_t *dst_pixels,
    int32_t x,int32_t y,
    const uint8_t *color,
    uint32_t mode);

Подкрашивать (modulate) всё-таки хочется, как минимум ради текста. Ну и чтобы плавно уводить спрайт в полупрозрачность.

Препроцессинг спрайтов - видимо нет, т. к. это специфично игре/движку, и библиотека не знает деталей. Соответственно - блитаем как есть.

Вопросы:
1. Какая ещё функциональность нужна? Поворот, масштаб, аффинное преобразование - относятся к компетенции блиттера?
2. Детектирование CPU. Так чтобы работало на MSVC, clang, GCC. Интринсики vs. асм. Пример проблем с интринсиками и GCC: http://www.virtualdub.org/blog/pivot/entry.php?id=363 , https://gist.github.com/rygorous/f26f5f60284d9d9246f6 , https://github.com/nothings/stb/issues/280. Какие SIMD использовать (SSE2, SSE4.1, AVX2)? Где ещё кроме x86 делать специальные версии?
3. Точность vs. скорость. В соседней теме есть максимально точные версии функций. Замерить, сравнить, если менее точные функции не получится сделать существенно быстрее - так можно и забить. Иначе - видимо добавить соотв. mode.
4. Обычный и модулированный блит - разнести или пусть будет одной функцией (верхний уровень если захочет - разнесёт)?
5. color - может const float*? И разрешить значения больше 1.
6. RGBA vs. BGRA. Влияет только на интерпретацию color. #define, параметр функции, вообще забить (пусть верхний уровень сам интерпретирует)?
7. Что-то кроме 32-битного RGBA - надо?
8. Инициализация (детектирование CPU, заполнение таблиц) - явная/on-demand?
9. Один вызов на спрайт - это ok, или батчинг имеет смысл с т. з. производительности?
10. В гамма-корректном режиме альфа всё равно линейна? Обычно это вроде бы так.
11. В гамма-корректном режиме color интерпретировать как линейный или sRGB?

#1
8:42, 16 сен 2016

FordPerfect
> Минимально видится интерфейс, состоящий из ровно одной функции:
Предлагаю запилить функцию, где полагать, что src_stride == std_width и dst_dstride == dst_width. Оно часто таким бывает.

> Какая ещё функциональность нужна? Поворот, масштаб, аффинное преобразование -
> относятся к компетенции блиттера?
Целочисленное масштабирование не помешало бы.

> RGBA vs. BGRA. Влияет только на интерпретацию color. #define, параметр функции,
> вообще забить (пусть верхний уровень сам интерпретирует)?
Предлагаю забить на порядок. И чтобы можно было дефайнами задавать какой по номеру компонент является альфой.

> Что-то кроме 32-битного RGBA - надо?
8/16-битный grayscale неплохо было бы иметь.
8-битный палитровый можно (ТарасБ спасибо скажет).

> 10. В гамма-корректном режиме альфа всё равно линейна? Обычно это вроде бы так.
> 11. В гамма-корректном режиме color интерпретировать как линейный или sRGB?
Предлагаю забить на гамму вообще и интерпретировать всё как линейное. Гамма-коррекция ведь по факту нужна только при непосредственном выводе изображения на экран.

#2
9:28, 16 сен 2016

FordPerfect
> Препроцессинг спрайтов - видимо нет
Добавь интерфейс для препроцессинга, чтоб его можно было добавлять, не меняя твой блиттер. У Тараса даже что-то такое есть

#3
9:35, 16 сен 2016

Panzerschrek[CN]
> Предлагаю забить на гамму вообще и интерпретировать всё как линейное. Гамма-коррекция ведь по факту нужна только при непосредственном выводе изображения на экран.
А как толсто то. Автор ведь только ради этого блиттер и пишет (:

#4
16:13, 16 сен 2016

FordPerfect
> Поворот, масштаб, аффинное преобразование - относятся к компетенции блиттера?
Это уже не блиттер. Рисоватор с такими наворотами, по-моему, даже как-то по-особому называется.

> 9. Один вызов на спрайт - это ok, или батчинг имеет смысл с т. з.
> производительности?
Смотря какова реализация низкого уровня. Но, если закладываться на его возможность, то вот это вот всё:

int32_t src_w,int32_t src_h,int32_t src_stride,
    const uint8_t *src_pixels,
    int32_t dst_w,int32_t dst_h,int32_t dst_stride,
    uint8_t *dst_pixels,
    int32_t x,int32_t y,
    const uint8_t *color,
    uint32_t mode

есть смысл запихнуть в одну-единственную структуру и в функцию её передавать по указателю. Заодно будет гибкость с точки зрения кодонаписания (элементы в структуру можно добавлять-удалять-переставлять местами, и тем участкам кода, которые не задействуют изменившиеся параметры, почти ничего не будет, а если поменять состав параметров - придётся переписывать вообще все вызовы).

#5
17:16, 16 сен 2016

GEGL, не?

#6
20:45, 16 сен 2016

Panzerschrek[CN]
>Предлагаю запилить функцию, где полагать, что src_stride == std_width и dst_dstride == dst_width. Оно часто таким бывает.
Вот уж это верхнему уровню совсем тривиально обернуть.

>Целочисленное масштабирование не помешало бы.
Подразумевается GL_NEAREST (иначе ограничение на целочисленный масштаб не очень понятно)?
Можно сделать.

>И чтобы можно было дефайнами задавать какой по номеру компонент является альфой.
Вроде бы и внятная идея. Но оно хоть где-то востребовано? Бывает альфа в жизни не в [3]?

>8/16-битный grayscale неплохо было бы иметь.
>8-битный палитровый можно (ТарасБ спасибо скажет).
Очевидный вопрос - а как у них с альфой.
5551? GL_LUMINANCE8_ALPHA8? Colorkey? Забить, и делать copy-only?

>Предлагаю забить на гамму вообще и интерпретировать всё как линейное. Гамма-коррекция ведь по факту нужна только при непосредственном выводе изображения на экран.
Идея считать буфер линейным, а конвертировать только при выводе на экран кажется здравой, но потом мы вспоминаем, что:
1. Если (промежеточный) буфер по 8 бит на компоненту, то при конверсии и дискретизации будет меньше возможных цветов (183 значения, вместо 256).
2. Конверсии - дополнительная погрешность, особенно если у нас исходные спрайты художник нарисовал на линейные, а sRGB.
3. Цветовосприятие (зрение) тоже нелинейно, так что sRGB в каком-то смысле эффективнее использует пространство значений, чем линейное пространство.
Если промежуточный буфер - флоат, тогда ок, но здесь не тот случай.
Т. е. как минимум предоставить возможность работать в sRGB и гамма-блитать - считаю внятным.
Ну и вообще - зачем забивать на гамму, если она уже реализована?

laMer007
>Добавь интерфейс для препроцессинга, чтоб его можно было добавлять, не меняя твой блиттер. У Тараса даже что-то такое есть
Ты видимо чуть о другом препроцессинге.
Я о вещах вида "зарание составить список непустых пикселей спрайта и блитать их".
Или "начало линии/конец линии", как пропагандирует 122.

Как такое интегрировать в агностический движку блиттер?

Sbtrn. Devil
>есть смысл запихнуть в одну-единственную структуру и в функцию её передавать по указателю. Заодно будет гибкость с точки зрения кодонаписания (элементы в структуру можно добавлять-удалять-переставлять местами, и тем участкам кода, которые не задействуют изменившиеся параметры, почти ничего не будет, а если поменять состав параметров - придётся переписывать вообще все вызовы).
Тогда уж можно пойти дальше и передавать void* а функция его сама проинтерпретирует, как glDrawArrays.

eMan.Lived
>GEGL
Может и клёвый, но толстый (>1 файла), и LGPL.
Здесь чуть другой прицел.

#7
23:18, 16 сен 2016

И ещё:
нужна premultiplied alpha?

#8
3:07, 17 сен 2016

FordPerfect
> Тогда уж можно пойти дальше и передавать void* а функция его сама
> проинтерпретирует, как glDrawArrays.
Это уже полиморфизьм, а у нас тут вроде как более низкий уровень обсуждается.

#9
19:49, 17 сен 2016

FordPerfect
> нужна premultiplied alpha?
Я бы сказал, что не нужно ничего, кроме нее.
Еще бы я хранил гамма-скорректированную (1 – alpha) вместо просто alpha, но это уже для ценителей.

#10
23:37, 17 сен 2016

}:+()___ [Smile]
Я так понимаю - только или оба src и dst обычные (выкидывать этот режим всё-таки стрёмно) или оба premultiplied; смешанные нафиг не нужны?

>гамма-скорректированную (1 – alpha)
Как его дружить с гаммой - ещё думать.

#11
1:30, 18 сен 2016

FordPerfect
> смешанные нафиг не нужны?
dst — это фреймбуффер, а src — спрайт, так что на мой взгляд, возможны все комбинации прозрачности.
Или ты про не premultiplied?

> Как его дружить с гаммой - ещё думать.
Я исхожу из того, что гамма нужна для увеличения разрешения в темных областях.
Для блендинга наиболее плохим случаем будет почти непрозрачный черный спрайт поверх белого.
В этом случае результат — это коэффициент пропускания, т. е. 1 – aplha.
Соответственно, чтобы не терять точность, необходимо этот коэффициент хранить с гаммой.

#12
3:09, 18 сен 2016

}:+()___ [Smile]
>Или ты про не premultiplied?
Я про вот такой случай: есть например спрайт RGBA, который premultiplied, и dst который тоже RGBA, но не premultiplied. Такое - нафиг? Конкретно в случае dst=framebuffer возможно разница не заметна, т. к. dstA=255, но если мы, например, генерируем промежуточный спрайт (GUI пререндерим, мало ли чего), это влияет. Или обратный случай, совсем стрёмный: обычный RGBA спрайт на premultiplied RGBA dst.

>Соответственно, чтобы не терять точность, необходимо этот коэффициент хранить с гаммой.
Ну это же всё-равно хак?

#13
3:20, 18 сен 2016

}:+()___ [Smile]
А послать нафиг "обычный", а делать только premultiplied, как ты предлагаешь - мысль могущая вызвать непонимание в народных массах.

#14
20:26, 18 сен 2016

FordPerfect
> Я про вот такой случай: есть например спрайт RGBA, который premultiplied, и dst который тоже RGBA, но не premultiplied.
В не premultiplied фреймбуффер ты нифига не сблендишь, там формулы ужасные с делением.

> Ну это же всё-равно хак?
В смысле, хак? Вся гамма — это один большой хак, можно ведь работать с линейным uint16 или флоатами.
Просто, если быть последовательными, то гамму, помимо цветовых компонент, надо применять и к коэффициенту пропускания.
Альфа без гаммы — это для меня аналогично, например, зеленому с гаммой, а красному и синему — без.

> А послать нафиг "обычный", а делать только premultiplied, как ты предлагаешь - мысль могущая вызвать непонимание в народных массах.
Я бы сделал функцию подготовки спрайтов, которая переводит одно в другое, и на этом успокоился.

Страницы: 1 2 38 9 Следующая »
ФлеймФорумПрограммирование

Тема в архиве.