Image cropping added

This commit is contained in:
Yousef Khadadeh 2026-01-06 12:36:15 +03:00
parent df7ec9cb99
commit c15a21e488
7 changed files with 91 additions and 20 deletions

View file

@ -1,32 +1,51 @@
#include "ImageCanvasComponent.hpp"
#include "Eigen/Core"
#include "Helpers/ConvertVec.hpp"
#include "Helpers/MathExtras.hpp"
#include "Helpers/SCollision.hpp"
#include "../SharedTypes.hpp"
#include "../World.hpp"
#include "../MainProgram.hpp"
#include <include/core/SkBlendMode.h>
#include <include/core/SkSamplingOptions.h>
#include "../DrawCollision.hpp"
#include "CanvasComponentContainer.hpp"
SkRect get_cropped_rectangle(const Vector2f& p1, const Vector2f& p2, const Vector2f& cropP1, const Vector2f& cropP2) {
float pWidth = p2.x() - p1.x();
float pHeight = p2.y() - p1.y();
return SkRect::MakeLTRB(p1.x() + pWidth * cropP1.x(), p1.y() + pHeight * cropP1.y(), p1.x() + pWidth * cropP2.x(), p1.y() + pHeight * cropP2.y());
}
SkRect rect_from_points(const Vector2f& p1, const Vector2f& p2) {
return SkRect::MakeLTRB(p1.x(), p1.y(), p2.x(), p2.y());
}
CanvasComponentType ImageCanvasComponent::get_type() const {
return CanvasComponentType::IMAGE;
}
void ImageCanvasComponent::save(cereal::PortableBinaryOutputArchive& a) const {
a(d.p1, d.p2, d.imageID);
a(d.editing, d.p1, d.p2, d.imageID, d.cropP1, d.cropP2);
}
void ImageCanvasComponent::load(cereal::PortableBinaryInputArchive& a) {
a(d.p1, d.p2, d.imageID);
a(d.editing, d.p1, d.p2, d.imageID, d.cropP1, d.cropP2);
}
void ImageCanvasComponent::save_file(cereal::PortableBinaryOutputArchive& a) const {
a(d.p1, d.p2, d.imageID);
a(d.p1, d.p2, d.imageID, d.cropP1, d.cropP2);
}
void ImageCanvasComponent::load_file(cereal::PortableBinaryInputArchive& a, VersionNumber version) {
a(d.p1, d.p2, d.imageID);
if(version >= VersionNumber(0, 4, 0))
a(d.cropP1, d.cropP2);
else {
d.cropP1 = Vector2f{0.0f, 0.0f};
d.cropP2 = Vector2f{1.0f, 1.0f};
}
d.editing = false;
}
std::unique_ptr<CanvasComponent> ImageCanvasComponent::get_data_copy() const {
@ -46,7 +65,7 @@ void ImageCanvasComponent::draw_download_progress_bar(SkCanvas* canvas, const Dr
p.setStrokeWidth(10.0f);
p.setStrokeCap(SkPaint::kRound_Cap);
p.setColor4f(drawData.main->toolbar.io->theme->fillColor1);
Vector2f center = convert_vec2<Vector2f>(imRect.center());
Vector2f center = (d.p1 + d.p2) * 0.5f;
const float DOWNLOAD_ARC_RADIUS = 50.0f;
canvas->drawArc(SkRect::MakeLTRB(center.x() - DOWNLOAD_ARC_RADIUS, center.y() - DOWNLOAD_ARC_RADIUS, center.x() + DOWNLOAD_ARC_RADIUS, center.y() + DOWNLOAD_ARC_RADIUS), 0.0f, progress * 360.0f, false, p);
@ -69,10 +88,37 @@ void ImageCanvasComponent::get_used_resources(std::unordered_set<NetworkingObjec
void ImageCanvasComponent::draw(SkCanvas* canvas, const DrawData& drawData) const {
ResourceDisplay* display = drawData.rMan->get_display_data(d.imageID);
if(display)
display->draw(canvas, drawData, imRect);
else
canvas->drawRect(imRect, SkPaint({0.5f, 0.5f, 0.5f, 0.5f}));
if(d.editing) {
canvas->saveLayerAlphaf(nullptr, 1.0f);
display->draw(canvas, drawData, rect_from_points(d.p1, d.p2));
canvas->saveLayerAlphaf(nullptr, 1.0f);
SkPaint rectPaint;
rectPaint.setColor4f(SkColor4f{0.0f, 0.0f, 0.0f, 0.5f});
canvas->drawRect(rect_from_points(d.p1, d.p2), rectPaint);
SkPaint openingRectPaint;
openingRectPaint.setColor4f(SkColor4f{0.0f, 0.0f, 0.0f, 0.0f});
openingRectPaint.setBlendMode(SkBlendMode::kSrc);
canvas->drawRect(get_cropped_rectangle(d.p1, d.p2, d.cropP1, d.cropP2), openingRectPaint);
canvas->restore();
canvas->restore();
}
else {
bool isCropped = d.cropP1 != Vector2f{0.0f, 0.0f} || d.cropP2 != Vector2f{1.0f, 1.0f};
if(isCropped) {
canvas->save();
canvas->clipRect(get_cropped_rectangle(d.p1, d.p2, d.cropP1, d.cropP2));
}
if(display) {
display->draw(canvas, drawData, rect_from_points(d.p1, d.p2));
canvas->restore();
}
else
canvas->drawRect(rect_from_points(d.p1, d.p2), SkPaint({0.5f, 0.5f, 0.5f, 0.5f}));
if(isCropped)
canvas->restore();
}
}
void ImageCanvasComponent::update(DrawingProgram& drawP) {
@ -84,7 +130,6 @@ void ImageCanvasComponent::update(DrawingProgram& drawP) {
}
void ImageCanvasComponent::initialize_draw_data(DrawingProgram& drawP) {
create_draw_data();
create_collider();
}
@ -92,14 +137,17 @@ bool ImageCanvasComponent::collides_within_coords(const SCollision::ColliderColl
return collisionTree.is_collide(checkAgainst);
}
void ImageCanvasComponent::create_draw_data() {
imRect = SkRect::MakeLTRB(d.p1.x(), d.p1.y(), d.p2.x(), d.p2.y());
}
void ImageCanvasComponent::create_collider() {
using namespace SCollision;
ColliderCollection<float> strokeObjects;
std::array<Vector2f, 4> newT = triangle_from_rect_points(d.p1, d.p2);
SkRect startingRect;
if(d.editing)
startingRect = rect_from_points(d.p1, d.p2);
else
startingRect = get_cropped_rectangle(d.p1, d.p2, d.cropP1, d.cropP2);
std::array<Vector2f, 4> newT = triangle_from_rect_points(convert_vec2<Vector2f>(startingRect.TL()), convert_vec2<Vector2f>(startingRect.BR()));
strokeObjects.triangle.emplace_back(newT[0], newT[1], newT[2]);
strokeObjects.triangle.emplace_back(newT[2], newT[3], newT[0]);
collisionTree.clear();

View file

@ -22,23 +22,26 @@ class ImageCanvasComponent : public CanvasComponent {
struct Data {
Vector2f p1;
Vector2f p2;
Vector2f cropP1 = {0.0f, 0.0f};
Vector2f cropP2 = {1.0f, 1.0f};
NetworkingObjects::NetObjID imageID;
bool editing = false;
} d;
virtual void update(DrawingProgram& drawP) override;
bool contains_actual_image();
private:
virtual void draw(SkCanvas* canvas, const DrawData& drawData) const override;
virtual void initialize_draw_data(DrawingProgram& drawP) override;
virtual bool collides_within_coords(const SCollision::ColliderCollection<float>& checkAgainst) const override;
void create_draw_data();
void create_collider();
virtual SCollision::AABB<float> get_obj_coord_bounds() const override;
SkRect imRect;
SCollision::BVHContainer<float> collisionTree;
};

View file

@ -187,7 +187,7 @@ void EditTool::tool_update() {
bool clickedAway = false;
if(!pointDragging && drawP.controls.leftClick) {
for(HandleData& h : pointHandles) {
if(SCollision::collide(mouseCircle, SCollision::Circle<float>(drawP.world.drawData.cam.c.to_space(objInfoBeingEdited->obj->coords.from_space(*h.p)), drawP.drag_point_radius()))) {
if(SCollision::collide(mouseCircle, SCollision::Circle<float>(drawP.world.drawData.cam.c.to_space(objInfoBeingEdited->obj->coords.from_space(h.coordMatrix * (*h.p))), drawP.drag_point_radius()))) {
pointDragging = &h;
isMovingPoint = true;
break;
@ -198,7 +198,7 @@ void EditTool::tool_update() {
}
else if(pointDragging) {
if(drawP.controls.leftClickHeld) {
Vector2f newPos = objInfoBeingEdited->obj->coords.get_mouse_pos(drawP.world);
Vector2f newPos = pointDragging->coordMatrix.inverse() * objInfoBeingEdited->obj->coords.get_mouse_pos(drawP.world);
if(newPos != *pointDragging->p) {
if(pointDragging->min)
newPos = cwise_vec_max((*pointDragging->min + Vector2f{pointDragging->minimumDistanceBetweenBoundsAndPoint, pointDragging->minimumDistanceBetweenBoundsAndPoint}).eval(), newPos);
@ -228,7 +228,7 @@ bool EditTool::prevent_undo_or_redo() {
void EditTool::draw(SkCanvas* canvas, const DrawData& drawData) {
if(objInfoBeingEdited) {
for(HandleData& h : pointHandles)
drawP.draw_drag_circle(canvas, drawData.cam.c.to_space((objInfoBeingEdited->obj->coords.from_space(*h.p))), {0.1f, 0.9f, 0.9f, 1.0f}, drawData);
drawP.draw_drag_circle(canvas, drawData.cam.c.to_space((objInfoBeingEdited->obj->coords.from_space(h.coordMatrix * *h.p))), {0.1f, 0.9f, 0.9f, 1.0f}, drawData);
}
}

View file

@ -13,6 +13,7 @@ class EditTool : public DrawingProgramToolBase {
Vector2f* min;
Vector2f* max;
float minimumDistanceBetweenBoundsAndPoint = MINIMUM_DISTANCE_BETWEEN_BOUNDS;
Affine2f coordMatrix = Affine2f::Identity();
};
EditTool(DrawingProgram& initDrawP);

View file

@ -5,7 +5,9 @@
#include "../../../Toolbar.hpp"
#include "../../../MainProgram.hpp"
#include "../../../ResourceManager.hpp"
#include "../EditTool.hpp"
#include "DrawingProgramEditToolBase.hpp"
#include "Eigen/Geometry"
#include <fstream>
#ifdef __EMSCRIPTEN__
@ -17,9 +19,23 @@ ImageEditTool::ImageEditTool(DrawingProgram& initDrawP):
{}
void ImageEditTool::edit_start(EditTool& editTool, CanvasComponentContainer::ObjInfo* comp, std::any& prevData) {
auto& a = static_cast<ImageCanvasComponent&>(comp->obj->get_comp());
static Vector2f staticZero = {0.0f, 0.0f};
static Vector2f staticOne = {1.0f, 1.0f};
Affine2f transformMat = Translation2f(a.d.p1) * AlignedScaling2f(a.d.p2 - a.d.p1) * Affine2f::Identity();
prevData = a.d;
editTool.add_point_handle({&a.d.cropP1, &staticZero, &a.d.cropP2, 0.0f, transformMat});
editTool.add_point_handle({&a.d.cropP2, &a.d.cropP1, &staticOne, 0.0f, transformMat});
a.d.editing = true;
comp->obj->commit_update(drawP);
}
void ImageEditTool::commit_edit_updates(CanvasComponentContainer::ObjInfo* comp, std::any& prevData) {
auto& a = static_cast<ImageCanvasComponent&>(comp->obj->get_comp());
a.d.editing = false;
comp->obj->commit_update(drawP);
}
bool ImageEditTool::edit_update(CanvasComponentContainer::ObjInfo* comp) {

View file

@ -278,6 +278,8 @@ void TextBoxEditTool::edit_start(EditTool& editTool, CanvasComponentContainer::O
editTool.add_point_handle({&a.d.p1, nullptr, &a.d.p2});
editTool.add_point_handle({&a.d.p2, &a.d.p1, nullptr});
comp->obj->commit_update(drawP);
}
bool TextBoxEditTool::edit_update(CanvasComponentContainer::ObjInfo* comp) {

View file

@ -7,6 +7,7 @@
//#include <include/codec/SkRawDecoder.h>
#include <include/codec/SkWbmpDecoder.h>
#include <include/codec/SkWebpDecoder.h>
#include <include/core/SkCanvas.h>
#include "../MainProgram.hpp"
#include <Helpers/Logger.hpp>