OpenOrienteering / mapper

OpenOrienteering Mapper is a software for creating maps for the orienteering sport.
https://www.openorienteering.org/apps/mapper/
GNU General Public License v3.0
402 stars 107 forks source link

Review map color implementation #151

Open dg0yt opened 11 years ago

dg0yt commented 11 years ago

dg0yt reported on Sourceforge [tickets:#151]:

The current map color implementation is rather simple: each map color is either defined in an (abstract?) RGB or CMYK color model. These is insufficient to cover the requirements of all output processes:

The user interface for map color manipulations might focus on device related operations. Operations which interfere with spot color or CMYK printing are for advanced users, mainly during symbol set implementation and maintenance.

Related:

dg0yt commented 11 years ago

puzzlepaint posted on Sourceforge:

Here are some thoughts about implementing some of these requirements, without any considerations about the user interface yet. First, a suggestion on how the color implementation could be done, using data structures for spot colors, map colors and printer colors:

--- Spot colors ---

Components:

Ordering: according to spot color print order Storage: in map file (this ensures that all spot colors referenced by map colors are always available)

--- Map colors ---

Components:

Ordering: according to color priority Storage: in map file

The color for display is determined:

  1. from the map color CMYK, if available
  2. by linear combination of the referenced spot color CMYKs

--- Printer colors ---

Components:

Ordering: does not matter Storage: in some data directory for Mapper


When printing, the user can choose to use the map color set or a printer color set. If a printer color set is chosen, it replaces all map colors which can be matched with a printer color by comparing the spot color reference lists. If no direct match is possible, possibly interpolation could be used.

Alternatively, it should be possible to output the spot color layers separately.


In implementing the overprinting effect, care should be taken to exclude cases where it is not desired. For example, it might be desired to use overprinting for small watercourses, as shown by the overprinting example in the ISOM document. But for a lake with the same color, which might be accidentally drawn on top of some other area symbols which are blocked out by the lake in on-screen display, these other areas should not shine through in the printed map and must be erased for the separate spot color output.

This is why I think that there should be an overprint flag in the map colors. If set to yes, the color will be printed on top of all colors with lower map color priority. If set to no, the color erases all colors with lower map color priority.

Simulating overprinting for outputting separate spot color layers could then be implemented like this:

  1. Allocate an internal image (layer) for every spot color (plus one for all other colors?)
  2. Draw every renderable, in normal map color order, into the image layer corresponding to the renderable's color. If this color does not have the overprint flag set, also draw the renderable into every other layer, using a white color or a blend mode to erase everything which was there before. (3. If desired, blend all layers, darkening overlapping parts to simulate overprinting.)

Because of the erasing in step 2, I think that overprinting and antialiasing might not work at the same time this way. The erasing of other color layers will probably be off in the border area of the drawn shapes.

For just getting the simulated output without creating separate spot color layers, it could be done easier in case the blend mode which simulates the overprinting allows to draw over the same spot twice with the same color without changing the result. Then the drawing could be done as implemented now, while additionally applying the overprinting blend mode for map colors where the flag is set. The advantages would be that this is easier and possibly works with antialiasing. A drawback would be however that the blend order not necessarily corresponds to the spot color draw order this way because it is determined by the map color draw order instead.


Is this what you have in mind, or would you do things differently?

dg0yt commented 11 years ago

With some variations, this is what I have in mind. I'm already working on this task, and I'm preparing a first commit. I think we need to collect some experience. I attach a screenshot from the (single) color dialog, showing a CMYK color which is achieved by overprinting in spot color printing. This is not a mockup but working code.

I think I will remove the CMYK columns from the color dock widget, as they shouldn't be edited by hand very frequently. As I tried to explain, most/all desktop printer drivers accept only RGB (probably thanks to Windows' GDI), and even Qt's PDF export doesn't come with CMYK support. For professional CMYK printers, true Color Management would be desirable.

I don't know how I will treat the opacity value. Probably I will partially disable or ignore it for the moment. It could be difficult to transfer to spot color printing. In addition, we have opacity for templates. So any transparent artwork can be loaded as template.

I fear we cannot easily use (distribute) the proprietary trademarked spot color names. When I update our symbol sets, I will refer to ISOM.

Attachments (on Sourceforge):

dg0yt commented 11 years ago

puzzlepaint posted on Sourceforge:

Wow, thanks for the information. No worries about the opacity control, it's not really needed. But controlling the overprinting should be possible, as described in my post above or equivalently (and for both "desktop" and professional printing in the same way, so there won't be surprises). If that is accounted for somewhere, e.g. in the color dock widget, this looks very good to me.

dg0yt commented 11 years ago

Another screenshot, now a revised color dock widget and the desktop tab. Among other things, I still need to add the new map color attributes to the file formats...

Attachments (on Sourceforge):

dg0yt commented 11 years ago

I pushed the basic code for defining spot colors. For a first review, I updated the 1:10000 ISOM template to use spot color specifications.

The native file format is not yet updated. In the XML format, I limited the precision of single color values to 0.001 which should be more than enough (but already triggered the file format system test, so I also relaxed the color equality epsilon in MapColor::equals()). I incremented the format version number.

dg0yt commented 11 years ago

puzzlepaint posted on Sourceforge:

Note: In case you want to drop write support for the binary file format to prevent some boring work, please go ahead. There's at least one other spot that needs to be adjusted in this case: the copy/paste code for objects and symbols, e.g. in MapEditorController::paste(). This is because copied objects/symbols are currently serialized to memory in binary format, which needs to be changed to the xml format.

In the ISOM symbol set, it would be required to set more "knockout" parameters (assuming this means "no overprint") to get the desired behavior. For example, at a junction of two roads the borders would currently show through. Or overgrowth would show through light green areas painted on top.

Small suggestion: the color dialog UI could be a bit more verbose. For example I took a while to realize that some of the map colors define the spot colors. So in the color dialog I'd suggest to change "Spot color:" to "Defines spot color:", and the other option to e.g. "Uses spot colors\n(screens and overprint):" Also "Knockout" as single-word option leads to some guessing. Maybe "Knockout (no overprinting)", or "Knockout (erases other colors)"? Other than that, very good work of course.

I won't be able to have a closer look at the changes in the next time though because of exams and other stuff ...

dg0yt commented 11 years ago

I have finished a proof-of-concept implementation of overprint effects for the MapWidget. I don't know how well it scales for big maps. Since it is based on QImage, it cannot be easily transferred to printing (except for printing a big image).

I will commit this soon as an alternative view. It shouldn't break anything. And the road crossing works as expected...

Attachments (on Sourceforge):

dg0yt commented 11 years ago

puzzlepaint posted on Sourceforge:

And the road crossing works as expected...

This is obviously because of this :-)

// knockout under halftones // FIXME: This is a ISOM/ISSOM rule, move from code to data!

And this:

// knockout for out-of-sequence colors

What is the latter intended for? This behavior seems a bit strange to me because, after disabling the knockout under halftones, the overprint of the half brown over the road borders depends on if the half brown is above or below the full brown, which has nothing to do with the black border.

Activating overprint simulation darkens the base white of the map background a bit, this can be seen when switching the setting, where only the bounding boxes of the map objects are darkened.

By the way, printing as one big image is already practiced in some cases because printing half transparent templates directly did not work (onto a PDF, in my case). Using the big image solved the problem. As long as

dg0yt commented 11 years ago
else if (component.factor < 1.0f && component.spot_color->getPriority() < spot_color->getPriority())
    // knockout under halftones
    // FIXME: This is a ISOM/ISSOM rule, move from code to data!

This rule fires e.g. while rendering the YELLOW separation when rendering comes to the "Green 30%" map color renderables. ISOM states that the overprinting effect is recommended for particular solid colors (100%). The color circles in the example (top right) demonstrate the effect of this rule.

This has nothing to do with halftones of the current spot color (such as Green 30% during GREEN separation) which are simply processed in the order of map colors. And it cannot deal with the black border of the road symbol, since BLACK has not been rendered at the moment when BROWN is processed.

else if (map_color->getPriority() < component.spot_color->getPriority())
    // knockout for out-of-sequence colors

The term "out-of-sequence color" was coined by me for the situation with the road colors. We have BROWN below "Black belor brown" below "Brown 50%" below BLACK, i.e. the colors in the middle are not in the same sequence as the spot colors. Since "Black below brown" is explicitly below the "Brown 50%" (which is printed first by spot color sequence) it cannot mean the normal overprinting. It is interpreted as black which has knockouts for items of spot colors which are printed before black, but have a map color of lower priority.

So this rule fires while rendering the BLACK separation when rendering comes to "Brown 50%" map color renderables, which are processed after the "Black below brown" map color renderables (for every separation).

Activating overprint simulation darkens the base white of the map background a bit,

I have noticed this, too. That's why I called it proof-of-concept... This particular issue is now solved by clearing the reused separation QImage with Qt::white instead of Qt::transparent.

There may be a few more subtle issues, like the thin green line at in the 411 "Forest runnable in one direction" symbol. For true spot color printing, it may require a different solution than for the simulation.

By the way, printing as one big image is already practiced in some cases because printing half transparent templates directly did not work (onto a PDF, in my case). Using the big image solved the problem. As long as

  • the dpi of the image matches that of the printer, and
  • the printer uses the RGB color space this should be equivalent to printing directly, is it?

I saw the code during my experimentation. I know that it works in many cases. But I see two issues:

Qt's PDF implementation is probably not sufficient for our purposes.

dg0yt commented 11 years ago

puzzlepaint posted on Sourceforge:

Thanks for the explanations! About the out-of-sequence colors: try moving brown on top of brown 50%. The black below browns is still out of order, but then it is overprinted normally. Wouldn't this fit your description better?

:::c++
else if (map_color->getPriority() > spot_color->getPriority())
    // knockout for out-of-sequence colors

The thin green line at the forest runable in one direction is an antialiasing problem which is also there without overprint simulation.

dg0yt commented 11 years ago

Diff:


--- old
+++ new
@@ -15,3 +15,4 @@
 * #52 Provide a way to adjust colors globally
 * #120 Color correction
 * #147 Color component editing is confusing
+* #169 Export of color separations for offset printing 
ghost commented 8 years ago

Is it possible implement "Knockout: erase upper colors"?

dg0yt commented 8 years ago

"Erase upper colors" would be possible, but it adds more complexity than benefits. It will certainly not be implemented in Mapper. "Knockout" is a term from (spot color) printing which I wouldn't use in that context.