Russian Qt Forum

Программирование => С/C++ => Тема начата: Авварон от Ноябрь 30, 2016, 18:06



Название: Варианты вариантов
Отправлено: Авварон от Ноябрь 30, 2016, 18:06
Продолжаем играть в Igors:)

Пишу сейчас библиотечку, замену imageformat'ам.
Итак, что мы имеем. Есть 3 типа "картинок" - текстура (здесь и далее Image), кубическая текстура (здесь и далее CubeTexture), объемная текстура (здесь и далее VolumeTexture). Бывают массивы каждого типа (на самом деле, массива VolumeTexture я не встречал, но для простоты пусть будет). Бывают mipmaps - то есть массив однотипных текстур разного размера (каждая следующая в 2 раза меньше предыдущей).
Например:
png, jpeg - это просто Image.
gif - это массив Image (с доп атрибутами, например задержка между кадрами или кол-во циклов, пока опустим)
ico, icns - это мимпапы Image'ей
dds - в зависимости от флагов либо Image (один или массив), либо CubeTexture (одна или массив), либо VolumeTexture. Ну и для каждого элемента массива могут быть мипмапы. Итого, двумерный массив (index массива, level мипмапы) элементов одного из 3х типов.
Это вводная часть.

Вкратце:
У нас есть двумерный массив однотипных объектов.

Проблема: как его представить.
Вариант 1
Код:
using Resource = union {Image, CubeTexture, VolumeTexture } // обычный Variant из 3х типов
using ImageDocument: QVector<QVector<Resource>>;

// reading:
auto doc = readFromFile();
for (int i = 0; i < doc.imageCount(); ++i) {
    auto res = doc.resource(i, /*level*/ 0);
    switch (res.type) {
    ....
    }
}
// writing:
auto imageCount = 10;
Document doc(imageCount, Resource::Type::Image);
for (int i = 0; i < imageCount; ++i) {
    doc.setResource(i, 0, Resource(QImage(...))); // тту выдаем ошибку если пытаемся засунунть ресурс не того типа.
}
Этот вариант плох тем, что юзер может попробовать класть ресурсы разных типов. А может и разрешить? Вдруг появятся такие форматы?)

Вариант 2.
Выкидываем ресурс, засовываем внутрь документа:
Код:
using ImageDocument: QVector<QVector<{Image, CubeTexture, VolumeTexture}>>;

// reading:
auto doc = readFromFile();
if (doc.type() == Type::CubeTexture) {
    for (int i = 0; i < doc.imageCount(); ++i) {
        auto res = doc.resource(i, /*level*/ 0);
            auto texture = doc.cubeTexture(i, 0);
        }
    }
}

// запись то же самое, doc.setCubeTexture(i, 0);
Этот вариант те же минусы что и первый, но не имеет "лишнего" класса, зато в документе больше методов (6 вместо 2)

Вариант 3
Код:
template<class T>
using Mipmaps = QVector<T>; // тут, на самом деле, целый класс-обертка со своими методами (int mipmapCount(), T mimpap(int index), QSize size(int index))
using ImageMipmaps = Mimpaps<Image>;
using CubeTextureMipmap = Mimpaps<CubeTexture>;
using VolumeTextureMipmap = Mimpaps<VolumeTexture>;
using ImageDocument = union { QImage, QVector<QImage>, ImageMipmaps, QVector<ImageMipmaps>, CubeTexture, QVector<CubeTexture>, ... } // Variant для 3*2*2 = 12 типов

// reading
auto doc = readFromFile();
if (doc.type() & Type::VolumeTexture) {
    if (doc.type() & Type::Mipmaps) {
        auto texture = doc.toVolumeTextureMipmaps().mipmap(0);
    } else {
        auto texture = doc.toVolumeTexture();
    }
}
Тут, вроде бы, накосячить сложно. Однако вот этот вариант на 12 типов меня смущает. Да и юзать это становится менее удобно, хотя всяких неявных конвертаций (массив -> его первый элемент, мимпапа -> её  первый элемент) можно добавить для удобства.

Сорян за простыни кода, надеюсь кто-нибудь осилит:)


Название: Re: Варианты вариантов
Отправлено: Old от Ноябрь 30, 2016, 21:01
По мне, так удобней, что бы документ хранил от одной до N картинок, а у картинки может быть от 1 до K фреймов (например).
Код
C++ (Qt)
QImage i1 = ...;
QImage i2 = ...;
 
Image img;
img.addFrame( i1 );
img.addFrame( i2 );
 
ImageDocument doc;
doc.addImage( img );
 
// ---------------------
 
ImageDocument doc = readFromFile();
for( auto img : doc )
{
   qDebug() << img.frames();
   QImage i = img[ 0 ];
}
 
 
 


Название: Re: Варианты вариантов
Отправлено: Авварон от Ноябрь 30, 2016, 22:32
Идея в целом понятна, но она не отвечает на вопрос, на какой уровень всунуть вариант.
Типа такого?
Код:
QImage i1 = ...;
CubeTexture i2 = ...;
 
Image img;
img.addFrame( i1 );
img.addFrame( i2 );
...


Название: Re: Варианты вариантов
Отправлено: Old от Ноябрь 30, 2016, 22:58
А можно чуть подробней, что такое кубическая и объемная текстура.


Название: Re: Варианты вариантов
Отправлено: Авварон от Ноябрь 30, 2016, 23:11
Конечно. Объемная текстура - это как QImage только с 3мя измерениями (width, heigth, depth) вместо 2х (width, height). В файле обычно хранится в виде массива (опять!) двумерных текстур одного размера.
Код:
class VolumeTexture
{
public:
    VolumeTexture();
    VolumeTexture(int width, int heigth, int depth = 1);

    QImage slice(int index); // returns QImage(width, height)
    void setSlice(int index, const QImage &image);
};

Кубическая текстура - это тоже "трехмерная" текстура, но с пустотой внутри, у неё есть только внешние грани. Представляется в виде 6 плоских изображений.
Код:
class CubeTexture
{
public:
    CubeTexture();
    CubeTexture(int size);

    enum Side {
        PositiveX = 0x1,
        NegativeX = 0x2,
        PositiveY = 0x3,
        NegativeY = 0x4,
        PositiveZ = 0x5,
        NegativeZ = 0x6,
    };

    QImage side(Side side); // returns QImage(size, size)
    void setSide(Side side, const QImage &image);
};

Для каждой из них в файле может быть набор мипмап, то есть текстур меньшего размера, чтобы не скейлить в рантайме.

Вот пример структуры самого сложного файла (отсюда (https://developer.valvesoftware.com/wiki/Valve_Texture_Format))
Цитировать
For Each Mipmap (Smallest to Largest)
    For Each Frame (First to Last)
        For Each Face (First to Last)
            For Each Z Slice (Min to Max; Varies with Mipmap)
                VTF High Resolution Image Data
На практике Face и Slice взаимоисключающие (если FacesCount > 1 то SlicesCount == 1 и наоборот)

То есть, обобщая (уровни Mipmaps и Frames могут быть наоборот вложены)
Код:
For Each Mipmap (Smallest to Largest)
    For Each Frame (First to Last)
        QImage OR CubeTexture OR VolumeTexture


Название: Re: Варианты вариантов
Отправлено: Igors от Декабрь 01, 2016, 08:44
Рисуем общую структуру которая может иметь все-все-все, напр
Код
C++ (Qt)
struct CImageDoc {
 int GetNumFrames( void ) const;    // { return impl->GetNumFrames(); }
 int GetNumMips( void ) const;
 CImageDoc * GetParent( void ); // может и это
 ...
 ...
// data
 CImpl * impl;
};
Когда приходит конкретный формат - не пытаемся его как-то уложить в варианты, пусть он будет такой класс как есть, но добавляем "обеспечение" для всех методов CImageDoc

[OFF]
Кубическая текстура - это тоже "трехмерная" текстура, но с пустотой внутри, у неё есть только внешние грани. Представляется в виде 6 плоских изображений.
Ну вряд ли такую текстуру можно назвать "трехмерной", это один из вариантов карты отражения или окружения [/OFF]


Название: Re: Варианты вариантов
Отправлено: Авварон от Декабрь 01, 2016, 12:10
Рисуем общую структуру которая может иметь все-все-все, напр
Код
C++ (Qt)
struct CImageDoc {
 int GetNumFrames( void ) const;    // { return impl->GetNumFrames(); }
 int GetNumMips( void ) const;
 CImageDoc * GetParent( void ); // может и это
 ...
 ...
// data
 CImpl * impl;
};
Когда приходит конкретный формат - не пытаемся его как-то уложить в варианты, пусть он будет такой класс как есть, но добавляем "обеспечение" для всех методов CImageDoc
А вот это не очень понял. Куда добавляем? Через наследование от общей базы, (CImageDoc) что ли?

[OFF]
Кубическая текстура - это тоже "трехмерная" текстура, но с пустотой внутри, у неё есть только внешние грани. Представляется в виде 6 плоских изображений.
Ну вряд ли такую текстуру можно назвать "трехмерной", это один из вариантов карты отражения или окружения [/OFF]
Потому я и взял слово трехмерная в кавычки:)
Кстати хорошо, что вы заглянули, вы мне и ответите на вопрос - а бывают ли "паралелепипедные" кубические текстуры (с разной длиной граней?)


Название: Re: Варианты вариантов
Отправлено: Igors от Декабрь 01, 2016, 15:35
А вот это не очень понял. Куда добавляем? Через наследование от общей базы, (CImageDoc) что ли?
Нет, через связку CImpl. Создаете "нулевой" базовый CImpl который на все возвращает нули. Для каждого конкретного формата создаете наследника CImpl и делаете указатель на класс формата его членом, ну и через виртуалы CImpl делегируете ф-ционал формата в CImageDoc. 

а бывают ли "паралелепипедные" кубические текстуры (с разной длиной граней?)
Длину граней легко задать матрицей текстуры, а бывают ли кубики с 6 имеджами разного размера - нет, такого не встречал. В последнее время обычно все 6 сторон хранятся в 1 картинке (vert/horz cross). Ну и вообще "кубики" бывают разные (может быть просто 1 имедж без затей).

Что действительно хотелось бы иметь "ис каропки" - поддержку .exr и .hdr


Название: Re: Варианты вариантов
Отправлено: Авварон от Декабрь 01, 2016, 16:32
Нет, через связку CImpl. Создаете "нулевой" базовый CImpl который на все возвращает нули. Для каждого конкретного формата создаете наследника CImpl и делаете указатель на класс формата его членом, ну и через виртуалы CImpl делегируете ф-ционал формата в CImageDoc. 

А, ну мы получаем всё те же 12 CImpl - их не очень удобно использовать при создании файла.

Длину граней легко задать матрицей текстуры, а бывают ли кубики с 6 имеджами разного размера - нет, такого не встречал. В последнее время обычно все 6 сторон хранятся в 1 картинке (vert/horz cross). Ну и вообще "кубики" бывают разные (может быть просто 1 имедж без затей).

Вот поэтому я и хожу отдельный класс CubeTexture который бы позволял создавать его из/конвертиторовать его в проекции (вертикальный крест, горизонтальный и тп). Это уже написано, кстати.

Что действительно хотелось бы иметь "ис каропки" - поддержку .exr и .hdr

Погуглил, это же просто текстуры? Это можно сделать и на текущих имадж форматах. Если дадите примеров картиночек (вроде там вообще 1 вариант?), могу написать и законтрибутить (если с лицензией всё ок).


Название: Re: Варианты вариантов
Отправлено: Igors от Декабрь 01, 2016, 17:02
А, ну мы получаем всё те же 12 CImpl - их не очень удобно использовать при создании файла.
Пусть 12, но работаем с однообразным CImageDoc к которому привели слабо-связные классы, за счет этого многое схлопывается

Погуглил, это же просто текстуры? Это можно сделать и на текущих имадж форматах.
Та куда там, Qt не умеет их ни хранить, ни даже отображать (здесь это разные вещи). Это форматы float на канал, применимость - ну уж точно больше чем у "VolumeTexture"


Название: Re: Варианты вариантов
Отправлено: Авварон от Декабрь 01, 2016, 18:55
Та куда там, Qt не умеет их ни хранить, ни даже отображать (здесь это разные вещи). Это форматы float на канал, применимость - ну уж точно больше чем у "VolumeTexture"

Хотя да, это потребует перепиливания QImage. Сейчас это можно сделать только с потерей точности.


Название: Re: Варианты вариантов
Отправлено: Igors от Декабрь 02, 2016, 07:51
Хотя да, это потребует перепиливания QImage. Сейчас это можно сделать только с потерей точности.
Не думаю что "тролли" будут сильно возражать, скорее наоборот - этого явно не хватает, а юзается давно, хотя бы поиграться с exposure/logarithm впечатляет. Возможен и упрощенный вариант - имедж грузится как обычный QImage::ARGB_32 с потерей точности, НО по ходу дела создается буфер хранящий half или ergb, на практике вполне достаточно.


Название: Re: Варианты вариантов
Отправлено: Авварон от Декабрь 02, 2016, 11:28
Не думаю что "тролли" будут сильно возражать, скорее наоборот - этого явно не хватает, а юзается давно, хотя бы поиграться с exposure/logarithm впечатляет. Возможен и упрощенный вариант - имедж грузится как обычный QImage::ARGB_32 с потерей точности, НО по ходу дела создается буфер хранящий half или ergb, на практике вполне достаточно.

Можно посмотреть, если это не сломает бинарную совместимость, то можно добавить не дожидаясь Qt6.
В целом, не вижу проблем - добавить геттеров для QRgbF(float, float, float) да добавить энум.