[Interest] [Development] Imageformats v2

André Somers andre at familiesomers.nl
Wed May 20 14:18:30 CEST 2015


Иван Комиссаров schreef op 20-5-2015 om 13:36:
>
>
> 2015-05-20 13:19 GMT+03:00 André Somers <andre at familiesomers.nl 
> <mailto:andre at familiesomers.nl>>:
>
>     Иван Комиссаров schreef op 20-5-2015 om 11:06:
>
>     Show different error messages for instance. Invalid data points to
>     a corrupted file. That is not the same as just not recognizing the
>     file format or having a network connection break in the middle of
>     reading.
>
>
> Different messages can be show using QString error() :) Anyway, i can 
> add enum later.
Problem is that you cannot show your _own_ message anymore. I might want 
to choose a different wording in a message that I'd like to show to my 
end user than something a Qt engineer choose. I also might like to offer 
different choices for different results: a Retry may make sense in case 
of a network error, but not in case of an unrecognized file type error...

>     Perhaps it should just read the header directly when opening from
>     file/iodevice and have an isValid() method or something like that?
>
>
> Ok, do we really need separately open/read imageformat? AFAIK, not 
> evry format has useful header. For example, Icns has Table of 
> Contents, but it can be missing. AFAIK, without ToC, there's no way to 
> know how many mipmaps file contains.
> In that case the pair of read/write (open/save) functions will be 
> enough. What do you think?
I think that it makes sense to attempt to load a header if it exists. If 
not... Not sure. Probably need to read the entire file in order to 
determine what it contains?

>
> Agreed. On the other side, making document bounded to a type means 
> that simple operation "open(typeA) -> save(typeB)" becomes more 
> complex: "document-reader -> storage -> document-writer". Storage 
> should be some user-defined class. And it's up to user to decide what 
> parts of a document he will drop. I was thinking about the document as 
> the storage, not about reader+writer.
You would not need a separate reader and writer. You would need to have 
an ImageDocument be specific about the type of image it represents. Your 
operation would, I think, look like this:

ImageDocument imageDoc("thefile.png"); //imageDoc gets type from file: png
qDebug() << imageDoc.mimeType(); //outputs "image/png"
imageDoc.convert("image/jpeg"); //convert in-place to jpg document
qDebug() << imageDoc.mimeType(); //outputs "image/jpeg"
imageDoc.saveAs("thefile.jpg");

or
ImageDocument imageDoc("thefile.png");
qDebug() << imageDoc.mimeType(); //outputs "image/png"
imageDoc.converted("image/jpeg").saveAs("thefile.jpg"); //convert 
in-place to jpg document
qDebug() << imageDoc.mimeType(); //outputs "image/png" as imageDoc 
itself did not change

No need to say goodbye to your ImageDocument design and create your own 
storage?
>
>     Thinking about this, I think I'd make the conversion explicit. If
>     you loaded a multipage TIFF and want to save as a normal PNG, you
>     could either have a void convert(QMimeType type) that converts the
>     current document to the new value (dropping unsupported features
>     in the process) and/or have a ImageDocument converted(QMimeType
>     type) function that creates a new ImageDocument in the requested
>     format based on the current document. Then, save _that_ document.
>
>
> That can be done silently by handler while saving. Why do you want to 
> make the convertion explicit? My idea was that we lose information 
> only while saving, but not while dealing with document.
I would not like it much if information is silently dropped. If you make 
the conversion explicit, it is also explicit what features are available 
for the type you have now. How else are you going to expose that 
information?
>
>
>     Perhaps you/the file format plugin could even offer different
>     options of how to deal with the degradation. Layers might be
>     flattened to a single image, meta data from a specific image
>     stored in the main meta data or vise versa, pages glued together
>     to form a single image... Even if the current structure doesn't
>     offer it, it would be nice if the API were such that such
>     fine-tuning would be possible.
>
>
> I think, those options should be hardcoded in the plugins.
It would be nice to (some day) expose API to insert custom conversions I 
think.

> While replying, i've got an idea. What if we change capabilities from 
> "format caps" to the "document caps" and allow to choose what formats 
> support provided caps. Say - you need to save an array of cubemabs. 
> This is provided by DDS and Valve Texture Format - VTF (for example). 
> Or you need to save 1D array of animated frames. Those are supported 
> by GIF, APNG, VTF. In case you want to save "array of cubemaps with 
> volume textures on each side" - ok, no format support that. In case 
> you force to save into format with "poor" capabilities - ok, format 
> drops what it doesn't support.
I think it is useful if it is possible to query what formats support 
what feature(s), or what other formats support the features the current 
document uses, but that is not a core feature I'd say.
>
>
>     With something like document.images().frame(10).mipmap(5)? That
>     would result in a list of 6 images for the requested frame and
>     mipmap.
>
>     If images() returns a QVector<QImage>, you could use the same
>     trick as used in QByteArrayList to add some special filtering
>     methods on it without actually subclassing QVector<QImage>.
>
>     frame() and mipmap() (and side() and page() and layer() and
>     whatever else one might come up with) could act as filtering
>     functions that again return a list of images that has these methods.
>
>
> Ok, it's better provide an example:
>
> Document doc("1.dds"); // has mipmaps, depth and "frames"
> doc.open(); doc.read(); doc.fooBar() // read some how
Why open explicitly?
> ImageMeta toFilter;
What do you need a ImageMeta object for?
> toFilter.setMipmap(1);
> toFilter.setFrame(0);
> toFilter.setSlice(5);
> auto resources = doc->filter(toFilter); // returns one image
What type would resources be then? QImage? Something else?
>
> toFilter.setMipmap(2);
> toFilter.setSlice(0);
> auto resources = doc->filter(toFilter); // returns all frames at depth 0
>
> toFilter.setMipmap(3);
> auto resources = doc->filter(toFilter); // returns all frames of all 
> depths
>
> So, i'm still forced to specify 2 coordinates to the filter function + 
> i have to create and pass a meta structure. Why is it better than 
> 2D-array?:)
That is an artefact of your choices, not inherent in how it could be. If 
I rewrite you example using my suggested API, you'd end up with 
something like this:

Document doc("1.dds"); // has mipmaps, depth and "frames"
QVector<QImage> resources; //lets be explicit what we return when 
discussing API...
resources = doc->images().slice(5).frame(0).mipmap(1); //in any order, 
resources is QVector<QImage>

resources = doc->images().slice(0).mipmap(2); // returns all frames at 
depth 0
resources = doc->images().mipmap(3); //returns all images of all depths


But also:
QImage firstPage = doc->images().page(0).first();
QImage secondPage = doc->images().page(1).first();

>     Well, I don't know how your Meta things work of course, so I
>     cannot comment on that. To me it makes little sense to set a meta
>     property that belongs to a specific image on a document directly,
>     but that may be my misunderstanding of your design.
>
>
> Icns is an *array* of images. Typically those are mipmaps, however 
> there can be elements with the same size. What is important is that 
> each element can have it's own subtype - 1st element is 1024*1024 jp2 
> compressed image (quality, exif can be embedded into it), 1nd element 
> is a 512*512 PNG image (some other option), last element is 1x1 RGBA 
> image (no options at all)
If you want to set certain meta data attributes on all images in the 
document in one go, then that still isn't the same as setting it on the 
document itself, right? I'd keep them separate for now. You can always 
add convenience API that does that later on. Start out with the minimum, 
built up from there in later releases.

>     I think it is actually simpler than forcing a 2D array onto image
>     document formats that don't benefit from it.
>
>
> What if i add another layer - Page/Frame: Document <>-- Page <>-- 
> Resource. Page can (or cannot) have mipmaps and/or preview image.
>
> if (doc->capabilities() & HasPages) {
>     for (int i = 0; i < doc->pageCount(); i++)
>         auto page = doc->page(i);
>         if (doc->capabilites() & hasPreview) {
>             auto preview = page.preview();
>         } else (hasMimpaps) {
>             for (mimpaps)
>                 auto resource = page.resource(mipmapIndex);
>        } else {
>             auto resource = page.resource(); // aka page.resouce(0);
>        }
> } else {
>      auto resource = doc->page().resource();
> }
>
> It's complicated, but a bit better than 2D array.
I don't think that helps. Now you are forcing a page paradigm on image 
documents that don't support *that*. The problem is that there are 
multiple orthagonal dimensions here (I think we have seen 'page', 
'frame', 'mipmap', 'side', and 'depth' so far) and that which dimensions 
really materialize depends on the image format. Imposing any of these in 
the hard-coded structure of the document will hinder all other formats 
interaction with it. If you tend to work with 3D stuff, you might want 
to get the 3D aspects like sides, mipmaps and depth more to the front of 
the API. If you want to show multi-page documents, you might think that 
pages really need to be there, and if you build an image viewer you just 
might need a quick way to query for something to use as a thumbnail 
fast. I think that if you want to stay agnostic to the application of 
ImageDocument, you should try to avoid pinning your API to any of these 
dimensions.

>
>>     How do i supposed to control that EACH mipmap have the same
>>     number of frames as document->frameCount(). I didn't find
>>     suitable solution.
>     Why would you need to control that? If an image format requires
>     it, then that image format should impose that limit (or even
>     auto-create the missing ones?) but I don't see why it should be a
>     general limitation. Perhaps there are formats where it is possible
>     that one page has a mipmap, while others don't? Just the first
>     page for instance to provide a quick preview?
>
>
> Sure, we can generate missing mipmaps. We can even "generate" missing 
> sides of a cubemap (black/opaque image of the required size) or slices 
> of the volume texture. But we can't generate missing frames. Suppose 
> you've added (frame0, mipmap0) and (frame2, mipmap0) images. Frame 3 
> is missing and there's no way to generate it. More, such API allows to 
> add frame and mipmaps that are out of bounds.
So?
The image plugin would return an error on writing I'd think? Or even 
isValid() would go to false? But it depends on the image format what is 
to be considered valid right? As those are pluggable, I don't think your 
generic API should restrict the options.

Andre

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/interest/attachments/20150520/2cae3219/attachment.html>


More information about the Interest mailing list