Prior to this fix, the following particular conditions caused
an incorrect conversion of an opaque color to a transparent color
during RGBA->Indexed conversion:
- RGBA image with black color (#000000 a=255) painted on the canvas
- The black color is absent from the palette.
- The mask color is present in the palette
- Converts the sprite to indexed color mode using
Sprite > Color Mode > More Options
- Select Advanced Options, select a Color Best Fit Criteria other than
Default (for example CIELAB) and press OK
- The original black color becomes the mask color.
This regression came from 09bb5cc3d3676e88048d5cd845b075ea30ca8e66 as
now we don't Sprite::setTransparentColor() on each undo/redo and only
when needed. This brought a new kind of error where the mask color for
images was set to -1 after convert_pixel_format().
This also fixes a conversion from Indexed -> RGB where the transparent
color was not set back to 0. And the transparent color is always set
when we are in indexed mode to avoid any assert in debug mode.
Original issue title: When using a background layer, switching to
Indexed Color Mode fills all layer bounding rectangles with
Color 0.
Conditions to reproduce the original issue:
- Opaque RGBA sprite, i.e. the bottom layer is 'Background'.
- There is a second layer with an ellipse (for example).
- There is a mask color #000000 alpha=0 is in the palette.
- The mask color index is greater and not equal than 0.
- Go to Sprite > Color Mode > Indexed.
Result: the transparent color of the second layer will change to
index color = 0 (usually black).
Also added test for RGBA->INDEXED conversion
Added other color comparison criterias (fit criteria) during
color mode conversion RGBA to Indexed or Grayscale to Indexed.
The 'fit criteria' will help us to recolor an RGB image with
a limited color palette taking into account different color
perception criteria (color spaces: RGB, linearized RGB,
CIE XYZ, CIE LAB).
Before this fix, a multi-layer mask movement/scaling (with mixed layer
types: normal layer and tilemap layers with different grids) caused
loss of drawing areas.
The heart of this solution is to correctly align the 'selection mask'
and 'transform data' according to the layer's grid, and also, forcing
'site' TilemapMode/TilesetMode before each
reproduceAllTransformationsWithInnerCmds() iteration.
During the life of a PixelMovement object there is a tilemap mode lock.
Additionally arrow keys now work to move a selected area in
TilemapMode::Tiles.
This fixes a violation of module layers (circular dependency)
introduced in e6cd13d7e13775de2a687e55e59819cb7f19e7e9 where doc-lib
started to depend on app-lib (which cannot happen, as app-lib depends
on doc-lib).
This DocFormat/SerialFormat was used only from app-lib previously, but
when properties were introduced in user data, the serialization format
version was needed to read user properties too. So now it makes sense
to move this type/its values to the doc-lib.
A couple of extra issues were found in this refactor:
1) The recursive call inside read_layer() didn't receive this "serial"
argument
2) read_grid() doesn't need the setId parameter
The code was refactored moving the BlenderHelper class from "render"
to "doc", and now doc::blend_image() supports blending different color
modes.
Some work is still needed to work with grayscale images correctly.
Only in debug mode with TEST_BACKUP_INTEGRITY flag, we now don't
detect as a layer diff between the doc in memory and the restore doc
from backup if only some layer flags like visibility/collapse changed.
This refactor includes:
- In Lua now we can clone a custom brush with Brush(Image) and the new
brush doesn't share the image with the original one (added a new test
for this).
- Avoid creating extra images when it's not needed using
Brush::cloneWithExistingImages() (we can inject existing images in
the brush itself).
- Delete Brush-copy contructor & assign operator to use
Brush::clone() functions instead (which are more explicit).
- Some code from 12d81352647e96c8ac6d70e4a252c37ce5a29ade (#4023)
reverted to avoid recreating brushes on left-click or in the brush
preview, i.e. moving the mouse (#4013 refers only to right-click, so
only on right-click we have to adjust the custom brush).
Before this fix to change the main color of the image brush,
it was necessary to choose a new color from the palette.
The secondary color can now also be used.
Also added some tests for image brushes.
Fix regression introduced in aeeef8e255c2d83a1b3e3cb2858939233d5a5d0b
when we replaced the std::vector with aligned memory allocations (or
just malloc/free).
Without this when the playback cue is on the highest frame in "PlayAll"
mode it doesn't take into account the direction of the current tag that
is being played and assumes the end of the animation was reached
This is due zlib returning Z_OK (instead of Z_STREAM_END) after
inflate() when all the output buffer was filled (avail_out = 0) but it
reports like there is still available uncompressed data (avail_in > 0).
It makes no sense but an extra inflate() call with avail_out=0
consumes the whole avail_in and the expected Z_STREAM_END is finally
reported.
This already fixes a lot of possible problems that can happen when a
script is running and modifying some part of a sprite that is being
backed up in a background thread.
We still need some work to being able to lock a sprite two or more
times in the same thread to write it. E.g. an app.transaction() should
lock the sprite for write access, but the script transaction function
could call a command, and that command could use a ContextWriter to
lock the sprite again. At the moment this is not possible because we
need a re-entrant RWLock implementation.
We are not sure, but due to new bug reports about a lagging mouse
movement (#4174), it might be because some memory cache issues: having
the rows array at the end of the pixels data might not be the best
decision.
It was moved at the end because we didn't need that rows array to be
aligned, only the pixels data. But with this patch we're trying to see
if this fixes the issue. So now we moved back the rows array at the
beginning of the image buffer as it was before aeeef8e255c2d83a1b3e3cb2858939233d5a5d0b
By default Aseprite will not try to match flipped versions of the
tiles (as it requires more CPU), but when we create a tileset we can
specify which flips can be matched automatically (new
Tileset::matchFlags() property).
These flags are just for the Auto mode, if we manually insert a
flipped tile, that is always supported, even when the matchFlags() are
not specified.
* Added a new app::Color type for tiles, to store the flags of the
picked tile.
* Fixed color bar/status bar with a new draw_tile() to draw tiles
with flip flags.
* Changed the "90cw" flag to "diagonal flip" (the tile should be
rendered with X/Y axis switched in this case)
* Each time we read/write an .aseprite file we have to convert
the mask/shift from the file to the values expected in
memory (tile_f_xflip/yflip/dflip)