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

Issue #598: ASAN crashes when drawing sprites #601

Merged
merged 1 commit into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions LATEST_RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Bugs addressed in this release:

* [#593](../../issues/593) Are the draw colors for selected and reference inverted on the drawing setup
* [#598](../../issues/598) ASAN crashes when drawing sprites

Other changes:

Expand Down
11 changes: 7 additions & 4 deletions src/AnimationView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ AnimationView::~AnimationView()
{
}

void AnimationView::RegenerateImages() const
void AnimationView::RegenerateImages()
{
auto spriteScale = mConfig.Get_SpriteBitmapScale();
if (spriteScale == mScaleSize) {
Expand All @@ -93,7 +93,10 @@ void AnimationView::RegenerateImages() const
constexpr auto image_Y = 128;
auto images = GenerateSpriteImages(image, mSpriteCalChartImages.size(), image_X, image_Y, mScaleSize);
std::transform(images.begin(), images.end(), mSpriteCalChartImages.begin(), [](auto&& image) {
return std::make_shared<CalChart::ImageData>(wxCalChart::ConvertToImageData(image));
return BitmapSize_t{ std::make_shared<wxCalChart::BitmapHolder>(image), wxCalChart::toCoord(image.GetSize()) };
});
std::transform(images.begin(), images.end(), mSelectedSpriteCalChartImages.begin(), [](auto&& image) {
return BitmapSize_t{ std::make_shared<wxCalChart::BitmapHolder>(image.ConvertToGreyscale()), wxCalChart::toCoord(image.GetSize()) };
});
}

Expand All @@ -111,9 +114,9 @@ void AnimationView::OnDraw(wxDC* dc)
mCurrentBeat,
mDrawCollisionWarning,
onBeat,
[this](CalChart::Radian angle, CalChart::Animation::ImageBeat imageBeat) {
[this](CalChart::Radian angle, CalChart::Animation::ImageBeat imageBeat, bool selected) {
auto imageIndex = CalChart::AngleToQuadrant(angle) + CalChart::toUType(imageBeat) * 8;
return mSpriteCalChartImages[imageIndex];
return selected ? mSelectedSpriteCalChartImages[imageIndex] : mSpriteCalChartImages[imageIndex];
}));
}

Expand Down
12 changes: 8 additions & 4 deletions src/AnimationView.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@

class AnimationPanel;
class CalChartView;
namespace wxCalChart {
struct BitmapHolder;
}
namespace CalChart {
class Configuration;
class Continuity;
Expand Down Expand Up @@ -88,7 +91,7 @@ class AnimationView : public wxView {
void Generate();
void RefreshFrame();

void RegenerateImages() const;
void RegenerateImages();
[[nodiscard]] auto GenerateDraw(CalChart::Configuration const& config) const -> std::vector<CalChart::Draw::DrawCommand>;
[[nodiscard]] auto GenerateDrawSprites(CalChart::Configuration const& config, CalChart::Animation::AngleStepToImageFunction imageFunction) const -> std::vector<CalChart::Draw::DrawCommand>;

Expand All @@ -104,9 +107,10 @@ class AnimationView : public wxView {
bool mPlayCollisionWarning = false;

static constexpr auto kAngles = 8;
// these need to be mutable because effectively they are a cache and may need to be regenerated.
mutable double mScaleSize = 0;
mutable std::array<std::shared_ptr<CalChart::ImageData>, kAngles * CalChart::toUType(CalChart::Animation::ImageBeat::Size)> mSpriteCalChartImages;
double mScaleSize = 0;
using BitmapSize_t = std::tuple<std::shared_ptr<wxCalChart::BitmapHolder>, CalChart::Coord>;
std::array<BitmapSize_t, kAngles * CalChart::toUType(CalChart::Animation::ImageBeat::Size)> mSpriteCalChartImages;
std::array<BitmapSize_t, kAngles * CalChart::toUType(CalChart::Animation::ImageBeat::Size)> mSelectedSpriteCalChartImages;

CalChart::MeasureDuration mMeasure;
};
58 changes: 32 additions & 26 deletions src/BackgroundImages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,24 @@

class BackgroundImage {
public:
BackgroundImage(wxImage const& image, int x, int y, int scaled_width, int scaled_height);
explicit BackgroundImage(CalChart::ImageInfo const& image);

bool MouseClickIsHit(wxMouseEvent const& event, wxDC const& dc) const;
[[nodiscard]] auto MouseClickIsHit(wxMouseEvent const& event, wxDC const& dc) const -> bool;
void OnMouseLeftDown(wxMouseEvent const& event, wxDC const& dc);

// returns left, top, width, height
std::array<int, 4> OnMouseLeftUp(wxMouseEvent const& event, wxDC const& dc);
[[nodiscard]] auto OnMouseLeftUp(wxMouseEvent const& event, wxDC const& dc) -> std::array<int, 4>;

void OnMouseMove(wxMouseEvent const& event, wxDC const& dc);
void OnPaint(wxDC& dc, bool drawPicAdjustDots, bool selected) const;

private:
static constexpr auto kCircleSize = 6;

wxImage mImage;
CalChart::Coord mPosition{};
CalChart::Coord mScaledSize{};
wxImage mRawImage;
wxBitmap mBitmap;
wxPoint mBitmapPoint;

// what type of background adjustments could we do
enum class BackgroundAdjustType {
Expand All @@ -60,7 +62,7 @@ class BackgroundImage {
};
using BackgroundAdjustTypeIterator = CalChart::Iterator<BackgroundAdjustType, BackgroundAdjustType::kUpperLeft, BackgroundAdjustType::kLowerRight>;
BackgroundAdjustType mBackgroundAdjustType;
BackgroundAdjustType WhereIsMouseClick(const wxMouseEvent& event, const wxDC& dc) const;
[[nodiscard]] auto WhereIsMouseClick(const wxMouseEvent& event, const wxDC& dc) const -> BackgroundAdjustType;

static auto toOffsetX(BackgroundAdjustType where)
{
Expand Down Expand Up @@ -103,16 +105,16 @@ class BackgroundImage {
float mAspectRatio;
BackgroundAdjustType mAdjustType;
};
std::unique_ptr<CalculateScaleAndMove> mScaleAndMove;
std::optional<CalculateScaleAndMove> mScaleAndMove;
};

BackgroundImage::BackgroundImage(const wxImage& image, int x, int y, int scaled_width, int scaled_height)
: mImage(image)
, mBitmapPoint(x, y)
, // always adjust when we get created
mBackgroundAdjustType(BackgroundAdjustType::kLast)
BackgroundImage::BackgroundImage(CalChart::ImageInfo const& image)
: mPosition{ image.left, image.top }
, mScaledSize{ image.scaledWidth, image.scaledHeight }
, mRawImage{ wxCalChart::towxImage(image.data) }
, mBitmap{ mRawImage.Scale(mScaledSize.x, mScaledSize.y, wxIMAGE_QUALITY_HIGH) }
, mBackgroundAdjustType(BackgroundAdjustType::kLast) // always adjust when we get created
{
mBitmap = wxBitmap(mImage.Scale(scaled_width, scaled_height, wxIMAGE_QUALITY_HIGH));
}

BackgroundImage::BackgroundAdjustType BackgroundImage::WhereIsMouseClick(const wxMouseEvent& event, const wxDC& dc) const
Expand All @@ -122,10 +124,10 @@ BackgroundImage::BackgroundAdjustType BackgroundImage::WhereIsMouseClick(const w
auto y = dc.DeviceToLogicalY(point.y);

// where are we?
auto bitmapSize = mBitmap.GetSize();
auto middle = mBitmapPoint + bitmapSize / 2;
auto bitmapSize = wxCalChart::toSize(mScaledSize);
auto middle = wxCalChart::toPoint(mPosition) + bitmapSize / 2;
for (auto where : BackgroundAdjustTypeIterator()) {
if (where == BackgroundAdjustType::kMove && wxRect{ mBitmapPoint, mBitmap.GetSize() }.Contains(x, y)) {
if (where == BackgroundAdjustType::kMove && wxRect{ wxCalChart::toPoint(mPosition), wxCalChart::toSize(mScaledSize) }.Contains(x, y)) {
return where;
}
auto offsetX = toOffsetX(where);
Expand Down Expand Up @@ -160,15 +162,17 @@ void BackgroundImage::OnMouseLeftDown(wxMouseEvent const& event, wxDC const& dc)
return;
}
mBackgroundAdjustType = where;
mScaleAndMove = std::make_unique<CalculateScaleAndMove>(wxPoint{ x, y }, wxRect{ mBitmapPoint, mBitmap.GetSize() }, mBackgroundAdjustType);
mScaleAndMove = { wxPoint{ x, y }, wxRect{ wxCalChart::toPoint(mPosition), wxCalChart::toSize(mScaledSize) }, mBackgroundAdjustType };
}

std::array<int, 4> BackgroundImage::OnMouseLeftUp(const wxMouseEvent&, const wxDC&)
{
if (mScaleAndMove) {
// done moving, lock down the picture and make it pretty:
mBitmap = wxBitmap(mImage.Scale(mBitmap.GetWidth(), mBitmap.GetHeight(), wxIMAGE_QUALITY_HIGH));
std::array<int, 4> data{ { mBitmapPoint.x, mBitmapPoint.y, mBitmap.GetWidth(), mBitmap.GetHeight() } };
mBitmap = wxBitmap(mRawImage.Scale(mBitmap.GetWidth(), mBitmap.GetHeight(), wxIMAGE_QUALITY_HIGH));
auto [width, height] = wxCalChart::toSize(mScaledSize);
auto [x, y] = wxCalChart::toPoint(mPosition);
std::array<int, 4> data{ { x, y, width, height } };
mScaleAndMove.reset();
mBackgroundAdjustType = BackgroundAdjustType::kLast;
return data;
Expand All @@ -183,19 +187,21 @@ void BackgroundImage::OnMouseMove(const wxMouseEvent& event, const wxDC& dc)
auto y = dc.DeviceToLogicalY(point.y);

if (event.Dragging() && event.LeftIsDown() && mScaleAndMove) {
auto rect = (*mScaleAndMove)(x, y, { mBitmapPoint, mBitmap.GetSize() });
mBitmapPoint = rect.GetPosition();
mBitmap = wxBitmap(mImage.Scale(rect.width, rect.height));
auto rect = (*mScaleAndMove)(x, y, { wxCalChart::toPoint(mPosition), wxCalChart::toSize(mScaledSize) });
mPosition = wxCalChart::toCoord(rect.GetPosition());
mScaledSize = wxCalChart::toCoord(rect.GetSize());
mBitmap = wxBitmap(mRawImage.Scale(mScaledSize.x, mScaledSize.y));
}
}

void BackgroundImage::OnPaint(wxDC& dc, bool drawPicAdjustDots, bool selected) const
{
dc.DrawBitmap(mBitmap, mBitmapPoint.x, mBitmapPoint.y, true);
auto pos = wxCalChart::toPoint(mPosition);
dc.DrawBitmap(mBitmap, pos.x, pos.y, true);
if (drawPicAdjustDots) {
// draw guide dots
auto bitmapSize = mBitmap.GetSize();
auto middle = mBitmapPoint + bitmapSize / 2;
auto bitmapSize = wxCalChart::toSize(mScaledSize);
auto middle = wxCalChart::toPoint(mPosition) + bitmapSize / 2;
dc.SetBrush(*wxBLUE_BRUSH);
dc.SetPen(*wxBLUE_PEN);
for (auto where : BackgroundAdjustTypeIterator()) {
Expand Down Expand Up @@ -320,7 +326,7 @@ void BackgroundImages::SetBackgroundImages(std::vector<CalChart::ImageInfo> cons
mBackgroundImages.clear();
mWhichBackgroundIndex = -1;
for (auto&& image : images) {
mBackgroundImages.emplace_back(wxCalChart::ConvertTowxImage(image.data), image.left, image.top, image.scaled_width, image.scaled_height);
mBackgroundImages.emplace_back(image);
}
}

Expand Down
86 changes: 78 additions & 8 deletions src/CalChartDrawPrimativesHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
* Helper functions for creating wxWidgets from drawing primatives
*/

#include "CalChartDrawCommand.h"
#include "CalChartDrawPrimatives.h"
#include "CalChartImage.h"
#include "CalChartSizes.h"
Expand All @@ -45,6 +46,26 @@ namespace wxCalChart {

inline auto make_wxSize(CalChart::Coord coord) { return wxSize{ coord.x, coord.y }; }

inline auto toSize(CalChart::Coord size) -> wxSize
{
return { size.x, size.y };
}

inline auto toPoint(CalChart::Coord point) -> wxPoint
{
return { point.x, point.y };
}

inline auto toCoord(wxSize size) -> CalChart::Coord
{
return { size.x, size.y };
}

inline auto toCoord(wxPoint point) -> CalChart::Coord
{
return { point.x, point.y };
}

inline auto toColour(CalChart::Color::ColorRGB c) -> wxColour
{
return { c.red, c.green, c.blue, c.alpha };
Expand Down Expand Up @@ -232,6 +253,47 @@ inline auto setFont(wxDC& dc, CalChart::Font font)
dc.SetFont(wxFont(size, family, style, weight));
}

// Creates a deep copy the image data
inline auto towxImage(CalChart::ImageData const& image) -> wxImage
{
auto data = std::unique_ptr<unsigned char, void (*)(void*)>{
static_cast<unsigned char*>(std::malloc(image.data.size())),
[](void* p) { std::free(p); }
};
std::copy(image.data.begin(), image.data.end(), data.get());
if (image.alpha.empty()) {
return wxImage{ image.width, image.height, data.release() };
}
auto alpha = std::unique_ptr<unsigned char, void (*)(void*)>{
static_cast<unsigned char*>(std::malloc(image.alpha.size())),
[](void* p) { std::free(p); }
};

std::copy(image.alpha.begin(), image.alpha.end(), alpha.get());
return wxImage{ image.width, image.height, data.release(), alpha.release() };
}

struct BitmapHolder : CalChart::Draw::OpaqueImageData {
BitmapHolder(wxImage const& image)
: bitmap(image)
{
}
~BitmapHolder() override = default;
BitmapHolder(CalChart::ImageData const& image)
{
wxImage converted = [](CalChart::ImageData const& image) {
auto data = image.data;
if (image.alpha.size()) {
auto alpha = image.alpha;
return wxImage{ image.width, image.height, data.data(), alpha.data(), true };
}
return wxImage{ image.width, image.height, data.data(), true };
}(image);
bitmap = converted;
}
wxBitmap bitmap;
};

inline auto ConvertToImageData(wxImage const& image) -> CalChart::ImageData
{
auto width = image.GetWidth();
Expand All @@ -246,7 +308,7 @@ inline auto ConvertToImageData(wxImage const& image) -> CalChart::ImageData
std::copy(a, a + width * height, alpha.data());
}

return { width, height, data, alpha };
return { width, height, data, alpha, std::make_shared<BitmapHolder>(image) };
}

inline auto ConvertToImageInfo(wxImage const& image, int x = 0, int y = 0) -> CalChart::ImageInfo
Expand All @@ -256,13 +318,21 @@ inline auto ConvertToImageInfo(wxImage const& image, int x = 0, int y = 0) -> Ca
return CalChart::ImageInfo{ x, y, width, height, ConvertToImageData(image) };
}

inline auto ConvertTowxImage(CalChart::ImageData const& image) -> wxImage
inline auto ConvertTowxBitmap(CalChart::Draw::Image const& image) -> wxBitmap
{
auto data = image.data;
if (image.alpha.size()) {
auto alpha = image.alpha;
return { image.image_width, image.image_height, data.data(), alpha.data(), true };
}
return { image.image_width, image.image_height, data.data(), true };
// visit
return std::visit(
CalChart::overloaded{
[](std::shared_ptr<CalChart::Draw::OpaqueImageData> data) {
return dynamic_cast<BitmapHolder&>(*data).bitmap;
},
[](std::shared_ptr<CalChart::ImageData> data) {
if (data->render == nullptr) {
data->render = std::make_shared<BitmapHolder>(*data);
}
return dynamic_cast<BitmapHolder&>(*(data->render)).bitmap;
},
},
image.mImage);
}
}
5 changes: 1 addition & 4 deletions src/CalChartDrawing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -736,10 +736,7 @@ namespace details {
},
[&dc, layoutPoint = surface.origin](CalChart::Draw::Image const& c) {
auto where = wxCalChart::to_wxPoint(c.mStart) + layoutPoint;
auto image = wxCalChart::ConvertTowxImage(*c.mImage);
if (c.mGreyscale) {
image = image.ConvertToGreyscale();
}
auto image = wxCalChart::ConvertTowxBitmap(c);
dc.DrawBitmap(image, fDIP(where));
},
[]([[maybe_unused]] CalChart::Draw::Ignore const& c) {
Expand Down
12 changes: 10 additions & 2 deletions src/CalChartDrawingGetMinSize.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,16 @@ namespace details {

inline auto GetMinSize([[maybe_unused]] Context context, CalChart::Draw::Image const& image) -> StackSize
{
// TODO: should this have scale in it?
return wxSize(image.mImage->image_width, image.mImage->image_width);
return std::visit(
CalChart::overloaded{
[](std::shared_ptr<CalChart::ImageData> data) {
return wxSize(data->width, data->width);
},
[](std::shared_ptr<CalChart::Draw::OpaqueImageData> data) {
return dynamic_cast<wxCalChart::BitmapHolder&>(*data).bitmap.GetSize();
},
},
image.mImage);
}

inline auto GetMinSize(Context context, CalChart::Draw::DrawManipulators const& cmd) -> StackSize;
Expand Down
6 changes: 3 additions & 3 deletions src/core/CalChartAnimation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,11 @@ auto Animation::GenerateSpritesDrawCommands(beats_t whichBeat, SelectionList con
}
return *onBeat ? ImageBeat::Left : ImageBeat::Right;
}();
auto image = imageFunction(info.mMarcherInfo.mFacingDirection, image_offset);
auto [image, size] = imageFunction(info.mMarcherInfo.mFacingDirection, image_offset, selectionList.contains(index));
auto position = info.mMarcherInfo.mPosition;
auto offset = CalChart::Coord(image->image_width * comp_X, image->image_height * comp_Y);
auto offset = CalChart::Coord(size.x * comp_X, size.y * comp_Y);

return CalChart::Draw::Image{ position, image, selectionList.contains(index) } - offset;
return CalChart::Draw::Image{ position, image } - offset;
}));
return drawCmds;
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/CalChartAnimation.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class Animation {
Right,
Size
};
using AngleStepToImageFunction = std::function<std::shared_ptr<ImageData>(Radian, ImageBeat)>;
using AngleStepToImageFunction = std::function<std::tuple<std::shared_ptr<Draw::OpaqueImageData>, CalChart::Coord>(Radian, ImageBeat, bool)>;

// Drawing commands
[[nodiscard]] auto GenerateDotsDrawCommands(beats_t whichBeat, SelectionList const& selectionList, bool drawCollisionWarning, Configuration const& config) const -> std::vector<CalChart::Draw::DrawCommand>;
Expand Down
Loading