diff --git a/xbanan/Base.cpp b/xbanan/Base.cpp index 9448ee4..6777061 100644 --- a/xbanan/Base.cpp +++ b/xbanan/Base.cpp @@ -2699,6 +2699,9 @@ BAN::ErrorOr handle_packet(Client& client_info, BAN::ConstByteSpan packet) case X_PolySegment: TRY(poly_segment(client_info, packet)); break; + case X_FillPoly: + TRY(fill_poly(client_info, packet)); + break; case X_PolyFillRectangle: TRY(poly_fill_rectangle(client_info, packet)); break; diff --git a/xbanan/Drawing.cpp b/xbanan/Drawing.cpp index e3f8550..2d19820 100644 --- a/xbanan/Drawing.cpp +++ b/xbanan/Drawing.cpp @@ -3,6 +3,8 @@ #include "SafeGetters.h" #include "Utils.h" +#include + #include struct DrawSegmentInfo @@ -105,16 +107,17 @@ BAN::ErrorOr poly_line(Client& client_info, BAN::ConstByteSpan packet) auto request = decode(packet).value(); dprintln("PolyLine"); - dprintln(" drawable: {}", request.drawable); - dprintln(" gc: {}", request.gc); + dprintln(" drawable: {}", request.drawable); + dprintln(" gc: {}", request.gc); + dprintln(" coordMode: {}", request.coordMode); + + auto [out_data_u32, out_w, out_h, _] = TRY(get_drawable_info(client_info, request.drawable, X_PolyLine)); + + const auto& gc = TRY_REF(get_gc(client_info, request.gc, X_PolyLine)); if (packet.size() < sizeof(xPoint)) return {}; - auto [out_data_u32, out_w, out_h, _] = TRY(get_drawable_info(client_info, request.drawable, X_PolyFillRectangle)); - - const auto& gc = TRY_REF(get_gc(client_info, request.gc, X_PolyFillRectangle)); - DrawSegmentInfo info { .out_data_u32 = out_data_u32, .out_w = out_w, @@ -161,9 +164,9 @@ BAN::ErrorOr poly_segment(Client& client_info, BAN::ConstByteSpan packet) dprintln(" drawable: {}", request.drawable); dprintln(" gc: {}", request.gc); - auto [out_data_u32, out_w, out_h, _] = TRY(get_drawable_info(client_info, request.drawable, X_PolyFillRectangle)); + auto [out_data_u32, out_w, out_h, _] = TRY(get_drawable_info(client_info, request.drawable, X_PolySegment)); - const auto& gc = TRY_REF(get_gc(client_info, request.gc, X_PolyFillRectangle)); + const auto& gc = TRY_REF(get_gc(client_info, request.gc, X_PolySegment)); DrawSegmentInfo info { .out_data_u32 = out_data_u32, @@ -195,6 +198,81 @@ BAN::ErrorOr poly_segment(Client& client_info, BAN::ConstByteSpan packet) return {}; } +BAN::ErrorOr fill_poly(Client& client_info, BAN::ConstByteSpan packet) +{ + auto request = decode(packet).value(); + + dprintln("FillPoly"); + dprintln(" drawable: {}", request.drawable); + dprintln(" gc: {}", request.gc); + dprintln(" shape: {}", request.shape); + dprintln(" coordMode: {}", request.coordMode); + + auto [out_data_u32, out_w, out_h, _] = TRY(get_drawable_info(client_info, request.drawable, X_FillPoly)); + + const auto& gc = TRY_REF(get_gc(client_info, request.gc, X_FillPoly)); + + if (packet.size() < sizeof(xPoint)) + return {}; + + BAN::Vector points; + TRY(points.push_back(decode(packet).value())); + while (packet.size() >= sizeof(xPoint)) + { + auto point = decode(packet).value(); + if (request.coordMode == CoordModePrevious) + { + point.x += points.back().x; + point.y += points.back().y; + } + TRY(points.push_back(point)); + } + + int32_t min_x = INT32_MAX, max_x = INT32_MIN; + int32_t min_y = INT32_MAX, max_y = INT32_MIN; + for (const auto& point : points) + { + min_x = BAN::Math::min(min_x, point.x); + min_y = BAN::Math::min(min_y, point.y); + max_x = BAN::Math::max(max_x, point.x); + max_y = BAN::Math::max(max_y, point.y); + } + + for (int32_t y = min_y; y <= max_y; y++) + { + BAN::Vector intersections; + + for (size_t i = 0; i < points.size(); i++) + { + const auto& p1 = points[i]; + const auto& p2 = points[(i + 1) % points.size()]; + if (!(BAN::Math::min(p1.y, p2.y) <= y && y < BAN::Math::max(p1.y, p2.y))) + continue; + + const float t = static_cast(y - p1.y) / (p2.y - p1.y); + if (t < 0.0f || t > 1.0f) + continue; + TRY(intersections.push_back(p1.x + (p2.x - p1.x) * t)); + } + + BAN::sort::sort(intersections.begin(), intersections.end()); + + for (size_t i = 0; i + 1 < intersections.size(); i += 2) + { + const int32_t lx = BAN::Math::clamp(BAN::Math::ceil (intersections[i + 0]), 0, out_w); + const int32_t rx = BAN::Math::clamp(BAN::Math::floor(intersections[i + 1]), 0, out_w); + for (int32_t x = lx; x <= rx; x++) + if (!gc.is_clipped(x, y)) + out_data_u32[y * out_w + x] = gc.foreground; + } + } + + if (g_objects[request.drawable]->type == Object::Type::Window) + invalidate_window(request.drawable, min_x, min_y, max_x - min_x + 1, max_y - min_y + 1); + + return {}; +} + BAN::ErrorOr poly_fill_rectangle(Client& client_info, BAN::ConstByteSpan packet) { auto request = decode(packet).value(); diff --git a/xbanan/Drawing.h b/xbanan/Drawing.h index 6a5184b..b07be95 100644 --- a/xbanan/Drawing.h +++ b/xbanan/Drawing.h @@ -4,5 +4,6 @@ BAN::ErrorOr poly_line(Client& client_info, BAN::ConstByteSpan packet); BAN::ErrorOr poly_segment(Client& client_info, BAN::ConstByteSpan packet); +BAN::ErrorOr fill_poly(Client& client_info, BAN::ConstByteSpan packet); BAN::ErrorOr poly_fill_rectangle(Client& client_info, BAN::ConstByteSpan packet); BAN::ErrorOr poly_fill_arc(Client& client_info, BAN::ConstByteSpan packet);