Comments on 2d api for c++ N3888.pdf

730 views
Skip to first unread message

germa...@gmail.com

unread,
Jan 21, 2014, 10:01:02 PM1/21/14
to std-pr...@isocpp.org
Hello everyone.

I have been taking a look at http://isocpp.org/files/papers/N3888.pdf

Cairo is an object oriented API. No c++ standard library component except iostreams is object-oriented.

The current proposal proposes this:

    auto x1 = some_object();

    //Silently aliases x1 
    auto x2 = x1;


I think this approach won't be satisfactory, since it violates the usual semantics for c++ objects, at least in the standard library.

I see that random_access_iterators vs bidirectional operators provide operator +=.

Random_access_iterator -> operator+=
Bidirectional_iterator       -> std::advance  --- Named function since it is expensive.

I have more proposals on how to handle values for the library.

1. Disable copy with copy constructor, since it's expensive, 
Provide:
    copy() method for deep copy.

class Texture {
public:
    Texture & operator=(Texture const &) = delete;
    Texture(Texture const &) = delete;

    //Movable

    Texture copy() const;
private:
   native_object obj;
};

Advantages:

- Well known semantics.
- No need for synchronization. When wanting a reference, you use a true reference to the object or
a pointer, so you know you are aliasing.
- Avoids copy accidents.

Disadvantage:

- Not reference counted anymore means taking care of lifetime by yourself.


2. Disable copy with copy constructor, since it's expensive.
Provide:
    1.- copy() method for deep copy.
    2.- share() method for sharing the native object.

class Texture {
public:
    Texture & operator=(Texture const &) = delete;
    Texture(Texture const &) = delete;

    //Movable

    Texture copy() const;
    Texture share() const;
private:
    //Reference-counted object
    shared_ptr<native_object> obj;
};

Advantages:

- Well known semantics.

Disadvantage:

- You can still alias objects, though aliasing is more explicit since you need a call to share().

3.- Differentiate between mutable and const objects? (Just idea, didn't think too much about this yet)

In this case Const objects CANNOT point to mutable objects:

//Mutable
class Texture {
public:
  //Movable but NOT copyable.

  Texture copy() const;
  ConstTexture const_copy() const;

   //No share() method, make a const_copy first.
private:
  native_object obj;
};


//Immutable guaranteed
class ConstTexture {
 //Movable and copyable

   Texture muable_copy() const;
private:
  shared_ptr<native_object> obj;  
};

1.- Const objects are freely copyable.
2.- Non-const objects must be explicitely copied.

Here my 2 cents. :D 
I think we shouldn't allow implicit sharing, at least, since this is bad for multithreaded code in general.


Edward Catmur

unread,
Jan 23, 2014, 5:44:03 AM1/23/14
to std-pr...@isocpp.org, germa...@gmail.com
On Wednesday, 22 January 2014 03:01:02 UTC, germa...@gmail.com wrote:
I have been taking a look at http://isocpp.org/files/papers/N3888.pdf
The current proposal proposes this:

    auto x1 = some_object();
    //Silently aliases x1 
    auto x2 = x1;

I think this approach won't be satisfactory, since it violates the usual semantics for c++ objects, at least in the standard library.

I agree; the paper proposes an alternative reference semantics using the existing unique_ptr and shared_ptr.  This would allow:

    unique_ptr<surface> surf = make_surface(...);
    unique_ptr<context> ctx = make_context(*surf, ...);

Alternatively, make_surface() could be written surface::make() etc. The copy constructors and assignment operators would be deleted; constructors would be private.

Sharing would be via shared_ptr<>, which could be specialised to use the underlying C library's reference counting.

This would make the aliasing semantics explicit, and do so using standard vocabulary types (unique_ptr and shared_ptr).  Users (even beginners) can be expected to know and understand these types. The syntactic cost (over shared ownership semantics) is minimal: indirection for method calls. Most code would of course use auto instead of writing unique_ptr explicitly.

Sebastian Gesemann

unread,
Jan 23, 2014, 6:09:41 AM1/23/14
to std-pr...@isocpp.org
On Thu, Jan 23, 2014 at 11:44 AM, Edward Catmur <e...@catmur.co.uk> wrote:
> On Wednesday, 22 January 2014 03:01:02 UTC, germa...@gmail.com wrote:
>>
>> I have been taking a look at http://isocpp.org/files/papers/N3888.pdf
>> The current proposal proposes this:
>>
>> auto x1 = some_object();
>> //Silently aliases x1
>> auto x2 = x1;
>>
>> I think this approach won't be satisfactory, since it violates the usual
>> semantics for c++ objects, at least in the standard library.

Well, there is std::shared_ptr.

> I agree; the paper proposes an alternative reference semantics using the
> existing unique_ptr and shared_ptr. This would allow:
>
> unique_ptr<surface> surf = make_surface(...);
> unique_ptr<context> ctx = make_context(*surf, ...);
>
> Alternatively, make_surface() could be written surface::make() etc. The copy
> constructors and assignment operators would be deleted; constructors would
> be private.

The deleter type of a unique_ptr is not type-erased. This could be a
problem especially if other kinds of resources are involved (like GPU
resources).

Michael McLaughlin

unread,
Jan 25, 2014, 4:27:38 AM1/25/14
to std-pr...@isocpp.org
This is a post in two parts. The first is an announcement and the second is a first attempt at a reply to all the great feedback in this thread.
 
The reference implementation of N3888 is now posted to GitHub: https://github.com/mikebmcl/N3888_RefImpl . It currently requires Windows (tested on 7 and 8.1) with Visual Studio 2013 (the free Visual Studio 2013 Express for Windows Desktop works fine).
 
I'm hoping we will have a GNU/Linux X11-based (or maybe XCB-based) build using GCC sometime in the near future but I can't make any promises. (The code is mostly standard, portable C++ with only a handful of Windows-specific things, most of which relate to the entry point and creating a window. So it's almost entirely a matter of finding the time to create a project file system using CMake or Autotools. The past few days have been pretty quiet and thus productive but now with the reference implementation rolling out there could be a lot of feedback, questions, and concerns that need attention. So we'll see. It shouldn't be difficult to do a quick and dirty port if you want to try it out on a different platform).
 
Also, I can't promise there are no bugs. I've found and squashed several this week but there may be more lurking which will show up when I have a chance to really test the functionality.
 
Thanks!
 
(p.s. Apologies to those who read this in multiple places since I am copying and pasting the same release message because I'm exhausted and want to get to sleep!)
 
p.p.s. - Since I haven't had a chance to properly respond to this thread yet, I decided to take this opportunity (tiredness notwithstanding). I've tried to address the main points but this is all really good feedback so I need to re-read it when I'm fresh and consider it more carefully and thoroughly. So I offer my apologies in advance if in my response below I have missed anything or failed to properly and completely address something.
 
Hi! Thanks for all the great feedback so far. Sorry I'm so late replying to this thread. The decision to go with shared_ptr-style shared ownership semantics was difficult. My co-authors are likely to have different opinions on the topic so I can only speak for myself. To me, shared ownership semantics produces cleaner looking code than move-only semantics would. It's not an unknown type of semantics in the standard library, just uncommon (only shared_ptr and shared_mutex currently uses it).
 
To me the question is whether the committee is willing to more broadly accept shared ownership semantics and if so under what conditions. The types could all be prefixed with shared_ to keep with the naming convention of other shared ownership types. But the concerns may run deeper than just naming. I'm going to do my best to be able to provide a very good case for shared ownership semantics in Issaquah. I appreciate all the insightful comments as they are helping me to clarify my thinking and better understand the concerns that people will have on this issue.
 
There is also the issue of threading. It is something I need to give more thought to. Graphics programming is typically single threaded (or the graphics portion is anyway; ancillary operations can be and often are placed on separate threads). The reason for this is that to maintain smooth animations the program generally needs a frame rate of at least 30 frames per second. Because of how massively parallel modern GPUs are, stalling them in any way can be very expensive. So blocking a program's rendering thread is generally unacceptable unless it is unavoidable. Multithreading graphics using something such as a mutex for synchronization would generally be a bad idea for most non-trivial applications. D3D 11 and OpenGL both have paradigms for multithreading that generally avoid the need for blocking synchronization on the main rendering thread. Cairo is able to use a strategy that is similar to that used with OpenGL (in brief, each ancillary thread gets its own contexts with their own surfaces to which components of a scene are rendered; the main rendering thread concatenates the resulting components together while caching previously rendered data for reuse in the event that a thread is not ready with new data when the main thread requires it. In this way the main thread uses synchronization but does not block waiting on child threads). Nonetheless I will be spending additional time considering the implications of 17.6.5.9 [res.on.data.races] in relation to these types since exceptions cannot necessarily be made simply because of typical usage patterns and best practices.
 
Thank you for your time.
 
Respectfully yours,
 
Michael B. McLaughlin
 

--
 
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

Bengt Gustafsson

unread,
Jan 26, 2014, 3:38:36 PM1/26/14
to std-pr...@isocpp.org
Here are my comments. I understand that implementing these changes would in some cases imply that it can't be implemented on top of Cairo, but I don't think that could be a primary objective of a standardized API. I'm not knowledgeable in Cairo but in other drawing APIs and to some extent the underlying hardware. I think that it is more important to follow modern C++ paradigms is more important than simplicity of implementation. If we are not even taking away the void* context handling which is really arcane we can just as well use the original Cairo library, if you ask me. Also I think that this library is modeled too closely on Win32 with its 80ties style and today almost bizarre ideas of how to organize things. This is definitely not something we want to perpetuate.

Here are detailed comments based on reading the synopsis. As the paper does not go into detail I may have misunderstood some things, I apologize for this already here:

The format enum seems a bit limited, as it does not contain for instance 3x12 bit RGB, which will be more and more used. Would it be possible to do a struct again to get an open ended set of formats. This should also contain the
subpixel_order information.

font_slant and font_weight: For instance wxWidgets provides more weight values than normal and bold. Some even has a 0-100 percent weight and slant in degrees. But maybe the enums only provide specific values on such a gradual scale?

rectangle and rectangle_int should be replaced with a template, instances of which are used in the api. This template along with point<T> should be in the top level std namespace and used throughout std when appropriate. Over time this will increase interoperability between 3rd party libraries as they start using them.

Similarly matrix should be called something like transform_matrix and inherit a generic std::matrix<2,3>, adding the affine transform specific setup methods as indicated.

If the to me a little bizzare class recangle_list exists region should have a ctor from it.

Is user_data_key and its use in device/surface really necessary to have?

Is shared_ptr semantics logical on device, really? It seems to me that these are more in the vein of singletons (although there may be more than one). That is, you get a handle to it from somewhere central, which you then never actually copy.

The same goes for surface unless you can create subsurfaces as rects on a parent surface, in which case reference counting is appropriate. However, I think that the reference counting should only be used between such surface objects, not for all "handles" to any one particular subwindow. This would mean that there would be no copy constructors or assignment operators. The ctor taking rect components is the one used to create a subsurface (and doing the reference counting).

There should be a ctor of surface which takes a rectangle<double> to describe the subsurface extent, not only one with four discrete ints. The use of double in this API is strange to me, or needs to be complemented with a (more commonly used) API using int. The latter solution would be appropriate in the case that using double implies a coordinate transform taking place before the subsurface is created. The hard thing about this is that if rotation or shear is included in the transform the subwindow can hardly be created.

get_font_options should return a const ref to the internal font_options object. I suspect that this is really a property of the device, but maybe not given recent GPU developments.

It seems that the fallback resolution handling would be more appropriately placed in device.

mark_dirty_rectange shoud be available with a rectangle<int> as parameter. It seems inconsistent that the subsurface creation ctor takes a double rect and the dirty function takes a int rect.

Is dirty rect handling really a function in this API at all? It is tightly connected to the windowing system event handling, while nothing else in this API really is.

write_to_png seems as a strange function here, as it sets one file format out in front of all others. Of course a image file read/write manager could be added to "make file formats equal" but I would put this in the hands of 3rd party developers and instead concentrate on standardizing an in-memory image memory class (placed in std or possibly its own sub-namespace, but not in std::drawing at least).

The class image_surface seems to try to "be" this image memory, but only for the limited application of drawing in it. This is really a pity as we have lots of other application areas where image memories are important entities.

vector<char> is a very unsuitable way to specify image data as it can't be constructed without being inited to 0. There seems be great opposition against allowing vectors to default-construct their elements, I have argued for this many a time without any success. In the absense of a real image memory class a char* + count API is actually much more appropriate as this allowd image memories from other libraries to be used. A stride parameter which is allowed to be larger than the number of bytes acutally required per line MUST be included or the value of this functionality dwindles. This is true even with the vector based signatures.

The image_surface constructor taking a generator function doesn't need the closure parameter handling now that we have lambdas (and functors).

The image_surface constructor from a filename indicates that there might be an underlying image format reader system, which is not described. This seems not to be feasible at this time. Or it again implicitly refers to png files.

A color class seems called for. Such an abstraction is present in most all drawing libraries, except apparently, Cairo. Again this is one of those classes that are best placed directly in std, to be able to reuse them in future extensions working with colors in other ways.

The unification of different types of brushes into pattern class hierarchy seems promising. This should be unified with images, as all of them are ways of presenting pixel values (color data) at regular 2d positions, i.e. a raster. The raster_pattern type comes close to the image_memory I discussed above. Let's at least not have more than one type representing a in-memory stored x,y indexed raster of pixels!

In the 3D world rasters are regularly called textures. This is today not really a good name for the actual data even in this application as texture classes are also used for other things such as for instance bump maps. This actually goes beyond image data as such, but becomes a matrix type class if generalized far enough.

To get around this mess it seems logical to follow the path that C++ has taken in so many other areas, i.e. to templatize the methods and treat the parameter type (now a T) like a concept. Thus it is the concept of an "image" or "raster" that would be sdtandardized and then anything that complies to this concept can be drawn on or blitted onto something else. However, due to the plethora of storage formats for pixel data and the large sizes of images an efficient implementation of this is not easy to implement. I worked in a project which had implemented this and it was the module that had by far the longest compile times due to the large amount of template instantiations created. Note also that due to the dynamic nature of image file loading there must be ways to dispatch to these template functions at run time depending on the user's selection of file.

The raster_source_pattern has a lot of callbacks and void* closure data that needs to be cleared out.

I'm not particularly fond of the name 'context' in this case, as it is rather non-descriptive.

It is not so logical that the actual drawing methods are located in the context, which mainly is a container for stateful drawing parameters. While it is established practice (stemming I think from WIN32) to use a notion of drawing context, I don't even think it really is sound object oriented design. I would much prefer that the actual drawing commands were methods on surface, and that these existed both in overloads with a context and without one (instead taking relevant parameters such as color and dash pattern for line drawing and brush for filling operations). This sort of unites the ideas of stateful parameter storage and GDI+ style possibilities of giving all parameters to each command. Both styles are useful! One reason for having a drawing context object may be if it enhances thread safety (i.e. contexts are not thread safe per se byt serialize their (hidden) interactions with the underlying surface so that several worker threads can be given parts of the same surface to draw on without problems). I don't however think that this is the intention of this proposal, is it?

I would like to see a simple way of rendering an image onto the surface without the convoluted procedure of first creating a pattern around the image, then setting this as the "sourc _pattern" of a context and then drawing a filled rectangle(!) to actually get the image data drawn.

Text output from unicode strings must be as simple as for 8 bit strings. Proper codepage handling must also be included for the 8 bit case I presume.

Drawing commands need to exist in overloads with int coordinates. This is the most common source of availability of data for drawing I think.

Drawing commands should primarily work on Point data rather than discrete coordinate values, although overloads are a possibility. Mainly to reduce typing when calling but also to increase eficiency as the data is known to be placed together.

Polyline/polygon drawing should be included, using a range-of-pointers concept.

The paint() versus stroke() methods which I assume mean to fill and/or outline closed figures subsequently drawn makes the interface even more stateful than Win32, as you have both a possibility to set pattern and enable its use. On the other hand, compared to win32 and its followers, this gets rid of the need to set an "empty" brush to avoid filling figures, which is an advantage. The double calls to get a patterned figure is however error prone. Also, as I understand this, there is no shortcut to set a solid fill colour (you have to give the context a solid_colour_pattern to achieve the simplest of filled figures). Most other libraries has a shortcut for this called "setbackgroundcolour" or something like this.

As filled/stroked figures are so common I would contemplate encoding this in the method name even. FillRectangle() and StrokeRectangle() is very easy to understand... You loose some if you want to do both with the same rectangle, but in honest, how common is that?

The copy_page() and show_page() seem misplaced in a context type of object, but if drawing commands were on the surface object they would come more natural.

Nicola Gigante

unread,
Jan 26, 2014, 6:23:28 PM1/26/14
to std-pr...@isocpp.org

Il giorno 26/gen/2014, alle ore 21:38, Bengt Gustafsson <bengt.gu...@beamways.com> ha scritto:

> Here are my comments. <cut>

Hello everybody.

I’m new to this list and I acknowledge that my opinion might not have a big weight if at all, but I have my two cents.

I completely agree with Bengt regarding everything he said, but my point goes further. With modern C++
gaining relevance thanks to new language features and new common practices, why on earth should the committee
standardize an interface based on an old C library? I think that the idea of basing a standard rendering library
on Cairo (or GDI, or any library designed before C++11 or even before C++98) is flawed by principle.

Differently from other standard library facilities or subsystems, a 2D drawing API cannot be designed by
one or few men alone. To get it right, it needs enough field-testing, to be sure that:
1) the API is easy to use (in my opinion, the Qt drawing API is a reference point from this point of view),
2) it is flexible enough, given possible future uses and future hardware capabilities
3) it doesn’t impose artificial constraints, allowing the most efficient implementation across varying architectures and OSes.

Because of this, proposing an interface (even if a modern, well thought interface), without actually having implemented it and
made it used by actual users for enough time, is a bad idea.

Anyway, should I have to answer my list of bullets, here it is:
1) Please make simple things simple, hard things possible.
i.e. We all want the highest flexibility, but I don’t want to create or retrieve 8 objects to draw a filled circle.
2) Please handle fonts in a modern way and flexible. A 2D drawing API is useless nowadays without strong typographic support.
3) This points out the convenience of a text-layout facility.
4) An extensible reading/writing facility for image formats is a must, not an option
5) Don’t ignore platform differences. Giving access to native handles is not sufficient. Make it possible to have, for example,
a raster engine, an OpenGL engine, a D3D engine, etc…
6) For the software engine, writing the spec to have (or make it easy to have) pixel-to-pixel portability across implementations is a must.
7) Printing support is highly coupled with drawing, and it’s difficult to get the API right. You need a complete interface to set
all the possible printing options out there, but with a connection with the underlying platform because in GUI code you want to make the
user choose them with the native interface (this means importing the settings from the native type that represent them, not to handle the
8) Interoperability with the underlying platform is essential (again with a standard way to convert types that represents images, brushes,
colors, fonts etc… from the native type to the std:: ones).
9) Please give us powerful mathematical and geometrical primitives (a well designed and efficient std::matrix and two point<T> and
rectangle<T> classes are not enough at all)
10) With regards of the previous two points, it would be even better if everything is abstracted with a trait system like the boost geometric l
library (but possibly less complex)
11) shared_ptr is good, but why do the API have to expose the memory managed object at all? Let’s face the users with RAII types that
opaquely handles everything.
12) Please investigate on an FP-like approach to compose drawing operations.
i.e. we have stroke() and fill(), make me compose them to stroke and fill and reuse the composition, in a way that it run faster than execute two operations again.

Note that I’m not saying “Cairo doesn’t do any of these”, but even if the library itself have strong support for a lot of those points,
the interface is still fundamentally old...

Well I think I’ve written enough already..

Good evening to everyone.

Nicola



Matthew Woehlke

unread,
Jan 27, 2014, 11:43:28 AM1/27/14
to std-pr...@isocpp.org
On 2014-01-26 15:38, Bengt Gustafsson wrote:
> vector<char> is a very unsuitable way to specify image data as it can't be
> constructed without being inited to 0. There seems be great opposition
> against allowing vectors to default-construct their elements, I have argued
> for this many a time without any success. In the absense of a real image
> memory class a char* + count API is actually much more appropriate as this
> allowd image memories from other libraries to be used. A stride parameter
> which is allowed to be larger than the number of bytes acutally required
> per line MUST be included or the value of this functionality dwindles. This
> is true even with the vector based signatures.

As a generic image class, I would encourage you to look at
vil_image_view¹ from VXL² for possible inspiration. In particular, the
use of three stride parameters to allow for arbitrary component
ordering, including plane-packed and RGB/BGR (though it may be better to
separately specify the component ordering in order to allow both ARGB
and RGBA). Whether or not your drawing library is able to cope with such
arbitrary ordering without conversion, the image class itself becomes
more useful if it can be used as a transport for arbitrary packing
orders, as this can reduce the need to perform expensive repacking of
the pixel data.

I've also found it very useful to be able to construct an image from
both a memory address and a std::function that will free the memory, in
order to allow copy-free construction from some other image format. For
example:

vil_image_view* img = obtain_image();
return std::image(..., std::bind(my_vil_free, img));



http://public.kitware.com/vxl/doc/release/core/vil/html/classvil__image__view.html)
http://vxl.sourceforge.net/)


> Drawing commands should primarily work on Point data rather than discrete
> coordinate values, although overloads are a possibility. Mainly to reduce
> typing when calling but also to increase eficiency as the data is known to
> be placed together.

These days, is there any strong reason why we would not require users to
write:

draw({x, y}, ...); // note the {}'s to implicitly construct a std::point

?

It's only two more characters (to always take coordinates as a
std::point or whatever) versus taking coordinates as individual
parameters. That's not much cost to avoid having additional overloads of
practically every drawing function.


FWIW, I'd like to second Nicola's comment as far as at least considering
Qt as a reference API for this design.

--
Matthew

Matthew Woehlke

unread,
Jan 27, 2014, 11:44:23 AM1/27/14
to std-pr...@isocpp.org
On 2014-01-26 18:23, Nicola Gigante wrote:
> 9) Please give us powerful mathematical and geometrical primitives (a well designed and efficient std::matrix and two point<T> and
> rectangle<T> classes are not enough at all)

If that's truly needed in the standard library, I would suggest looking
at Eigen as a possible reference implementation.

--
Matthew

Felipe Magno de Almeida

unread,
Jan 27, 2014, 12:19:16 PM1/27/14
to std-pr...@isocpp.org
I don't know why we would want a object oriented drawing library. I
think it should be completely value-based and algorithms should be
templatized.
> --
>
> ---
> You received this message because you are subscribed to the Google Groups
> "ISO C++ Standard - Future Proposals" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to std-proposal...@isocpp.org.
> To post to this group, send email to std-pr...@isocpp.org.
> Visit this group at
> http://groups.google.com/a/isocpp.org/group/std-proposals/.



--
Felipe Magno de Almeida

Thiago Macieira

unread,
Jan 27, 2014, 12:34:57 PM1/27/14
to std-pr...@isocpp.org
On segunda-feira, 27 de janeiro de 2014 15:19:16, Felipe Magno de Almeida
wrote:
> I don't know why we would want a object oriented drawing library. I
> think it should be completely value-based and algorithms should be
> templatized.

You most definitely do not want to run the image-based algorithms like alpha
blending or the Porter-Duff composition modes in inline template code. You want
highly optimised, vector versions of them using the resources of the CPU and
GPU, and not inline.

You could call it a quality-of-implementation issue and allow the
implementation to use extern templates for the known and common image formats.
But I suggest that we *first* get the algorithms for what really matters
settled down, then we figure out how or if they should be made generic.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358

Felipe Magno de Almeida

unread,
Jan 27, 2014, 12:45:25 PM1/27/14
to std-pr...@isocpp.org
On Mon, Jan 27, 2014 at 3:34 PM, Thiago Macieira <thi...@macieira.org> wrote:
> On segunda-feira, 27 de janeiro de 2014 15:19:16, Felipe Magno de Almeida
> wrote:
>> I don't know why we would want a object oriented drawing library. I
>> think it should be completely value-based and algorithms should be
>> templatized.
>
> You most definitely do not want to run the image-based algorithms like alpha
> blending or the Porter-Duff composition modes in inline template code. You want
> highly optimised, vector versions of them using the resources of the CPU and
> GPU, and not inline.

This is easily doable in a templated library with specialization or
SFINAE (or better yet, concepts if already available). Can't see the
problem with
giving a generic programming API and I don't see how OO would help here.

> You could call it a quality-of-implementation issue and allow the
> implementation to use extern templates for the known and common image formats.
> But I suggest that we *first* get the algorithms for what really matters
> settled down, then we figure out how or if they should be made generic.

Isn't it what Cairo has already done? What C++ needs is a generic
drawing library, not another wrapper over cairo. IMO.

> --
> Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
> Software Architect - Intel Open Source Technology Center
> PGP/GPG: 0x6EF45358; fingerprint:
> E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358


Regards,

Thiago Macieira

unread,
Jan 27, 2014, 2:09:37 PM1/27/14
to std-pr...@isocpp.org
On segunda-feira, 27 de janeiro de 2014 15:45:25, Felipe Magno de Almeida
wrote:
> > You could call it a quality-of-implementation issue and allow the
> > implementation to use extern templates for the known and common image
> > formats. But I suggest that we *first* get the algorithms for what really
> > matters settled down, then we figure out how or if they should be made
> > generic.
> Isn't it what Cairo has already done? What C++ needs is a generic
> drawing library, not another wrapper over cairo. IMO.

I'm arguing that we need an efficient C++ library more than we need a generic
one. So I'm arguing to *first* get what matters, and then generalise.

Just look at the bikeshed discussions in the SG7 mailing list over what type
we should use for "pixel" and "point".

Felipe Magno de Almeida

unread,
Jan 27, 2014, 3:06:49 PM1/27/14
to std-pr...@isocpp.org
On Mon, Jan 27, 2014 at 5:09 PM, Thiago Macieira <thi...@macieira.org> wrote:
> On segunda-feira, 27 de janeiro de 2014 15:45:25, Felipe Magno de Almeida
> wrote:
>> > You could call it a quality-of-implementation issue and allow the
>> > implementation to use extern templates for the known and common image
>> > formats. But I suggest that we *first* get the algorithms for what really
>> > matters settled down, then we figure out how or if they should be made
>> > generic.
>> Isn't it what Cairo has already done? What C++ needs is a generic
>> drawing library, not another wrapper over cairo. IMO.
>
> I'm arguing that we need an efficient C++ library more than we need a generic
> one. So I'm arguing to *first* get what matters, and then generalise.

If what you mean by first is that it is included to the C++ standard
library, then
I would be against. Not that I can vote :). If you meant someone writing a new
C++ library, then I'm always for it. The more the merrier, of course.

> Just look at the bikeshed discussions in the SG7 mailing list over what type
> we should use for "pixel" and "point".
>
> --
> Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
> Software Architect - Intel Open Source Technology Center
> PGP/GPG: 0x6EF45358; fingerprint:
> E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358


Thiago Macieira

unread,
Jan 27, 2014, 7:01:01 PM1/27/14
to std-pr...@isocpp.org
On segunda-feira, 27 de janeiro de 2014 18:06:49, Felipe Magno de Almeida
wrote:
> > I'm arguing that we need an efficient C++ library more than we need a
> > generic one. So I'm arguing to *first* get what matters, and then
> > generalise.
> If what you mean by first is that it is included to the C++ standard
> library, then
> I would be against. Not that I can vote :). If you meant someone writing a
> new C++ library, then I'm always for it. The more the merrier, of course.

That's partially what I meant. In my opinion, having a library that cannot
possibly do ARGB32 efficiently is a disservice to C++ developers, even if it can
also do ARGB48, ARGB64, RGB565, YUV420, HSV, and other crazy pixel formats.

I meant that we first need to figure out the API, we need to get started
somewhere. Let's figure out what we need, how it would work, and how it can be
implemented efficiently. Without a reference implementation that passes basic
quality check, we'll get nowhere. This is why the current proposal is based on
Cairo: it implements today's technology efficiently, even if the API is not very
C++-like yet.

After we get that, we can study generalisations without compromising the
quality. Once that is done, it can be proposed to the standard.

We're still trying to get step 1 done.

Michael McLaughlin

unread,
Jan 27, 2014, 7:16:14 PM1/27/14
to std-pr...@isocpp.org
I'm in the process of providing more detailed answers because the I appreciate the earlier feedback and feel that it is helpful. As such I think each post deserve a detailed response.
 
But I just wanted to pause to say that I think that Thiago's post is a excellent summary of things as they stand going in to Issaquah.
 
Elsewhere, in a humorous context, I summarized it as follows: If N3888 were incorporated into the Standard as-is, I would feel compelled to request that the Issaquah Police Department investigate the possibility that we had all been unknowingly exposed to some substance with psychotropic properties. :)
 
-Mike
 

maltes...@gmail.com

unread,
Jan 27, 2014, 10:50:06 PM1/27/14
to std-pr...@isocpp.org
I think this is a great thing and thank you for doing this. Having a standard 2D graphics library would be great.

That being said I disagree with some of the comments so far about what the purpose of this library should be. In my opinion the main point of this library should be to provide simple building blocks and to make it easy to extend the library. Just like you can add any type to a std::unordered_map by implementing std::hash, and like you can print any type with an iostream by overloading operator<<, you should be able to add any type to the 2D graphics library. If I make a "piechart" class that can draw a std::map<std::string, float>, it should be easy for you to add that class to your application. One way to do that would be to have a common base type like Qt's QWidget. No matter what your code looks like, if you give me a QWdiget I can add it to my layout. (but ideally I would like a solution that does not rely on inheritance, just like std::hash does not rely on inheritance)

I don't think that a standard 2D graphics library has to be the most efficient graphics library around. I believe that attempting that is doomed to fail. Even if it turns out to be very fast now, the standards committee will be playing catch-up with other libraries forever. (and even if it is the fastest, the people who care about speed (game developers) are going to implement their own library anyway) Instead it should be recognized that if you write a library for the standard, you have got a whole different set of opportunities. The main one being that you could define the interface that most 2D drawing will follow in C++. My ideal standard 2D library would be one about which I could say "if you implement your drawing in this, it won't be the fastest, but it will be fast enough in almost all cases, and Qt has a compatibility layer that will make your code work in all Qt applications. And so does GTK+. And so does SDL. So unless performance is super important to you, just use this." (that being said performance should not be ignored and I would much prefer abstractions which are almost free like those used in <random> as opposed to those used in iostreams)

Cheers,

Malte

Thiago Macieira

unread,
Jan 28, 2014, 12:02:35 AM1/28/14
to std-pr...@isocpp.org
On segunda-feira, 27 de janeiro de 2014 19:50:06, maltes...@gmail.com
wrote:
> I don't think that a standard 2D graphics library has to be the most
> efficient graphics library around.

I don't think it has to be. But it has to have reasonable performance for
regular things like ARGB32 and opaque composition modes.
signature.asc

Michael McLaughlin

unread,
Jan 28, 2014, 1:35:05 AM1/28/14
to std-pr...@isocpp.org
Here are my comments. I understand that implementing these changes would in some cases imply that it can't be implemented on top of Cairo, but I don't think that could be a primary objective of a standardized API. I'm not knowledgeable in Cairo but in other drawing APIs and to some extent the underlying hardware. I think that it is more important to follow modern C++ paradigms is more important than simplicity of implementation. If we are not even taking away the void* context handling which is really arcane we can just as well use the original Cairo library, if you ask me. Also I think that this library is modeled too closely on Win32 with its 80ties style and today almost bizarre ideas of how to organize things. This is definitely not something we want to perpetuate.

 
First, thank you for the detailed, insightful feedback! I appreciate it very much.
 
Interestingly enough, while cairo does bear some resemblance to the Win32/GDI drawing model, its origins are in X11. The library was originally named Xr and was meant to be a significant improvement over raw Xlib programming. I believe that the name changed when the library was changed to be cross-platform (with English pronunciations of the Greek letter names (Chi Rho) used as basis the current name).
 
I very much agree that we want a clean, modern C++ library at the end of the day. N3888 is offered as a possible starting point for reaching that goal. In hindsight I should've made that much clearer in the paper. The hope for N3888 is that it, or a revision to it, will be accepted by the SG as a starting point, with amendments and modifications made to move it away from the C-like style that it still retains to a clean, modern design that would ultimately become a TS. More on that below as I address your points individually.
 

Here are detailed comments based on reading the synopsis. As the paper does not go into detail I may have misunderstood some things, I apologize for this already here:

The format enum seems a bit limited, as it does not contain for instance 3x12 bit RGB, which will be more and more used. Would it be possible to do a struct again to get an open ended set of formats. This should also contain the
subpixel_order information.

 
Right now the semantics of the library are as defined in the cairo API documentation: http://cairographics.org/manual/ . The subpixel order details are described there and are defined in a way that they are reliant on the machine endianness.
 
We've decided to handle proposed changes by making them issues tagged with "enhancement" in the GitHub repository for the reference implementation: https://github.com/mikebmcl/N3888_RefImpl/issues?state=open . I had already added an issue suggesting that subpixel order be changed to adopt a specific byte order, regardless of endianness, for places where the user can retrieve or supply a vector of image data as unsigned chars. I'm not sure how we should handle it if we ever add a function to give the user a raw pointer to the data itself; in that case I suspect that we would just have to trust that the user knows what he or she is doing.
 
I've added the suggestions to switch format to a struct with subpixel order info included and to add additional formats as issues to the GitHub repo. For the format struct, a brief code snippet showing how you see it working would be helpful to me (and maybe others) in thinking about it. If you find a moment to do that, please feel free to add it directly to the issue: https://github.com/mikebmcl/N3888_RefImpl/issues/12 . If you prefer just to post it here instead then I will take care of adding it there.
 
I generally agree about adding more formats. I added some thoughts I have on practical implications we'll need to resolve if we do that to the GitHub issue for that. (The gist of them being that since not all GPUs support all formats, we'll need to decide how to handle unsupported formats, and that it'd also be good to consider what, if any, mandatory formats there should be).
 
Rather than me saying it each time, you can assume that I added an issue for each point I responded to unless I explicitly say that I didn't for some reason.
 
 
font_slant and font_weight: For instance wxWidgets provides more weight values than normal and bold. Some even has a 0-100 percent weight and slant in degrees. But maybe the enums only provide specific values on such a gradual scale?
 
I'm strongly in favor of keeping the API (especially the text parts) simple for now. I think font_slant should stay as is. I'm open to adding more weights but not many and I think that should be deferred until later in the process of getting to a TS. Later in the design process, we might also consider adding a UDL to provide finer-grained control over the weight on systems that support it. But I need to think about that more before I'd be able to vote for or against it. As much as is reasonable, I want to avoid appearing to offer options that in reality would only really work on some platforms.
 
I think that in general we can go a bit above the least common denominator (especially where there's a reasonable fallback for platforms that don't support a particular feature). But I'd rather be in the position of receiving requests for more features than complaints that implementations aren't delivering what the interface appears, at a casual glance, to offer.
 
My view here is to defer consideration of this until later in the process.
 


rectangle and rectangle_int should be replaced with a template, instances of which are used in the api. This template along with point<T> should be in the top level std namespace and used throughout std when appropriate. Over time this will increase interoperability between 3rd party libraries as they start using them.

 
I definitely agree as far as creating a point type. I need to think more about the merits of templating point and rectangle; it seems obviously good but I'm unsure if it might not haunt us someday (not doing it might also haunt us; this is something I hope more people comment on either here or in Issaquah). I'm not sure about trying to hoist such types into std right now. I think any decision to move these types out of drawing would be the purview of LWG/LEWG.
 
So I'm adding a suggestion to create these class template types but I'm not adding in a suggestion to move them to std. That'd require a separate proposal. I would expect it to have a detailed analysis of why they should be added to std, what effects adding them directly to std can and should have, and how and whether those types should interact with other Standard Library functionality (at a minimum). I also think it should be presented directly to LEWG, absent some instruction from them to re-route it elsewhere. If someone writes such a proposal and it is adopted, then we can always adopt those types in this library so long as the TS hasn't yet been published (even if it has, we could still probably work something out, though it'd be harder).
 

Similarly matrix should be called something like transform_matrix and inherit a generic std::matrix<2,3>, adding the affine transform specific setup methods as indicated.
 
I think this is a good idea, but I'm not adding it as a suggestion because I think the creation a matrix type falls under SG6 and so should be presented via a proposal to them (assuming they aren't already considering it). If SG6 is working on or decides to work on a matrix type, we should coordinate with them to follow its development and make use of it. I did add in a "question" issue as a reminder to check with SG6 about this. If they don't take it up, I don't see any reason for us to develop a generic matrix type when all we need is a 2x3 matrix for affine transformations.
 
Regardless, until we check with SG6 and hear back from them, I don't think there's anything for us to discuss just yet. But the matrix type we have now needs work so if they don't take it up then I think we should consider changing the name (in case they decide to take it up in the future) and should clean up the type (including adding appropriate operator overloads, etc.). Someone else suggested that already and I still need to go back and add that and other suggestions that were made before the reference implementation was published in to the issue tracker.
 
Incidentally, the standard suggests in a footnote that a hypothetical matrix class could be built up using the valarray class template. We might want to consider doing that here if we are left to our own devices in terms of the matrix type.
 


If the to me a little bizzare class recangle_list exists region should have a ctor from it.

 
Added as a suggestion. And I agree that it's a bizarre type. I think it should probably be replaced with a vector<rectangle> and will be surprised if it isn't. I added that as a suggestion too.
 

Is user_data_key and its use in device/surface really necessary to have?

 
As far as I'm concerned, user_data_key (and the functionality it exists to provide) should be obliterated. It's already a suggestion. But it's nice to know that others also question its usefulness.
 

Is shared_ptr semantics logical on device, really? It seems to me that these are more in the vein of singletons (although there may be more than one). That is, you get a handle to it from somewhere central, which you then never actually copy.

 
I honestly don't know if there's a legitimate reason to have the device type at all. I added a note to investigate it for possible elimination. During the discussion of that, if we decide it has a purpose we would then go on to decide its semantics. It's there because it came along as part of the mechanical transformation.
 
 
The same goes for surface unless you can create subsurfaces as rects on a parent surface, in which case reference counting is appropriate. However, I think that the reference counting should only be used between such surface objects, not for all "handles" to any one particular subwindow. This would mean that there would be no copy constructors or assignment operators. The ctor taking rect components is the one used to create a subsurface (and doing the reference counting).

 
You can create subsurfaces from parent surfaces. Further, you can create an image_surface, make it the target of contextA, draw on it with contextA, and then draw it to the target surface of contextB by creating a surface_pattern from it or by calling contextB::set_source_surface [which creates an ad hoc surface_pattern and sets it as the source pattern on the context]. In other words, you can use image_surface objects as render targets (and this in turn is a key part of the pattern that allows you to implement multi-threaded graphics without stalling the thread that renders to your output device). If you'll be in Issaquah, part of the presentation I'll be giving there is going to cover in depth the reason why I think that shared ownership semantics are the right way to go for these types. I'll also share my slides for the benefit of everyone who can't be there. The rough-draft soundbite version is that shared ownership semantics give you clean, modern-looking C++ without having everything wrapped in a shared_ptr (ugly and a non-trivial possibility of misuse, especially by newcomers to C++) while respecting the fact that deep copies of GPU resources will decimate performance and increase memory usage without any real benefit to the end user (even if you wanted a copy of a texture, the best way to create it is to draw it as a sprite to a render target).
 
That said, shared ownership semantics are not a final decision. I think they are justified here and will be making a formal case for it, but the decision will ultimately rest in the hands of LEWG and SG13. We may well end up with move-only semantics for GPU objects. I very much doubt we would decide on value semantics due to the expense of copying GPU resources. I added an issue for this for the sake of completeness.
 

There should be a ctor of surface which takes a rectangle<double> to describe the subsurface extent, not only one with four discrete ints. The use of double in this API is strange to me, or needs to be complemented with a (more commonly used) API using int. The latter solution would be appropriate in the case that using double implies a coordinate transform taking place before the subsurface is created. The hard thing about this is that if rotation or shear is included in the transform the subwindow can hardly be created.

 
Oddly enough, cairo states that the semantics for the function that the ctor derives from are only defined if you use whole units. Non whole units have not been finalized as of yet. Given that, I don't see any reason to keep this as a double (what it is currently). That said, I'm not adding it as an issue simply because when the issue of what to do about the proposed point type and the existing rectangle and rectangle_int types is resolved, what will follow will be rules transforming these sorts of functions into functions that take the appropriate resulting type. That was one of the transforms we intended to include (it's noted as a comment at the end of N3825) but that I did not get around to including in N3888 (in part because I figured that people would want to discuss how we should define types such as point, rectangle, etc.). So rather than create types where none existed, I stuck with cairo's types and transformed them. Since cairo has no point type, N3888 has no point type. But there will be one and when there is there will be a new transformation rule added to section 2 of the rules that will cover it.
 
 
get_font_options should return a const ref to the internal font_options object. I suspect that this is really a property of the device, but maybe not given recent GPU developments.

 
This isn't the only place where this "out" parameter pattern occurs. It definitely needs to be eliminated wherever possible. I just did not want to do it on the first pass since the transform rules are already quite complex and my hope is that by having kept them as simple as possible, people will spot any bugs or problems with them (as indeed people have; the rules call for virtual dtors for base classes but it was a late rule and I did not get around to changing it in the reference implementation until after publication and so I also forgot to fix it in the Technical Specification section). But it's in the queue now so that when the time is right it will be dealt with.
 

It seems that the fallback resolution handling would be more appropriately placed in device.

mark_dirty_rectange shoud be available with a rectangle<int> as parameter. It seems inconsistent that the subsurface creation ctor takes a double rect and the dirty function takes a int rect.

Is dirty rect handling really a function in this API at all? It is tightly connected to the windowing system event handling, while nothing else in this API really is.

 
The fallback resolution functionality has to do with non-raster output surfaces (e.g. printers, PDFs, SVGs). I don't know of any good reason to exclude these as potential surfaces that an implementation could choose to let a user draw to just now so I see no reason to consider removing that functionality at this point. As such I'm not adding an issue for it. There will be later passes through the API to remove functionality that wound up becoming unused or unnecessary. The only things I'm willing to put up for removal now are things that are unambiguously unnecessary (e.g. the user_data_key stuff). The dirty rect handling functionality is how cairo is informed that something else was drawing to the surface. While we aren't standardizing cairo, having these function calls available to implementers could facilitate interop where they choose to provide useful native handles to enable that. So it may well have uses. If not, we'll eliminate it in a later pass.
 

write_to_png seems as a strange function here, as it sets one file format out in front of all others. Of course a image file read/write manager could be added to "make file formats equal" but I would put this in the hands of 3rd party developers and instead concentrate on standardizing an in-memory image memory class (placed in std or possibly its own sub-namespace, but not in std::drawing at least).

The class image_surface seems to try to "be" this image memory, but only for the limited application of drawing in it. This is really a pity as we have lots of other application areas where image memories are important entities.

vector<char> is a very unsuitable way to specify image data as it can't be constructed without being inited to 0. There seems be great opposition against allowing vectors to default-construct their elements, I have argued for this many a time without any success. In the absense of a real image memory class a char* + count API is actually much more appropriate as this allowd image memories from other libraries to be used. A stride parameter which is allowed to be larger than the number of bytes acutally required per line MUST be included or the value of this functionality dwindles. This is true even with the vector based signatures. 

The image_surface constructor taking a generator function doesn't need the closure parameter handling now that we have lambdas (and functors).

The image_surface constructor from a filename indicates that there might be an underlying image format reader system, which is not described. This seems not to be feasible at this time. Or it again implicitly refers to png files.
 
It comes from cairo's PNG functionality. I'm in favor of extending it to more image types rather than eliminating it.
 
You can call image_surface::get_data and write it out to whatever format you want. Or do whatever you want with the resulting data for that matter.
 
There's already an issue open to allow users direct access to pointers. I think having both is the best way to go since it leave the choice of safety versus performance in the hands of the user. Also, you can construct a vector without zero initializing it provided you have data to put in it already. The reference implementation does this in image_data::get_data. And if you don't have data to put in it right away, you can still call reserve on it. You're restricted in how you insert data when you use reserve rather than resize, but reserve will not zero initialize the memory. The library already has functionality that tells you the stride given a format and line width in pixels.
 
I added an issue to verify that the void* closure parameters are useless and then remove them from all of the functions that have them.
 
Yeah, it's PNG again (the filename ctor). I have an email from the cairo mailing list to do something about this but haven't had a chance to turn it into an issue yet. The suggestion there was to make it a static factory function (likely a member function) which has some appeal for me. I much prefer adding in support for more image types than removing the functionality regardless of how it is handled.

A color class seems called for. Such an abstraction is present in most all drawing libraries, except apparently, Cairo. Again this is one of those classes that are best placed directly in std, to be able to reuse them in future extensions working with colors in other ways.
 
Right now I see no reason for one. Further, the class has the potential to become a nightmare class of doom as soon as it starts to flirt with concrete representations of colors in pixel formats. If someone wants to draw up a proposal to add a color class, they can. If it's going to go directly in std, it should be directed to LEWG initially and only sent to SG13 if LEWG decides to do so.
 
Don't get me wrong, I like color classes (even if it's just an aggregate of const statics. How else am I supposed to know what values to use to get cornflower blue or any number of other named colors that I'm used to having available). And it may be that one will prove both useful and feasible here. But it's non-trivial and it's not something that must exist to evaluate N3888 as a starting point for a 2D drawing library. So I'm not adding it as an issue at this time. (Note: I'm not the only person who can add issues so if someone feels that it absolutely must exist, they can add it and include a very good reason why it must exist now because otherwise the chair may decide not to entertain it).
 


The unification of different types of brushes into pattern class hierarchy seems promising. This should be unified with images, as all of them are ways of presenting pixel values (color data) at regular 2d positions, i.e. a raster. The raster_pattern type comes close to the image_memory I discussed above. Let's at least not have more than one type representing a in-memory stored x,y indexed raster of pixels!
 
I'm not going to add an issue that will mess with cairo's basic type system right now. I think that's something to discuss after there's a starting point. Even then I would be against it. image_surface serves as a render target. A pattern does not and cannot. Cairo calls it a pattern, other libraries use terms like brush. The concept of pattern/brush being distinct from surface/texture is very much a part of the 2D libraries I'm familiar with. Just because all data is bytes doesn't mean we should abandon good conceptual frameworks to in order to generalize concepts.
 


In the 3D world rasters are regularly called textures. This is today not really a good name for the actual data even in this application as texture classes are also used for other things such as for instance bump maps. This actually goes beyond image data as such, but becomes a matrix type class if generalized far enough.

 
The term texture isn't used anywhere in the drawing header. I'm not quite sure what you're talking about here. Perhaps I'm misunderstanding you?
 
 
To get around this mess it seems logical to follow the path that C++ has taken in so many other areas, i.e. to templatize the methods and treat the parameter type (now a T) like a concept. Thus it is the concept of an "image" or "raster" that would be sdtandardized and then anything that complies to this concept can be drawn on or blitted onto something else. However, due to the plethora of storage formats for pixel data and the large sizes of images an efficient implementation of this is not easy to implement. I worked in a project which had implemented this and it was the module that had by far the longest compile times due to the large amount of template instantiations created. Note also that due to the dynamic nature of image file loading there must be ways to dispatch to these template functions at run time depending on the user's selection of file.

The raster_source_pattern has a lot of callbacks and void* closure data that needs to be cleared out.
 
Yeah, raster_source_pattern is complicated. The closures are wrapped up in the earlier issue re: closures. Most of the callbacks are used only in some non-typical cases; only acquire is mandatory. But I'm not yet ready to throw out the functionality. It does have uses (customizations when dealing with a printing surface, for example) so I'm not adding it as an issue at this time. We can remove it later if it still seems unnecessary then.
 


I'm not particularly fond of the name 'context' in this case, as it is rather non-descriptive.

 
Bike shed. Also D3D11, OpenGL, and Quartz 2D use context in the names of their objects that perform drawing operations to a surface. SDL and openFrameworks use it as a result of their ties with OpenGL. So I would not characterize it as non-descriptive. In fact, being a graphics::context object is the best, non-repetitive name for it in my opinion.
 
 
It is not so logical that the actual drawing methods are located in the context, which mainly is a container for stateful drawing parameters. While it is established practice (stemming I think from WIN32) to use a notion of drawing context, I don't even think it really is sound object oriented design. I would much prefer that the actual drawing commands were methods on surface, and that these existed both in overloads with a context and without one (instead taking relevant parameters such as color and dash pattern for line drawing and brush for filling operations). This sort of unites the ideas of stateful parameter storage and GDI+ style possibilities of giving all parameters to each command. Both styles are useful! One reason for having a drawing context object may be if it enhances thread safety (i.e. contexts are not thread safe per se byt serialize their (hidden) interactions with the underlying surface so that several worker threads can be given parts of the same surface to draw on without problems). I don't however think that this is the intention of this proposal, is it?

 
As explained at the beginning of this reply, cairo's origins are in X11 and Win32 wasn't even contemplated at the time. Regardless, drawing methods on a context are, as you point out, established practice, and the most recent major library to adopt this was D3D11 (D3D10 and earlier had drawing on done by the device itself). The context object is central to the proposal. I have some issues filed to consider moving some categories of functionality off of it. But the transform you are asking for requires a fundamentally new proposal.
 

I would like to see a simple way of rendering an image onto the surface without the convoluted procedure of first creating a pattern around the image, then setting this as the "sourc _pattern" of a context and then drawing a filled rectangle(!) to actually get the image data drawn.

 
Me too. This can easily be added later since it is already possible now. It's not central to evaluating N3888 as a starting point so I'm not filing an issue for it.
 

Text output from unicode strings must be as simple as for 8 bit strings. Proper codepage handling must also be included for the 8 bit case I presume.

 
The API as it stands deals in UTF-8 only. I'd be willing to entertain UTF-16 or UTF-32, but codepages must die. I have no interest in diving into the realm of locales and codepages for a modern library. Unless Bjarne himself asks for it or my coauthors demand it, it's not going into this proposal or anything based on it. Sorry.
 
 
Drawing commands need to exist in overloads with int coordinates. This is the most common source of availability of data for drawing I think.

Drawing commands should primarily work on Point data rather than discrete coordinate values, although overloads are a possibility. Mainly to reduce typing when calling but also to increase eficiency as the data is known to be placed together.

Polyline/polygon drawing should be included, using a range-of-pointers concept.

The paint() versus stroke() methods which I assume mean to fill and/or outline closed figures subsequently drawn makes the interface even more stateful than Win32, as you have both a possibility to set pattern and enable its use. On the other hand, compared to win32 and its followers, this gets rid of the need to set an "empty" brush to avoid filling figures, which is an advantage. The double calls to get a patterned figure is however error prone. Also, as I understand this, there is no shortcut to set a solid fill colour (you have to give the context a solid_colour_pattern to achieve the simplest of filled figures). Most other libraries has a shortcut for this called "setbackgroundcolour" or something like this.

As filled/stroked figures are so common I would contemplate encoding this in the method name even. FillRectangle() and StrokeRectangle() is very easy to understand... You loose some if you want to do both with the same rectangle, but in honest, how common is that?

The copy_page() and show_page() seem misplaced in a context type of object, but if drawing commands were on the surface object they would come more natural.

 
copy_page and show_page are related to the functionality that allows surfaces to be things like printers, PDFs, etc. It stays for now for the reasons previously described.
 
The need for some sort of clear function that clears the surface to a specified color is definitely something to add in the future. This and the other things aren't things I'm inclined to add to the (ever increasing) issues list since they can be done later once we've decided on whether N3888 or N3888 with modifications will be a suitable starting point for the library.
 
Thank you again for taking the time to make all these recommendations!
 
-Mike

Michael McLaughlin

unread,
Jan 28, 2014, 4:02:19 AM1/28/14
to std-pr...@isocpp.org
On Sun, Jan 26, 2014 at 6:23 PM, Nicola Gigante <nicola....@gmail.com> wrote:

Il giorno 26/gen/2014, alle ore 21:38, Bengt Gustafsson <bengt.gu...@beamways.com> ha scritto:

> Here are my comments. <cut>

Hello everybody.

I’m new to this list and I acknowledge that my opinion might not have a big weight if at all, but I have my two cents.

 
I try my best to judge opinions based on the ideas contained in them, not the person expressing them.
 

I completely agree with Bengt regarding everything he said, but my point goes further. With modern C++
gaining relevance thanks to new language features and new common practices, why on earth should the committee
standardize an interface based on an old C library? I think that the idea of basing a standard rendering library
on Cairo (or GDI, or any library designed before C++11 or even before C++98) is flawed by principle.

 
Cairo wasn't designed before C++98. It was started sometime in the 2002-2003 time frame as best I can tell. N3888 does not contemplate GDI.
 
I know of no major graphics library that was designed after C++11. 
 

Differently from other standard library facilities or subsystems, a 2D drawing API cannot be designed by
one or few men alone. To get it right, it needs enough field-testing, to be sure that:
1) the API is easy to use (in my opinion, the Qt drawing API is a reference point from this point of view),
2) it is flexible enough, given possible future uses and future hardware capabilities
3) it doesn’t impose artificial constraints, allowing the most efficient implementation across varying architectures and OSes.

Because of this, proposing an interface (even if a modern, well thought interface), without actually having implemented it and
made it used by actual users for enough time, is a bad idea.

 
You may wish to read N3791: http://isocpp.org/files/papers/n3791.html . Its author is Beman Dawes, one of the founders of Boost and someone who has been active in standardization for many years. In designing N3888 we took his advice from N3791 to heart. It makes sense and it comes from someone who has not only seen many things become standards but who has also seen many things fail to become standards.
 
I have implemented this interface. The reference implementation is available here: https://github.com/mikebmcl/N3888_RefImpl . I freely admit that the API as it stands now is awkward. The point of N3888 is not to be the standard but to propose a starting point that provides all the core functionality people need from a 2D drawing library in a form that is malleable enough to shape it into a clean, modern C++ API in the style of the C++ Standard Library.
 
The reason for using a mechanical transformation of cairo as a starting point is to address your second and third points above. Your first point is one of the primary things that SG13 will be working on once we have agreed upon a starting point.
 
As for Qt, I invite you to consider the number of dependencies that Qt has on Qt. I considered and consciously rejected deriving an API from QPainter. Not because it is bad; I think it is quite good. I rejected it because I believed that trying to carve out the 2D drawing part of Qt without needing to standardize most of the rest of Qt (or butcher the API to the point where we'd be losing most of the benefits that Qt provides) would be extremely difficult. To me, Qt is designed to be used with Qt. Take a look at the QPainter API: http://qt-project.org/doc/qt-5/qpainter.html . Note the QVector<T> and the QString and QImage and all the other Qt types. It's a lot of baggage. Cairo, by contrast, is a graphics library. Period.
 
If you want to write a proposal based on QPainter or some other aspect of Qt you can. Things only get standardized when someone decides to write a proposal and put in the work of explaining it, defending it, accepting valid criticisms of it, changing it, and so on until such time as it is at last accepted as a standard. And there is no guarantee that it ever will be accepted.
 

Anyway, should I have to answer my list of bullets, here it is:
1) Please make simple things simple, hard things possible.
  i.e. We all want the highest flexibility, but I don’t want to create or retrieve 8 objects to draw a filled circle.
 
We will be working on that. The first step is to establish a starting point. From there we can focus on turning it into a clean, modern API.
 
 
2) Please handle fonts in a modern way and flexible. A 2D drawing API is useless nowadays without strong typographic support.
3) This points out the convenience of a text-layout facility.
 
Handling fonts, text rendering, and typography in general is a hard problem. We are going to do the best we can on this front, but understand that A) N3888 is a starting point not a final destination; and B) the final destination will still only be version 1. We are intentionally aiming at a small scope for the reasons set forth in N3791. We will be doing our best to craft a final API that is not final. In other words, one that can be expanded in the future to provide more functionality based on what users of the library tell us they want.
 
Also, I completely reject the statement that "[a] 2D drawing API is useless nowadays without strong typographic support." Indeed, I contend that it would be useful even with no typography. If I could write standard, portable C++ code to do all of my 2D drawing and have it just work on Windows, GNU/Linux, the various *BSD distributions, Mac OS X, iOS, Android, Windows Phone, Blackberry, AIX, etc., and all I had to do was write platform specific code for font management and text rendering, I would still have saved myself a huge amount of work.
 

4) An extensible reading/writing facility for image formats is a must, not an option
 
I agree. See my responses to Bengt on this subject for more info as to why.
 
 
5) Don’t ignore platform differences. Giving access to native handles is not sufficient. Make it possible to have, for example,
a raster engine, an OpenGL engine, a D3D engine, etc…
 
This is untenable. Native handles are a nice, proven abstraction for exposing platform-specific functionality without being compelled to standardize all of those platforms. Because if we had an OpenGL engine and a D3D engine, etc., we would need to standardize those interfaces. Even assuming that the Khronos Group and Microsoft both suddenly decided to allow these technologies to be submitted for standardization, it would still be a herculean task. With a horribly fragile result. The changes between different versions of OpenGL and D3D are not trivial. Even if we could standardize these things we would then be chained to them for the indefinite future, forced to update the standard every time they made a change or else fall woefully behind. For these reasons and more, even contemplating a design that includes platform specifics is futile.
 

6) For the software engine, writing the spec to have (or make it easy to have) pixel-to-pixel portability across implementations is a must.
 
You can't even get pixel-to-pixel portability between different generations of GPUs from the same manufacturer (though they have gotten much better in recent years). Beyond stating that, I'm not really sure what point you were trying to make. We standardize an interface. The interface has semantics that define behaviors based on inputs. Implementers implement those interfaces and (hopefully) comply with the semantics, thus giving the results that the standard specifies. But we cannot compel implementers to do anything. It's ultimately their responsibility to deliver what their user base desires. So the best we can do is create something fun and exciting that we really want to use and in turn promote it to other C++ users so that they want to use it too which in turn puts pressure on the compiler vendors to provide what we are asking for.
 
 
7) Printing support is highly coupled with drawing, and it’s difficult to get the API right. You need a complete interface to set
   all the possible printing options out there, but with a connection with the underlying platform because in GUI code you want to make the
   user choose them with the native interface (this means importing the settings from the native type that represent them, not to handle the
 
See my response to 5 above. It's up to implementers to choose whether to support printing functionality at all and then to decide how to do it if they do decide to.
 
 
8) Interoperability with the underlying platform is essential (again with a standard way to convert types that represents images, brushes,
    colors, fonts etc… from the native type to the std:: ones).
 
This is one of the main purposes of native handles, those things that you dismissed earlier as being not sufficient.
 
 
9) Please give us powerful mathematical and geometrical primitives (a well designed and efficient std::matrix and two point<T> and
    rectangle<T> classes are not enough at all)
 
That is the domain of SG6 and LEWG. See generally http://isocpp.org/std/the-committee . Many of the people involved with SG13 undoubtedly have good math backgrounds, but to find people who are the real subject matter experts in this, you would turn to SG6. Standards don't happen unless someone writes a proposal. If you believe these things need to be standardized, start working on a proposal. If you need help, ask. There are people who would be happy to help guide you through the process of writing a proposal, though you need to be ready and willing to spend the hundreds of hours that a proposal of the nature that you have asked for would take. And, as mentioned above, willing to spend all of that time with no guarantee that anything will ever come of it.
 

10) With regards of the previous two points, it would be even better if everything is abstracted with a trait system like the boost geometric l
     library (but possibly less complex)
 
Someone else will have to address this point; I do not know if anyone has ever proposed standardizing Boost's Geometry library and if so what the status of any such effort is.
 
 
11) shared_ptr is good, but why do the API have to expose the memory managed object at all? Let’s face the users with RAII types that
    opaquely handles everything.
 
In points 5 and 7 you are asking for exposure of all sorts of platform specific functionality. Here you are saying bury everything in opaque types. There is a logical inconsistency between these two positions. Object lifetime and semantics matter regardless of what aspect of C++ someone is discussing. When it comes to graphics, it matters even if it's not C++. Why? Part of the reason is that GPUs are massively parallel computing devices. They scream like banshees through incomprehensible numbers of calculations every second. Certain operations can bring that vast machinery to a screeching, bone-chilling halt. One of them is moving any non-trivial quantity of data from the GPU to the CPU. Even if you avoid problems like that, you can still hobble the GPU in other ways, such as constantly recreating and destroying resources that it manages. So in a graphics library, the semantics of a type that represents a GPU resource matter a lot. If you want a clean, modern library that also has good performance, getting these issues right is fundamental. Making the API look pretty is fairly trivial by comparison.
 

12) Please investigate on an FP-like approach to compose drawing operations.
  i.e. we have stroke() and fill(), make me compose them to stroke and fill and reuse the composition, in a way that it run faster than execute  two operations again.

 
FP? I'm afraid I'm not sure what you are referring to. Possible because it's 3:46 in the morning and all that is coming to mind is floating point.
 
 
Note that I’m not saying “Cairo doesn’t do any of these”, but even if the library itself have strong support for a lot of those points,
the interface is still fundamentally old...

 
Cairo provides all of the functionality that we wanted from a 2D drawing library. That's why we chose it. The mechanical transformation turns its C API into a very rough C++ API. We think that this provides a good starting point. It has a ways to go yet even if it is accepted as the starting point. And accepting it as the starting point does not mean that the final product will be accepted. N3888 should be judged on its merits as a proposal for a starting point. If accepted, the final project will need to be judged on its merits as a proposed TS. If it gets to that point (and I hope that it does) I expect and, to the extent permissible, demand that it be judged strictly. Nobody wins if we deliver something bad to C++ developers. So we will give it our all to make the best thing we can and hope that the Committee ultimately deems it worthy. If not, I'll try again because I believe in this effort.
 
-Mike
 

Klaim - Joël Lamotte

unread,
Jan 28, 2014, 6:37:29 AM1/28/14
to std-pr...@isocpp.org

On Tue, Jan 28, 2014 at 10:02 AM, Michael McLaughlin <mike...@gmail.com> wrote:

12) Please investigate on an FP-like approach to compose drawing operations.
  i.e. we have stroke() and fill(), make me compose them to stroke and fill and reuse the composition, in a way that it run faster than execute  two operations again.

 
FP? I'm afraid I'm not sure what you are referring to. Possible because it's 3:46 in the morning and all that is coming to mind is floating point.

I believe FP here means Functional Programming (style).

Felipe Magno de Almeida

unread,
Jan 28, 2014, 8:15:27 AM1/28/14
to std-pr...@isocpp.org
On Tue, Jan 28, 2014 at 4:35 AM, Michael McLaughlin <mike...@gmail.com> wrote:
>

[snip]

>> rectangle and rectangle_int should be replaced with a template, instances
>> of which are used in the api. This template along with point<T> should be in
>> the top level std namespace and used throughout std when appropriate. Over
>> time this will increase interoperability between 3rd party libraries as they
>> start using them.
>
> I definitely agree as far as creating a point type. I need to think more
> about the merits of templating point and rectangle; it seems obviously
> good

IMO the coordinate space should be parameterizable by algorithms, which
makes rectangles and point obviously templates. But with the current OO
API this doesn't make sense. So I really don't see how the current API
can, through evolutionary steps, get anywhere near C++ Standard quality.
Have you evaluated Boost.GIL? I couldn't see it mentioned in the proposal.

[snip]
I don't buy this let's remove the copy-constructor because it is expensive.
If the user doesn't want to copy, he can just not copy it, if he wants to
copy, then why is the class irregular?

> That said, shared ownership semantics are not a final decision. I think they
> are justified here and will be making a formal case for it, but the decision
> will ultimately rest in the hands of LEWG and SG13. We may well end up with
> move-only semantics for GPU objects. I very much doubt we would decide on
> value semantics due to the expense of copying GPU resources. I added an
> issue for this for the sake of completeness.

I really don't get it what's the problem with expensive copying. It is only
expensive when they are used. It is also expensive to copy a list with
a million elements, but it is still copyable nevertheless.

>> There should be a ctor of surface which takes a rectangle<double> to
>> describe the subsurface extent, not only one with four discrete ints. The
>> use of double in this API is strange to me, or needs to be complemented with
>> a (more commonly used) API using int. The latter solution would be
>> appropriate in the case that using double implies a coordinate transform
>> taking place before the subsurface is created. The hard thing about this is
>> that if rotation or shear is included in the transform the subwindow can
>> hardly be created.
>
> Oddly enough, cairo states that the semantics for the function that the ctor
> derives from are only defined if you use whole units. Non whole units have
> not been finalized as of yet. Given that, I don't see any reason to keep
> this as a double (what it is currently). That said, I'm not adding it as an
> issue simply because when the issue of what to do about the proposed point
> type and the existing rectangle and rectangle_int types is resolved, what
> will follow will be rules transforming these sorts of functions into
> functions that take the appropriate resulting type. That was one of the
> transforms we intended to include (it's noted as a comment at the end of
> N3825) but that I did not get around to including in N3888 (in part because
> I figured that people would want to discuss how we should define types such
> as point, rectangle, etc.). So rather than create types where none existed,
> I stuck with cairo's types and transformed them. Since cairo has no point
> type, N3888 has no point type. But there will be one and when there is there
> will be a new transformation rule added to section 2 of the rules that will
> cover it.

Or we could have a Point concept instead.

>> To get around this mess it seems logical to follow the path that C++ has
>> taken in so many other areas, i.e. to templatize the methods and treat the
>> parameter type (now a T) like a concept. Thus it is the concept of an
>> "image" or "raster" that would be sdtandardized and then anything that
>> complies to this concept can be drawn on or blitted onto something else.
>> However, due to the plethora of storage formats for pixel data and the large
>> sizes of images an efficient implementation of this is not easy to
>> implement. I worked in a project which had implemented this and it was the
>> module that had by far the longest compile times due to the large amount of
>> template instantiations created. Note also that due to the dynamic nature of
>> image file loading there must be ways to dispatch to these template
>> functions at run time depending on the user's selection of file.

Was this project, by any chance, open source?

> -Mike
>
> --

Matthew Woehlke

unread,
Jan 28, 2014, 9:44:08 AM1/28/14
to std-pr...@isocpp.org
On 2014-01-28 04:02, Michael McLaughlin wrote:
> As for Qt, I invite you to consider the number of dependencies that Qt has
> on Qt. I considered and consciously rejected deriving an API from QPainter.
> Not because it is bad; I think it is quite good. I rejected it because I
> believed that trying to carve out the 2D drawing part of Qt without needing
> to standardize most of the rest of Qt (or butcher the API to the point
> where we'd be losing most of the benefits that Qt provides) would be
> extremely difficult. To me, Qt is designed to be used with Qt. Take a look
> at the QPainter API: http://qt-project.org/doc/qt-5/qpainter.html . Note
> the QVector<T> and the QString and QImage and all the other Qt types. It's
> a lot of baggage. Cairo, by contrast, is a graphics library. Period.

The worst of that is related to text rendering¹. If you're excising that
anyway, it's not an issue. The rest I suspect is either support classes
that should be part of the API anyway (e.g. QImage) or readily ported
(e.g. QVector<T> -> std::vector<T>).

Sacrificing quality of API for ease of porting an existing
implementation seems like a dubious trade-off IMHO. If it is generally
felt that Qt represents a good API design "except for all those Qt
classes", then IMHO the right thing to do is do a mass-rename of Qt
classes to STL classes and propose the result as the API.

(¹ for good reason; as mentioned, text rendering, especially when you
start getting into localization intricacies, is hard.)

> On Sun, Jan 26, 2014 at 6:23 PM, Nicola Gigante wrote:
>> 5) Don’t ignore platform differences. Giving access to native handles is
>> not sufficient. Make it possible to have, for example,
>> a raster engine, an OpenGL engine, a D3D engine, etc…
>
> This is untenable. Native handles are a nice, proven abstraction for
> exposing platform-specific functionality without being compelled to
> standardize all of those platforms.

As I understood it, the point here is that the API should be agnostic to
the engine. Ideally to the extent that the engine can be changed without
recompiling code. This is the case with e.g. Qt, which allows swapping
out the underlying engine via an environment variable.

Exposing too much of the underlying guts is an invitation to write
non-portable code.

(That said, I may be reading my own expectations to that comment and in
fact, agreeing with you and disagreeing with Nicola.)

> Because if we had an OpenGL engine and
> a D3D engine, etc., we would need to standardize those interfaces.

Not at all; see above. The drawing API is standard, the implementation
is not and is free to deal with non-standardized details of the
underlying engine technology. If anything, the opposite is true;
exposing internal details harms the "standard-ness" of the API without
the underlying engine itself being standardized. (And worse, invites
applications to write platform-specific code, which is rather counter to
the objective.)

>> 8) Interoperability with the underlying platform is essential (again with
>> a standard way to convert types that represents images, brushes,
>> colors, fonts etc… from the native type to the std:: ones).

Really? Why?

I've written a number of Qt and/or OpenGL applications, and can't think
of any case in which I've needed to deal directly with e.g. X objects.

Per above, this is actively *harmful* to writing portable code and IMHO
should be strongly discouraged. Even on a single platform, it precludes
(or at least, makes much more difficult) engine agnosticism.

An API that allows using the exact same compiled code to perform
comparable drawing operations regardless of the output device (e.g.
screen, buffer, printer) and engine is IMHO much preferable.

--
Matthew

Ville Voutilainen

unread,
Jan 28, 2014, 9:53:47 AM1/28/14
to std-pr...@isocpp.org
On 28 January 2014 16:44, Matthew Woehlke
<mw_t...@users.sourceforge.net> wrote:
> Sacrificing quality of API for ease of porting an existing implementation
> seems like a dubious trade-off IMHO. If it is generally felt that Qt

It's a trade-off to get an implementable starting point that can then be worked
towards a higher-quality API. The point is having an implementation available
from day one.

> represents a good API design "except for all those Qt classes", then IMHO
> the right thing to do is do a mass-rename of Qt classes to STL classes and
> propose the result as the API.

The question is whether QPainter as an API _starting point_ is better
than Cairo.

>>> 8) Interoperability with the underlying platform is essential (again with
>>> a standard way to convert types that represents images, brushes,
>>> colors, fonts etc... from the native type to the std:: ones).
> Really? Why?

In order to access platform-specific facilities that are not supported by the
portable API. Yes, that access is platform-specific and results in non-portable
code. However, it can be essential for interoperating with platform-specific
facilities, which sometimes are necessary.

> I've written a number of Qt and/or OpenGL applications, and can't think of
> any case in which I've needed to deal directly with e.g. X objects.

I can. I have written Qt applications that needed to use eg. XDamage to
get a live thumbnail of running applications. And for a window list, that
meant dealing with X window handles, too.

> Per above, this is actively *harmful* to writing portable code and IMHO
> should be strongly discouraged. Even on a single platform, it precludes (or
> at least, makes much more difficult) engine agnosticism.

Yes, it's harmful to writing portable code, but it is in contrast more harmful
to say that this API of ours allows no interoperability with platform-specific
facilities. The users who need such "escape hatches" to interoperate
with platform-specific facilities cannot use this portable API at all.

The native handles in the standard thread library are there for a reason.
That reason applies similarly to a standard drawing library.

Thiago Macieira

unread,
Jan 28, 2014, 11:46:43 AM1/28/14
to std-pr...@isocpp.org
On terça-feira, 28 de janeiro de 2014 09:44:08, Matthew Woehlke wrote:
> The worst of that is related to text rendering¹. If you're excising that
> anyway, it's not an issue. The rest I suspect is either support classes
> that should be part of the API anyway (e.g. QImage) or readily ported
> (e.g. QVector<T> -> std::vector<T>).

> (¹ for good reason; as mentioned, text rendering, especially when you
> start getting into localization intricacies, is hard.)

Text rendering and localisation represent probably 80% or more of the install
size of combined QtCore and QtGui, due to the CLDR and Unicode tables in Qt
and in ICU. And then we have external dependencies like fontconfig and freetype
to get fonts, harfbuzz to get shaping correct, classes like
QTextBoundaryFinder to find where to do word-splitting...

This part of the discussion is quite heated. What Michael proposed is to limit
the required font and text handling capabilities to what Cairo offers in the
toyfont, though implementations would be welcome to do the whole thing.

> > This is untenable. Native handles are a nice, proven abstraction for
> > exposing platform-specific functionality without being compelled to
> > standardize all of those platforms.
>
> As I understood it, the point here is that the API should be agnostic to
> the engine. Ideally to the extent that the engine can be changed without
> recompiling code. This is the case with e.g. Qt, which allows swapping
> out the underlying engine via an environment variable.
>
> Exposing too much of the underlying guts is an invitation to write
> non-portable code.

Which might be necessary. Not providing the native handles limits the
usefulness to only the API that is provided, no extension possible.

Qt provides the native handles for the widgets, for example, and that allows
applications like window managers and desktop fixtures to be developed. They'd
be impossible without platform-specific code.

PS: the swapping of the engine was removed in Qt 5 because there's only one
engine left.

> I've written a number of Qt and/or OpenGL applications, and can't think
> of any case in which I've needed to deal directly with e.g. X objects.
>
> Per above, this is actively *harmful* to writing portable code and IMHO
> should be strongly discouraged. Even on a single platform, it precludes
> (or at least, makes much more difficult) engine agnosticism.

Discouraged, yes. But still necessary: those applications might be few and far
between, but they exist.
signature.asc

Nicola Gigante

unread,
Jan 28, 2014, 11:58:12 AM1/28/14
to std-pr...@isocpp.org
Il giorno 28/gen/2014, alle ore 10:02, Michael McLaughlin <mike...@gmail.com> ha scritto:

 
I try my best to judge opinions based on the ideas contained in them, not the person expressing them.

Of course, thank you :)

 

I completely agree with Bengt regarding everything he said, but my point goes further. With modern C++
gaining relevance thanks to new language features and new common practices, why on earth should the committee
standardize an interface based on an old C library? I think that the idea of basing a standard rendering library
on Cairo (or GDI, or any library designed before C++11 or even before C++98) is flawed by principle.

 
Cairo wasn't designed before C++98. It was started sometime in the 2002-2003 time frame as best I can tell. N3888 does not contemplate GDI.

I’m afraid I’ve badly explained myself here. I know cairo has not been designed before. GDI was that “pre-C++98” library I was 
referring to, and I know it’s not mentioned in your proposal. All I wanted to say was that “generally” I don’t agree very much on the concept
of taking a previous library and reshape it. I know, as you have said a few times, that this is only a starting point, don’t worry about this.

 
I know of no major graphics library that was designed after C++11. 
 
That’s exactly the point here. Nobody have implemented a new design in years.
The new language features _may_ allow design patterns and kinds of interactions between the library and user code
that are unknown at this time. Starting from a pre-existing API and simply “restyling” it could hide those possibilities. Not that
I don’t understand that having a working implementation from the start is equally important.



Differently from other standard library facilities or subsystems, a 2D drawing API cannot be designed by
one or few men alone. To get it right, it needs enough field-testing, to be sure that:
1) the API is easy to use (in my opinion, the Qt drawing API is a reference point from this point of view),
2) it is flexible enough, given possible future uses and future hardware capabilities
3) it doesn’t impose artificial constraints, allowing the most efficient implementation across varying architectures and OSes.

Because of this, proposing an interface (even if a modern, well thought interface), without actually having implemented it and
made it used by actual users for enough time, is a bad idea.



Here, I’ve never intended to say that the people involved don’t have experience in implementing those things! It was only a general 
statement.

 
As for Qt, I invite you to consider the number of dependencies that Qt has on Qt. I considered and consciously rejected deriving an API from QPainter. Not because it is bad; I think it is quite good. I rejected it because I believed that trying to carve out the 2D drawing part of Qt without needing to standardize most of the rest of Qt (or butcher the API to the point where we'd be losing most of the benefits that Qt provides) would be extremely difficult. To me, Qt is designed to be used with Qt. Take a look at the QPainter API: http://qt-project.org/doc/qt-5/qpainter.html . Note the QVector<T> and the QString and QImage and all the other Qt types. It's a lot of baggage. Cairo, by contrast, is a graphics library. Period.
 
If you want to write a proposal based on QPainter or some other aspect of Qt you can. Things only get standardized when someone decides to write a proposal and put in the work of explaining it, defending it, accepting valid criticisms of it, changing it, and so on until such time as it is at last accepted as a standard. And there is no guarantee that it ever will be accepted.
 

Please note that the QtGui module (which in Qt 5.x does not contain widgets, only drawing code) depends only on QtCore, which is nothing 
more than an alternative to basic facilities offered by the standard library and the STL (threading, strings, containers, ecc…).
There are no other dependencies. I don’t see any difference in an API function drawPolygon(QVector<QPoint> points) and
the corresponding draw_polygon(std::array_view<std::point> points) (for example). 

Anyway I was not proposing to take Qt as the starting point of a proposal in the same way you took Cairo. What I was suggesting was to
take it as a reference from a point of view of ease of use and flexibility, which I admit is a matter of taste, but it seems a widely common taste.

 
2) Please handle fonts in a modern way and flexible. A 2D drawing API is useless nowadays without strong typographic support.
3) This points out the convenience of a text-layout facility.
 
Handling fonts, text rendering, and typography in general is a hard problem. We are going to do the best we can on this front, but understand that A) N3888 is a starting point not a final destination; and B) the final destination will still only be version 1. We are intentionally aiming at a small scope for the reasons set forth in N3791. We will be doing our best to craft a final API that is not final. In other words, one that can be expanded in the future to provide more functionality based on what users of the library tell us they want.
 
Also, I completely reject the statement that "[a] 2D drawing API is useless nowadays without strong typographic support." Indeed, I contend that it would be useful even with no typography. If I could write standard, portable C++ code to do all of my 2D drawing and have it just work on Windows, GNU/Linux, the various *BSD distributions, Mac OS X, iOS, Android, Windows Phone, Blackberry, AIX, etc., and all I had to do was write platform specific code for font management and text rendering, I would still have saved myself a huge amount of work.

I agree with you that my statement was a little exaggerated. But please distinguish between font rendering and text layout. The first, if done,
must be done well. Support for a variety of font formats and fully-featured font rendering (kerning, rtl ecc…) is essential, if 
the concept of “font” is supported at all. If the alternative is to support text rendering in a primitive way, it’s better to not support it at all
and let users use already available libraries to handle it.

  
5) Don’t ignore platform differences. Giving access to native handles is not sufficient. Make it possible to have, for example,
a raster engine, an OpenGL engine, a D3D engine, etc…
 
This is untenable. Native handles are a nice, proven abstraction for exposing platform-specific functionality without being compelled to standardize all of those platforms. Because if we had an OpenGL engine and a D3D engine, etc., we would need to standardize those interfaces. Even assuming that the Khronos Group and Microsoft both suddenly decided to allow these technologies to be submitted for standardization, it would still be a herculean task. With a horribly fragile result. The changes between different versions of OpenGL and D3D are not trivial. Even if we could standardize these things we would then be chained to them for the indefinite future, forced to update the standard every time they made a change or else fall woefully behind. For these reasons and more, even contemplating a design that includes platform specifics is futile.
 

I would have been mad to propose the standardization of OpenGL or D3D interfaces!! What I meant was another thing, in the spirit
of what Qt means by “engine”. The drawing API is standard. The underlying engine is what actually performs the drawing. The raster 
engine implements everything on the CPU thus not requiring additional hardware resources (see the point 6). The OpenGL engine uses 
OpenGL to draw, the D3D engine uses D3D. The OpenVG engine uses OpenVG. My idea was to make the API relying on an underlying
unspecified engine, mandating implementors to provide at least a raster engine and letting them providing all the engines that the underlying
platform may allow (for example there could be a Quartz engine on OS X and a GDI engine on Windows). Then the user can chose if using
the “default” engine, which the implementation can chose as the best performing depending of runtime availability, for example, of GPU acceleration, or to chose a specific engine, being conscious of portability issues. It’s a reasoning very similar to the container allocators 
infrastructure, if you think about it. 
Note that a design like this does not expose platform specifics at all. Indeed it hides details, by allowing the user to use native resources
without the need to interfacing directly with them. Note that engines are also separated from the concept of “drawing device”.
With a raster or OpenGL engine you can careless choose to draw to an image or to an on-screen framebuffer. Qt even have classes
like QPicture, which simply “records" drawing commands instead of render them, allowing to save the graphics to vectorial formats
like PDF or the like.

All of this is not an original idea. It’s just how the Qt drawing API is designed from the modularity point of view. This kind of “inspiration”
is what I meant by referring to Qt in the previous points, not a mechanical transformation of its APIs.


6) For the software engine, writing the spec to have (or make it easy to have) pixel-to-pixel portability across implementations is a must.
 
You can't even get pixel-to-pixel portability between different generations of GPUs from the same manufacturer (though they have gotten much better in recent years). Beyond stating that, I'm not really sure what point you were trying to make. We standardize an interface. The interface has semantics that define behaviors based on inputs. Implementers implement those interfaces and (hopefully) comply with the semantics, thus giving the results that the standard specifies. But we cannot compel implementers to do anything. It's ultimately their responsibility to deliver what their user base desires. So the best we can do is create something fun and exciting that we really want to use and in turn promote it to other C++ users so that they want to use it too which in turn puts pressure on the compiler vendors to provide what we are asking for.
 

My point started with “For the software engine”. What I meant was the raster engine I’ve talked about at the previous point. pixel-to-pixel
portability is what a lot of drawing software (from TeX to the Qt raster engine itself) actually achieves by not relying on hardware and 
implementing rendering algorithms by software. Note that not using the GPU might sound slow, but a software fallback is going to be needed 
anyway by portable standard libraries (like libstdc++ or libc++) that are not tied on a particular platform. It might also be the only choice 
available on embedded platforms (or simply if I want to draw something from a command line or server-side process and I can’t link to OpenGL or whatever). From that to allowing users to specify “I _want_ my graphic to be rendered in software” and making at 
least some guarantees about results, the trip is short.

 
8) Interoperability with the underlying platform is essential (again with a standard way to convert types that represents images, brushes,
    colors, fonts etc… from the native type to the std:: ones).
 
This is one of the main purposes of native handles, those things that you dismissed earlier as being not sufficient.

It depends on what you mean by handles. If you mean “any platform specific type that represents anything”, ok. But the concept is
vague. A HANDLE type on Windows is an handle of course, but if you’re on OS X, an instance of CFImage is an handle? a QBrush object is 
an handle?

I think a lot of users of this library will use it in already existing code bases. What i would like, is a way to shade slowly from using
the platform specific code to the standard API in the most seamless way possible. Or what if you have a mature, big code base of imaging
algorithms, and you want to use the results with the new standard library? The only way to do that is to allow 
the integration of previously used types. Letting users to go to and from std::drawing::image to CFImage, for example. Of course, the specific 
conversions cannot be ruled by the standard! What is needed by the standard is a generic architecture that is able to use user’s types for
the representation of basic concepts like images, fonts, colors, brushes, and geometric primitives, like as with a system of traits types.

It’s nothing new, it’s only the same spirit of the STL. It provides vectors, but generic algorithms work also on the user’s MyOwnVector, given
the interface is compatible. At the same time, libraries like Qt provides their own QVector, but have made it compatible with the STL 
algorithms!

 
 
9) Please give us powerful mathematical and geometrical primitives (a well designed and efficient std::matrix and two point<T> and
    rectangle<T> classes are not enough at all)
 
That is the domain of SG6 and LEWG. See generally http://isocpp.org/std/the-committee . Many of the people involved with SG13 undoubtedly have good math backgrounds, but to find people who are the real subject matter experts in this, you would turn to SG6.

The this point reduces to the triviality of suggesting collaboration between groups.

Standards don't happen unless someone writes a proposal. If you believe these things need to be standardized, start working on a proposal. If you need help, ask. There are people who would be happy to help guide you through the process of writing a proposal, though you need to be ready and willing to spend the hundreds of hours that a proposal of the nature that you have asked for would take. And, as mentioned above, willing to spend all of that time with no guarantee that anything will ever come of it.
 

I’m not that masochist :P Nevertheless I’m happy to provide my opinions for what it’s worth, and I think that incremental improving is better 
than forking proposals like linux distros ;)


10) With regards of the previous two points, it would be even better if everything is abstracted with a trait system like the boost geometric l
     library (but possibly less complex)
 
Someone else will have to address this point; I do not know if anyone has ever proposed standardizing Boost's Geometry library and if so what the status of any such effort is.
 
I was not proposing to standardize boost geometry. What I’m talking about, again, is to take its “principles” as reference. In this case I’m 
talking about the concept of providing geometric algorithms that work on any user-provided “point” or “rectangle” or “polygon” types, while
providing concrete types for these concepts anyway. Again, this is nothing but the general philosophy of the STL.


11) shared_ptr is good, but why do the API have to expose the memory managed object at all? Let’s face the users with RAII types that
    opaquely handles everything.
 
In points 5 and 7 you are asking for exposure of all sorts of platform specific functionality. Here you are saying bury everything in opaque types. There is a logical inconsistency between these two positions.
Here I was only saying that, if some objects have shared ownership semantics, in my opinion it's better to handle the ownership management 
opaquely instead of exposing shared_ptr in the API, and I think to understand that it’s already this way anyway. It has nothing to do with
platform specific functionalities.

12) Please investigate on an FP-like approach to compose drawing operations.
  i.e. we have stroke() and fill(), make me compose them to stroke and fill and reuse the composition, in a way that it run faster than execute  two operations again.

 
FP? I'm afraid I'm not sure what you are referring to. Possible because it's 3:46 in the morning and all that is coming to mind is floating point.
 

Sorry, I meant a Functional Programming-like approach. What I mean was to be able to compose drawing operations, or batching groups of 
operations in a composable way. It was an abstract point more than a concrete proposal. This is simply one of those things that I’ve talked
about at the beginning of the email: undiscovered possibilities. Nobody has designed a drawing API that can leverage lambdas because
C and C++ didn’t have lambdas. Lambdas are only an example anyway, so don’t ask me how they fit in the drawing library. I’m only
saying I think it’s worth spending some time investigating those possibilities to be able, maybe, to end up with something really innovative.


-Mike
 
Good bye,
Nicola

Nicola Gigante

unread,
Jan 28, 2014, 12:01:01 PM1/28/14
to std-pr...@isocpp.org

Il giorno 28/gen/2014, alle ore 17:58, Nicola Gigante <nicola....@gmail.com> ha scritto:

>
>
> I’m afraid I’ve badly explained myself here. I know cairo has not been designed before. GDI was that “pre-C++98” library I was
> referring to, and I know it’s not mentioned in your proposal. All I wanted to say was that “generally” I don’t agree very much on the concept
> of taking a previous library and reshape it. I know, as you have said a few times, that this is only a starting point, don’t worry about this.

Sorry, here I mean taking a previous _C_ library and reshape it... of course… reshaping previous libraries is what all the TR1 has done for C++11 :P

Nicola

Matthew Woehlke

unread,
Jan 28, 2014, 12:28:35 PM1/28/14
to std-pr...@isocpp.org
On 2014-01-28 11:46, Thiago Macieira wrote:
> On terça-feira, 28 de janeiro de 2014 09:44:08, Matthew Woehlke wrote:
>> As I understood it, the point here is that the API should be agnostic to
>> the engine. Ideally to the extent that the engine can be changed without
>> recompiling code. This is the case with e.g. Qt, which allows swapping
>> out the underlying engine via an environment variable.
>>
>> Exposing too much of the underlying guts is an invitation to write
>> non-portable code.
>
> Which might be necessary. Not providing the native handles limits the
> usefulness to only the API that is provided, no extension possible.

Sure. I think what I'm trying to say is more that if the API is such
that writing native code is necessary more often than "rarely, and only
in special situations", it's not a very good API.

Also, I wouldn't write native access into the standard except perhaps
for some brief wording that implementations may extend the API for that
reason. Otherwise you *do* get into standardizing X / WinGDI / DirectX /
OpenGL / etc.

IOW let the platform vendor "standardize" native access, and only worry
about the homogenous API at the language level.

--
Matthew

Ville Voutilainen

unread,
Jan 28, 2014, 12:50:29 PM1/28/14
to std-pr...@isocpp.org
On 28 January 2014 19:28, Matthew Woehlke
<mw_t...@users.sourceforge.net> wrote:
> Also, I wouldn't write native access into the standard except perhaps for
> some brief wording that implementations may extend the API for that reason.
> Otherwise you *do* get into standardizing X / WinGDI / DirectX / OpenGL /
> etc.
>
> IOW let the platform vendor "standardize" native access, and only worry
> about the homogenous API at the language level.


I think it's beneficial to be able to do, in a fairly straightforward
manner, things like

auto h = foo.native_handle();
low_level_processing_func(h);
// or a combination as a single line

than having to write
#if defined(PLATFORM_X)
auto h = foo.get_native_handle_for_platform_x();
#elif defined(PLATFORM_Y)
yadda yadda yadda
#endif
low_level_processing_func(h); // I do hope that the #else branch
initialized a h...

Jeffrey Yasskin

unread,
Jan 28, 2014, 1:22:52 PM1/28/14
to std-pr...@isocpp.org
On Tue, Jan 28, 2014 at 5:15 AM, Felipe Magno de Almeida
<felipe.m...@gmail.com> wrote:
> On Tue, Jan 28, 2014 at 4:35 AM, Michael McLaughlin <mike...@gmail.com> wrote:
>>
>
> [snip]
>
>>> rectangle and rectangle_int should be replaced with a template, instances
>>> of which are used in the api. This template along with point<T> should be in
>>> the top level std namespace and used throughout std when appropriate. Over
>>> time this will increase interoperability between 3rd party libraries as they
>>> start using them.
>>
>> I definitely agree as far as creating a point type. I need to think more
>> about the merits of templating point and rectangle; it seems obviously
>> good
>
> IMO the coordinate space should be parameterizable by algorithms, which
> makes rectangles and point obviously templates. But with the current OO
> API this doesn't make sense.

There might be use cases that need to parametrize the kind of integer
inside a point, but there also might not. We need concrete examples in
order to decide, not just a blind assumption that "real" C++ code
exposes templates instead of virtual functions or type erasure.

Jeffrey

Matthew Woehlke

unread,
Jan 28, 2014, 1:49:59 PM1/28/14
to std-pr...@isocpp.org
On 2014-01-28 12:50, Ville Voutilainen wrote:
> On 28 January 2014 19:28, Matthew Woehlke
> <mw_t...@users.sourceforge.net> wrote:
>> Also, I wouldn't write native access into the standard except perhaps for
>> some brief wording that implementations may extend the API for that reason.
>> Otherwise you *do* get into standardizing X / WinGDI / DirectX / OpenGL /
>> etc.
>>
>> IOW let the platform vendor "standardize" native access, and only worry
>> about the homogenous API at the language level.
>
> I think it's beneficial to be able to do, in a fairly straightforward
> manner, things like
>
> auto h = foo.native_handle();
> low_level_processing_func(h);

As low_level_processing_func is presumably already conditionally defined
depending on the platform, why would you not simply pass 'foo' to it
directly? (What if different implementations have multiple possible
handles that the user may need? What if an implementation supports
multiple, runtime-selectable engines with different handle types?)

> #if defined(PLATFORM_X)
> auto h = foo.get_native_handle_for_platform_x();
> #elif defined(PLATFORM_Y)
> yadda yadda yadda
> #endif
> low_level_processing_func(h); // I do hope that the #else branch
> initialized a h...

If it didn't, would you not have also gotten a compile error in the
above version due to there being no appropriate overload (if there is a
definition at all) of low_level_processing_func?

--
Matthew

Michael McLaughlin

unread,
Jan 28, 2014, 3:18:37 PM1/28/14
to std-pr...@isocpp.org
Thanks! That does indeed fit with the rest of the suggestion.

Ville Voutilainen

unread,
Jan 28, 2014, 4:28:55 PM1/28/14
to std-pr...@isocpp.org
On 28 January 2014 20:49, Matthew Woehlke
<mw_t...@users.sourceforge.net> wrote:
> On 2014-01-28 12:50, Ville Voutilainen wrote:
>> I think it's beneficial to be able to do, in a fairly straightforward
>> manner, things like
>> auto h = foo.native_handle();
>> low_level_processing_func(h);
> As low_level_processing_func is presumably already conditionally defined
> depending on the platform, why would you not simply pass 'foo' to it
> directly? (What if different implementations have multiple possible handles

Because I'd rather not duplicate the code that extracts the native handle
from foo.

> that the user may need? What if an implementation supports multiple,
> runtime-selectable engines with different handle types?)

Then these handles need to be separately extracted from the native handle.
That is, of course, not going to end up achieving the avoidance of boilerplate
in the previous part.

>> #if defined(PLATFORM_X)
>> auto h = foo.get_native_handle_for_platform_x();
>> #elif defined(PLATFORM_Y)
>> yadda yadda yadda
>> #endif
>> low_level_processing_func(h); // I do hope that the #else branch
>> initialized a h...
> If it didn't, would you not have also gotten a compile error in the above
> version due to there being no appropriate overload (if there is a definition
> at all) of low_level_processing_func?


Sure, but I would've gotten that compiler error from code that looks remotely
sane, rather than being a preprocessor mess.

Thiago Macieira

unread,
Jan 28, 2014, 4:49:19 PM1/28/14
to std-pr...@isocpp.org
On terça-feira, 28 de janeiro de 2014 23:28:55, Ville Voutilainen wrote:
> > that the user may need? What if an implementation supports multiple,
> > runtime-selectable engines with different handle types?)
>
> Then these handles need to be separately extracted from the native handle.
> That is, of course, not going to end up achieving the avoidance of
> boilerplate in the previous part.

FYI

This is exactly the case now in Qt 5 (especially on Unix), since the backend
is selected at runtime and there are no macros you can #ifdef on.

There's a function that can return the name of the backend at runtime and you
have to make your decision then. That means the user application needs to
guess ahead of time which backends it might need to integrate. This is
especially the case for Linux, where the backend might be "xcb", "wayland",
"eglfs" or other things.

The integration is done with a series of QPlatformXXXX classes, especially
QPlatformNativeInterface:

virtual void *nativeResourceForIntegration(const QByteArray &resource);
virtual void *nativeResourceForContext(const QByteArray &resource,
QOpenGLContext *context);
virtual void *nativeResourceForScreen(const QByteArray &resource, QScreen
*screen);
virtual void *nativeResourceForWindow(const QByteArray &resource, QWindow
*window);
virtual void *nativeResourceForBackingStore(const QByteArray &resource,
QBackingStore *backingStore);

Used, for example, like:

// xcb:
xcb_connection_t *d =
qApp->nativeInterface()->nativeResourceForWindow("connection", w);
signature.asc

Bengt Gustafsson

unread,
Feb 2, 2014, 11:14:20 PM2/2/14
to std-pr...@isocpp.org
About method overloads that take point or rect types. I now came to the conclusion that we should have a template overload to cater for "all" types
of point classes the user of the library may have on hand. Can this be done? Yes: Using the same principles as std::begin() and std::end() it can, even if x and y are not named so, for instance.

template <typename T> decltype(typename T::x) get_x(const T& src) { return src.x; }

// But now, oops, MyPoint didn't have an x member as it has a double data[2] instead to store the coordinates (it may be part of a matrix library). This is easily fixed:

double get_x(const MyPoint& src) { return src.data[0]; }


// Functions taking points inside std::drawing classes then have this style signature:

template<typename P> void DrawLine(const P& from, const P& to) {
    DrawLine(get_x(from), get_y(from), get_x(to), get_y(to));
}

// Which of course calls the non-template (virtual?) DrawLine() that does the job.

// The get_x() returns decltype(T::x) to handle the possibility that there are overloaded DrawLine for double and int parameters.

Aside: I think that the logical thing to do is to actually make such methods virtual. Any drawing operation is going to be very heavy compared to a virtual call. Forcing users to make templates out of all their drawing code in order to draw on different devices is just not practical. Firstly you don't want to have that type of code
in header files, and secondly you may not even know at the call site what you are drawing on (for instance different types of printers or vector file generators). Similar reasoning applies to image memory handling, where the runtime file format of a loaded image file may cause different memory layouts etc.

C++ provides many tools, we must try to use all of them appropriately!

Thiago Macieira

unread,
Feb 2, 2014, 11:54:32 PM2/2/14
to std-pr...@isocpp.org
Em dom 02 fev 2014, às 20:14:20, Bengt Gustafsson escreveu:
> About method overloads that take point or rect types. I now came to the
> conclusion that we should have a template overload to cater for "all" types
> of point classes the user of the library may have on hand. Can this be
> done? Yes:

Should it be done? IMHO, no.

Making an entirely-template API means potentially a lot of code bloat. It
means all of the actual API is implemented in private APIs, called by inline
template methods.

> C++ provides many tools, we must try to use all of them appropriately!

Appropriately also implies that there are conditions under which some tools
must not be used. I believe this to be one of them.

Bengt Gustafsson

unread,
Feb 9, 2014, 6:12:18 PM2/9/14
to std-pr...@isocpp.org, thi...@macieira.info
The compiler will definitely inline those trivial adaptor methods which look like this or very similar:

getx(const my_point& p) { return p.x; }

It won't generate any object code bloat at all. The alternative, using a std::drawing specific point class will however require creating a temprary object which in some cases is harder to optimize away, and introduces a lot of SOURCE code bloat:

dc.draw_point(std::drawing::point(p.x, p.y));

instead of:

dc.draw_point(p);

I think this is quite compelling!

By the way: Drawing methods such as draw_polygon which take a range of points can also be written to accept diffrerent types of points, although it will be harder to optimize away copying of data, depending on the underlying "device driver" implementation.

Thiago Macieira

unread,
Feb 10, 2014, 12:22:22 AM2/10/14
to std-pr...@isocpp.org
Em dom 09 fev 2014, às 15:12:18, Bengt Gustafsson escreveu:
> It won't generate any object code bloat at all. The alternative, using a
> std::drawing specific point class will however require creating a temprary
> object which in some cases is harder to optimize away, and introduces a lot
> of SOURCE code bloat:
>
> dc.draw_point(std::drawing::point(p.x, p.y));

It only happens if you have a foreign point type in the first place. I'm
recommending you don't. Stick to what the framework gives you.

Bengt Gustafsson

unread,
Feb 10, 2014, 5:38:28 PM2/10/14
to std-pr...@isocpp.org, thi...@macieira.info
We can dream of being able to do that. I have worked in image processing and graphics for 20 years in C++ now, and I can assure you that there are a gazillion of point struct out there that are not going away anytime soon. 90% or more have x and y members which are ints or doubles...

This of course was caused by C++ not defining a point struct in the standard initially, forcing everyone to invent one of their own. Now with new features and techniques we are in the position to be able to create a decent level of interoperability
despite this oversight. I suggest we use it or a standard drawing API will be still born.

mcro...@gmail.com

unread,
Feb 17, 2015, 5:16:46 AM2/17/15
to std-pr...@isocpp.org
I'm so sorry, but i don't understand, why point has all kinds of operations?
in a normal mode i can't add smth to point, the code
point a = {1, 1}, b = {1, 2};
a
+= b;

is without any sense.
Reply all
Reply to author
Forward
0 new messages