Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support true 32-bit colors in game data and script API #1980

Closed
11 of 14 tasks
ivan-mogilko opened this issue Apr 16, 2023 · 15 comments
Closed
11 of 14 tasks

Support true 32-bit colors in game data and script API #1980

ivan-mogilko opened this issue Apr 16, 2023 · 15 comments
Assignees
Labels
ags 4 related to the ags4 development context: game logic context: graphics context: script api type: enhancement a suggestion or necessity to have something improved

Comments

@ivan-mogilko
Copy link
Contributor

ivan-mogilko commented Apr 16, 2023

Problem

Historically AGS stores a script color value in 16-bit format (iirc it is R5-G6-B5) in both 16-bit and 32-bit games. This causes obvious problems in 32-bit games:

  • you do not have access to a full range of colors when selecting a color in editor or script,
  • you do not have access to the alpha channel.

Besides that, colors with values 0-31 (this corresponds to lower blue hues in ARGB format) have special meaning: they are forced to refer to the palette indices 0-31, even in 32-bit games. The reason why this is done is, AFAIK, because certain hardcoded graphics in the engine use these palette slots to get drawn.

EDIT: upon quick check of the code, it's not the real game palette where these colors are taken from, but the special 32-slot hardcoded palette. So any changes to the 256-colors game palette do not affect these.
This also means that these 0-31 color indices are not "dynamic", they are always resolving to the same RGB values

Proposed changes

In brief, the goal is this:

  1. The color values in game properties and script variables are treated either as full 32-bit color values (ARGB8888) or 8-bit indexes depending on the context. If there's any conversion necessary to the game's native color depth (like 16-bit), this conversion is performed in the code as close to drawing as possible, and result stored in internal or temporary variables.
  2. If 16-bit is still supported, the Editor may provide a help in finding closest supported RGB color when editing color properties.
  3. 0-31 color values are no longer locked to the palette indices in anything beyond 8-bit games. If we need hardcoded color for any purpose, then in 16/32 bit games the RGB should be hardcoded instead, and assigned in accordance to the current color depth.
  4. In 32-bit games the alpha channel has a meaning. It may be set, and affects the drawing operations. NOTE: this might require an additional work to implement consistently, so may be picked out into separate tickets.

Further notes:

  1. Game.GetColorFromRGB is superceded or accompanied with Game.GetColorFromRGBA(r, g, b, a)
  2. To clarify: any int color value in script API in 32-bit games should reliably be A8R8G8B8, meaning that users may also just make color themselves using bitwise operations. This must be documented in the manual (same for 16-bit games, if it's not already).
  3. Must look out for any hardcoded color values set in the engine. Not sure what to suggest here to ease the search. These has to be replaced with something like GetHardcodedRGB(index) (or better name).
    EDIT: good start is to search for GetCompatibleColor() calls which have hardcoded numbers as arguments.
  4. After this is done, we might see to support alpha channel in all the primitive drawing operations (setpixel, line, rectangle etc) (see AGS4: Attribute DrawingSurface::DrawingTransparency #1514 where this was first proposed).

Upgrading game projects

Unfortunately, the "color" properties are stored as raw values in both the classes and the game project files (and not as RGBs in high/true color games). I think, that for better future compatibility, and readability, it may be better to store RGB values along. (Need to find a good form for serialization, maybe something like a comma separated values, and avoid oververbose xml format).

When importing older projects the color values will be automatically converted to RGB, except for 8-bit games.

Proposed order of changes

Editor:

  • 1. Game project data. Perhaps it's easier to keep simple decimal integer for now, as it already may store 32-bit ARGB. But it's may be an interesting idea to plan implementing a new type for storing color values that may store and read them back both as a human-readable ARGB (e.g as "0xAARRGGBB" or decimal "N,N,N,N") or 8-bit palette index. There's already AGSColor type, which perhaps may be revamped for this purpose.
  • 2. AGSColor should work with full ARGB values.
  • 3. In the editor properties we might still keep 2 properties (ColorNumber / ColorRGB), but imho it is worth planning merging these two into one for easier use and adding more "color" properties. Such property would allow entering both 8-bit index and ARGB value, optionally by selecting it on palette or color picker. This may be left for a separate task though, as may require coding a custom property editor (this may be merged with serialization format change described in p1).
  • 4. "Hardcoded" RGB colors (0-31 indexes) are no longer used anywhere in the Editor (except perhaps for the room areas), should not be suggested in the color panel, should not impose any restrictions on the property values. If there will be a need to configure these for the game and/or for the editor (displaying the masks), then this may be done in the future by adding a new property objects, perhaps along with the game palette, on a separate tab in Color node, and/or Editor's preferences (if it's a design-time setting). Latter should be a separate task.
  • 5. If/since we still support 16-bit game depth, for these games the RGB selector might adjust the selected color to the nearest supported 16-bit one (but color value is still stored as 32-bit ARGB!).
  • 6. All color properties in game have to be converted when importing older projects.
  • 7. Compiled runtime game data should have every color property stored as a 32-bit integer. Need to check if there are any color properties that are saved as short and adjust them to int, changing format as necessary.
    • InventoryHotspotMarker.DotColor
    • InventoryHotspotMarker.CrosshairColor

Engine:

  • 7. In the engine all color properties similarly should be ensured to be 32-bit ints.
  • 8. In the engine all color properties are assumed to be either full 32-bit ARGB or 8-bit indexes, and treated corresponding to the context. The color will be converted to the native game depth / bitmap's color depth using Bitmap::GetCompatibleColor() (as it is now) only for storing in the internal/temp variables, as close to the place of use in code as possible/convenient. Converted values ideally are not a part of any save format, and only stay in runtime memory (I think they are now, but need to double check).
  • 9. All the hardcoded colors (which are used as defaults, or where there's no game settings) should be applied using a helper function that would either return a 8-bit palette index or full ARGB value (RGB values taken from the existing table of hardcoded colors) and then passed into Bitmap::GetCompatibleColor() to ensure they are used exactly as if they did come from game data / script API.
  • 10. DrawingSurface.GetPixel/SetPixel and DrawingSurface.DrawingColor script API should support full 32-bit ARGB.
  • 11. All the drawing that has "color" setting should support alpha channel. This includes: clearing a surface, drawing primitives (lines, rects, etc), drawing GUI borders and backgrounds, drawing text. This may be moved to a separate task too.

Script API

  • 12. Extend Game.GetColorFromRGB(r,g,b) with an optional alpha value, which is 255 by default.
  • 13. Function which returns RGB for palette entry (weird there's none yet). This maybe will be useful to replace colors 0-31 in old projects, where they mean first 32 palette indexes at any game's color depth.
    EDIT: looks like this is not necessary, as there's a global import ColorType palette[PALETTE_SIZE];, where ColorType is a struct with RGB fields.
@ivan-mogilko ivan-mogilko added type: enhancement a suggestion or necessity to have something improved ags 4 related to the ags4 development context: script api context: game logic labels Apr 16, 2023
@ivan-mogilko ivan-mogilko added this to the 4.0.0 (preliminary) milestone Apr 16, 2023
@AlanDrake
Copy link
Contributor

AlanDrake commented Apr 16, 2023

  1. The color values are stored in accordance to the game's color depth. 8-bit games have 1-byte palette indices, 16-bit games have classic 16-bit RGB (R5-G6-B5), and 32-bit has a true ARGB (A8-R8-G8-B8).

I would suggest to make AGS4 games 32-bit color depth only.
16bit is a dead format, there is no point in making a game with such a restriction, it makes absolutely no sense.
8bit is only cool for palette effects, but virtually nobody bothers these days. There should be better solutions than making the whole game 8bit.

  1. Game.GetColorFromRGB is superceded or accompanied with Game.GetColorFromRGBA(r, g, b, a)

It would be fine to have both, we could even move color related functions into a Color class.
Color.FromRGB, Color.FromRGBA, Color.FromRGB16, Color.FromHSV, etc.
But I should probably explore that idea as a module first.

  1. Must look out for any hardcoded color values set in the engine. Not sure what to suggest here to ease the search. These has to be replaced with something like GetHardcodedRGB(index) (or better name).

Maybe a FromLegacyColor() or similar?

(Need to find a good form for serialization, maybe something like a comma separated values, and avoid oververbose xml format).

Either "255,255,255,0" or "#FFFFFF00" (with or without the #)

@ivan-mogilko
Copy link
Contributor Author

ivan-mogilko commented Apr 16, 2023

16bit is a dead format, there is no point in making a game with such a restriction, it makes absolutely no sense.
8bit is only cool for palette effects, but virtually nobody bothers these days. There should be better solutions than making the whole game 8bit.

Both formats have at least the purpose of saving disk and runtime memory.
For example, one person was recreating very old games in AGS where the player can select a palette to play in (this game was made in 2020). Without 8-bit support that would make the size of the game many times larger. To do the same with 32-bit, AGS would have to support custom shaders first.
Paletted gfx is also a form of restricting yourself to a limited range of colors.

At the same time, what is the real downside of keeping these formats?

EDIT: Overall, I suspect, only few people are using these formats, like 8-bit, because AGS does not promote them. There are engines out there, like PICO-8, that specifically promote restrictions. I genuinely wonder if number of people trying to make 8-bit gfx games would increase if AGS was advertising this mode.

EDIT2: In any case, I would not want to do format removal along in this task, I'd prefer to have it as a separate matter.

Must look out for any hardcoded color values set in the engine. Not sure what to suggest here to ease the search. These has to be replaced with something like GetHardcodedRGB(index) (or better name).

Maybe a FromLegacyColor() or similar?

Well, it's not legacy color, it's a hardcoded color still used for certain purposes which do not have any exposed settings, like drawing a built-in dialog window, or default dialog options color, etc (I do not remember them all).

Game.GetColorFromRGB is superceded or accompanied with Game.GetColorFromRGBA(r, g, b, a)

Actually, to save on api entries, Game.GetColorFromRGB may be expanded with a argument, with default value of 255.

@ivan-mogilko
Copy link
Contributor Author

Oh, something I forgot to mention. 8-bit images and thus 8-bit color values are currently used on masks, so they cannot be thrown out.

@AlanDrake
Copy link
Contributor

I don't want to throw out 8bit images, just 8bit game projects if it reduces the complexity of code.

Mask colors are hardcoded too, but with extra hacks to prevent certain colors.

@ivan-mogilko
Copy link
Contributor Author

Mask colors are hardcoded too, but with extra hacks to prevent certain colors.

What do you mean? Mask colors just are 0-255 indexes.

@AlanDrake
Copy link
Contributor

Mask colors are hardcoded too, but with extra hacks to prevent certain colors.

What do you mean? Mask colors just are 0-255 indexes.

Maybe I'm remembering the part used in the editor, but there was a piece of code with hardcoded values up to 31, some whites are turned into red, and above 31 is all red.
Now that you make me think of it I never checked what happens ingame while displaying masks with the colors that would be replaced.

@ivan-mogilko
Copy link
Contributor Author

ivan-mogilko commented Apr 16, 2023

Maybe I'm remembering the part used in the editor, but there was a piece of code with hardcoded values up to 31, some whites are turned into red, and above 31 is all red.

No, that is not related to masks, I explained what it is in the ticket's description.

@ivan-mogilko
Copy link
Contributor Author

ivan-mogilko commented Apr 16, 2023

In any case, I would not want to do any format/feature removal along in this task. I'd prefer if it's considered a separate matter.

Currently I don't think that presence of alternate formats will make it difficult to implement 32-bit color values support.

@ivan-mogilko
Copy link
Contributor Author

ivan-mogilko commented Apr 20, 2023

The color values are stored in accordance to the game's color depth. 8-bit games have 1-byte palette indices, 16-bit games have classic 16-bit RGB (R5-G6-B5), and 32-bit has a true ARGB (A8-R8-G8-B8).

Upon more thinking, perhaps it would be better to store color properties simply as 32-bit ARGB or 8-bit index (depending on the context). It will be much easier that way for both users and doing any kind of analysis in the engine. It may still be converted to 16-bit or else when applying to a drawing operation (it's done using fast bitwise operations).

EDIT: amended the first post with proposed order of changes.

@AlanDrake
Copy link
Contributor

Sounds good to me.

@ivan-mogilko
Copy link
Contributor Author

ivan-mogilko commented Apr 20, 2023

An interesting problem of transparent color:

Right now, when the engine loads 32-bit sprites, it replaces all fully transparent pixels (w alpha 0) with standard AGS COLOR_TRANSPARENT (0x00FF00FF). This is done for compatibility, and also to let users draw and check for transparent pixels with DrawingSurface.

If we want a full ARGB support, we can no longer do that conversion, because even fully transparent pixel values may have a meaning.

But then users will no longer be able to check for transparency by comparing pixels with COLOR_TRANSPARENT. They will have to check the alpha channel (upper 8 bits of integer) instead. That is - in generic case. Of course if they draw the image themselves they might still use COLOR_TRANSPARENT,

@AlanDrake
Copy link
Contributor

They could keep comparing COLOR_TRANSPARENT, if they've drawn it with COLOR_TRANSPARENT. Obviously imported images may have other colors hidden in the RGB fields, so we may add a note somewhere about it.
I imagine COLOR_TRANSPARENT will have a different value depending whether it's compiled in a 8bit or 32bit game.

@ivan-mogilko
Copy link
Contributor Author

ivan-mogilko commented Aug 27, 2024

Opened a PR here: #2501
mostly seems to work, but still has few minor issues.

EDIT: ready for review now.
The two things that I did not do are:

  • support for alpha channel in color values.
  • making more convenient Color properties (like merging Color and ColorNumber properties together in a single field).
    These may be done as separate tasks.

@ivan-mogilko
Copy link
Contributor Author

I posted this in comments to #2501, but might repeat here:

I've been wondering, would that make sense to handle existing duality of Color / ColorNumber properties by hiding one of this pair depending on the current game's color depth? That is - display ColorNumber if game is 8-bit and Color (rgb) if game is 32-bit.

But then, it would also be nice to have color displayed on ColorNumber field. Maybe even add a [...] ColorNumber property which opens a palette for selecting a color.

Alternatively, how feasible that would be to merge Color and ColorNumber properties together?
Only ColorNumber is serialized, and Color (rgb) field is there only for editing at design time. This makes it possible to remove one, rename ColorNumber's "display name" to just Color. But the biggest question is whether it would be possible to change the looks and functionality of the field depending on game settings. Supposedly the field may have a custom editor attribute, that draws the field and provides [...] button depending on some condition.

@ivan-mogilko
Copy link
Contributor Author

Resolved most subtasks from the list in #2501, the rest should be moved to separate tasks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ags 4 related to the ags4 development context: game logic context: graphics context: script api type: enhancement a suggestion or necessity to have something improved
Projects
None yet
Development

No branches or pull requests

2 participants