diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt index 04ae76b75c..b63cf703c8 100644 --- a/bridge/CMakeLists.txt +++ b/bridge/CMakeLists.txt @@ -411,8 +411,11 @@ if (${WEBF_JS_ENGINE} MATCHES "quickjs") core/html/canvas/canvas_rendering_context_2d.cc core/html/canvas/canvas_gradient.cc core/html/canvas/canvas_pattern.cc + core/html/canvas/path_2d.cc core/geometry/dom_matrix.cc - core/geometry/dom_matrix_readonly.cc + core/geometry/dom_matrix_read_only.cc + core/geometry/dom_point.cc + core/geometry/dom_point_read_only.cc core/html/forms/html_button_element.cc core/html/forms/html_input_element.cc core/html/forms/html_form_element.cc @@ -545,11 +548,18 @@ if (${WEBF_JS_ENGINE} MATCHES "quickjs") out/qjs_canvas_rendering_context.cc out/qjs_canvas_gradient.cc out/qjs_canvas_pattern.cc + out/qjs_path_2d.cc + out/qjs_dom_matrix_init.cc out/qjs_dom_matrix.cc - out/qjs_dom_matrix_readonly.cc - out/qjs_union_dom_string_sequencedouble.cc + out/qjs_dom_matrix_read_only.cc + out/qjs_dom_point_init.cc + out/qjs_dom_point.cc + out/qjs_dom_point_read_only.cc + out/qjs_union_double_sequencedouble.cc out/qjs_unionhtml_image_elementhtml_canvas_element.cc out/qjs_union_dom_stringcanvas_gradient.cc + out/qjs_union_sequencedoubledom_matrix_init.cc + out/qjs_union_doubledom_point_init.cc out/canvas_types.cc out/qjs_html_button_element.cc out/qjs_html_input_element.cc @@ -560,6 +570,7 @@ if (${WEBF_JS_ENGINE} MATCHES "quickjs") out/qjs_promise_rejection_event_init.cc out/qjs_unionevent_listener_options_boolean.cc out/qjs_unionadd_event_listener_options_boolean.cc + out/qjs_unionpath_2_d_dom_string.cc out/qjs_html_template_element.cc out/qjs_html_unknown_element.cc out/qjs_performance.cc diff --git a/bridge/bindings/qjs/binding_initializer.cc b/bridge/bindings/qjs/binding_initializer.cc index f818eb5fae..9c087af470 100644 --- a/bridge/bindings/qjs/binding_initializer.cc +++ b/bridge/bindings/qjs/binding_initializer.cc @@ -11,6 +11,7 @@ #include "qjs_bounding_client_rect.h" #include "qjs_canvas_gradient.h" #include "qjs_canvas_pattern.h" +#include "qjs_path_2d.h" #include "qjs_canvas_rendering_context.h" #include "qjs_canvas_rendering_context_2d.h" #include "qjs_character_data.h" @@ -23,7 +24,9 @@ #include "qjs_document.h" #include "qjs_document_fragment.h" #include "qjs_dom_matrix.h" -#include "qjs_dom_matrix_readonly.h" +#include "qjs_dom_matrix_read_only.h" +#include "qjs_dom_point.h" +#include "qjs_dom_point_read_only.h" #include "qjs_dom_string_map.h" #include "qjs_dom_token_list.h" #include "qjs_element.h" @@ -163,8 +166,11 @@ void InstallBindings(ExecutingContext* context) { QJSCanvasRenderingContext2D::Install(context); QJSCanvasPattern::Install(context); QJSCanvasGradient::Install(context); - QJSDOMMatrixReadonly::Install(context); + QJSPath2D::Install(context); + QJSDOMMatrixReadOnly::Install(context); QJSDOMMatrix::Install(context); + QJSDOMPointReadOnly::Install(context); + QJSDOMPoint::Install(context); QJSCSSStyleDeclaration::Install(context); QJSInlineCssStyleDeclaration::Install(context); QJSComputedCssStyleDeclaration::Install(context); diff --git a/bridge/bindings/qjs/wrapper_type_info.h b/bridge/bindings/qjs/wrapper_type_info.h index 90245bf162..b15b2efd30 100644 --- a/bridge/bindings/qjs/wrapper_type_info.h +++ b/bridge/bindings/qjs/wrapper_type_info.h @@ -79,8 +79,11 @@ enum { JS_CLASS_CANVAS_RENDERING_CONTEXT_2_D, JS_CLASS_CANVAS_GRADIENT, JS_CLASS_CANVAS_PATTERN, + JS_CLASS_PATH_2_D, JS_CLASS_DOM_MATRIX, - JS_CLASS_DOM_MATRIX_READONLY, + JS_CLASS_DOM_MATRIX_READ_ONLY, + JS_CLASS_DOM_POINT, + JS_CLASS_DOM_POINT_READ_ONLY, JS_CLASS_HTML_TEMPLATE_ELEMENT, JS_CLASS_HTML_UNKNOWN_ELEMENT, JS_CLASS_HTML_INPUT_ELEMENT, diff --git a/bridge/core/binding_call_methods.json5 b/bridge/core/binding_call_methods.json5 index e4f8d8c6a7..2392e86e88 100644 --- a/bridge/core/binding_call_methods.json5 +++ b/bridge/core/binding_call_methods.json5 @@ -104,6 +104,7 @@ "lineTo", "moveTo", "rect", + "roundRect", "restore", "resetTransform", "rotate", @@ -114,6 +115,7 @@ "scale", "strokeText", "setTransform", + "addPath", "transform", "translate", "reset", @@ -179,6 +181,24 @@ "dir", "pageXOffset", "pageYOffset", - "title" + "title", + "is2D", + "isIdentity", + "flipX", + "flipY", + "inverse", + "multiply", + "rotateAxisAngle", + "rotateFromVector", + "scale3d", + "scaleNonUniform", + "skewX", + "skewY", + "toFloat32Array", + "toFloat64Array", + "toJSON", + "toString", + "transformPoint", + "matrixTransform" ] } diff --git a/bridge/core/binding_object.h b/bridge/core/binding_object.h index 693a2c2627..57369e7ce4 100644 --- a/bridge/core/binding_object.h +++ b/bridge/core/binding_object.h @@ -70,7 +70,11 @@ enum BindingMethodCallOperations { kAsyncAnonymousFunction, }; -enum CreateBindingObjectType { kCreateDOMMatrix = 0 }; +enum CreateBindingObjectType { + kCreateDOMMatrix = 0, + kCreatePath2D = 1, + kCreateDOMPoint = 2, +}; struct BindingObjectPromiseContext : public DartReadable { ExecutingContext* context; diff --git a/bridge/core/frame/module_manager.cc b/bridge/core/frame/module_manager.cc index 24036f0221..45ef7a6618 100644 --- a/bridge/core/frame/module_manager.cc +++ b/bridge/core/frame/module_manager.cc @@ -138,10 +138,25 @@ ScriptValue ModuleManager::__webf_invoke_module__(ExecutingContext* context, ExceptionState& exception) { NativeValue params = params_value.ToNative(context->ctx(), exception); - if (exception.HasException()) { + NativeValue* result = __webf_invoke_module__(context, module_name, method, params, callback, exception); + if (result == nullptr) { return ScriptValue::Empty(context->ctx()); } + ScriptValue return_value = ScriptValue(context->ctx(), *result); + dart_free(result); + return return_value; +} +NativeValue* ModuleManager::__webf_invoke_module__(ExecutingContext* context, + const AtomicString& module_name, + const AtomicString& method, + NativeValue& params, + const std::shared_ptr& callback, + ExceptionState& exception) { + if (exception.HasException()) { + return nullptr; + } + NativeValue* result; auto module_name_string = module_name.ToNativeString(context->ctx()); auto method_name_string = method.ToNativeString(context->ctx()); @@ -165,12 +180,12 @@ ScriptValue ModuleManager::__webf_invoke_module__(ExecutingContext* context, context->dartIsolateContext()->profiler()->FinishTrackLinkSteps(); if (result == nullptr) { - return ScriptValue::Empty(context->ctx()); + return nullptr; } - ScriptValue return_value = ScriptValue(context->ctx(), *result); - dart_free(result); - return return_value; + // ScriptValue return_value = ScriptValue(context->ctx(), *result); + // dart_free(result); + return result; } void ModuleManager::__webf_add_module_listener__(ExecutingContext* context, diff --git a/bridge/core/frame/module_manager.h b/bridge/core/frame/module_manager.h index 06d8e8ebb0..5276dd68ed 100644 --- a/bridge/core/frame/module_manager.h +++ b/bridge/core/frame/module_manager.h @@ -10,6 +10,8 @@ #include "bindings/qjs/qjs_function.h" #include "module_callback.h" +#include + namespace webf { class ModuleContext { @@ -37,6 +39,12 @@ class ModuleManager { ScriptValue& params_value, const std::shared_ptr& callback, ExceptionState& exception); + static NativeValue* __webf_invoke_module__(ExecutingContext* context, + const AtomicString& module_name, + const AtomicString& method, + NativeValue& params_value, + const std::shared_ptr& callback, + ExceptionState& exception); static void __webf_add_module_listener__(ExecutingContext* context, const AtomicString& module_name, const std::shared_ptr& handler, diff --git a/bridge/core/geometry/dom_matrix.cc b/bridge/core/geometry/dom_matrix.cc index b4a68c1467..30a2a20c16 100644 --- a/bridge/core/geometry/dom_matrix.cc +++ b/bridge/core/geometry/dom_matrix.cc @@ -3,18 +3,30 @@ */ #include "dom_matrix.h" +#include "core/executing_context.h" namespace webf { DOMMatrix* DOMMatrix::Create(ExecutingContext* context, - const std::shared_ptr& init, + const std::shared_ptr& init, ExceptionState& exception_state) { return MakeGarbageCollected(context, init, exception_state); } +DOMMatrix* DOMMatrix::Create(webf::ExecutingContext* context, webf::ExceptionState& exception_state) { + return MakeGarbageCollected(context, exception_state); +} + +DOMMatrix::DOMMatrix(webf::ExecutingContext* context, webf::ExceptionState& exception_state): + DOMMatrixReadOnly(context, exception_state) {} + DOMMatrix::DOMMatrix(ExecutingContext* context, - const std::shared_ptr& init, + const std::shared_ptr& init, ExceptionState& exception_state) - : DOMMatrixReadonly(context, init, exception_state) {} + : DOMMatrixReadOnly(context, init, exception_state) {} + +DOMMatrix::DOMMatrix(webf::ExecutingContext* context, webf::NativeBindingObject* native_binding_object): DOMMatrixReadOnly(context, native_binding_object) { + +} } // namespace webf \ No newline at end of file diff --git a/bridge/core/geometry/dom_matrix.d.ts b/bridge/core/geometry/dom_matrix.d.ts index b230a6e991..de5df3a12e 100644 --- a/bridge/core/geometry/dom_matrix.d.ts +++ b/bridge/core/geometry/dom_matrix.d.ts @@ -1,3 +1,7 @@ -interface DOMMatrix extends DOMMatrixReadonly { - new(init: string | double[]): DOMMatrix; +import {DOMMatrixInit} from "./dom_matrix_init"; +import {DOMMatrixReadOnly} from "./dom_matrix_read_only"; + +interface DOMMatrix extends DOMMatrixReadOnly { + fromMatrix(matrix: DOMMatrix): StaticMethod; + new(init?: number[] | DOMMatrixInit): DOMMatrix; } \ No newline at end of file diff --git a/bridge/core/geometry/dom_matrix.h b/bridge/core/geometry/dom_matrix.h index e8bcb4e045..ac04deefc4 100644 --- a/bridge/core/geometry/dom_matrix.h +++ b/bridge/core/geometry/dom_matrix.h @@ -5,25 +5,39 @@ #ifndef WEBF_CORE_HTML_CANVAS_DOM_MATRIX_H_ #define WEBF_CORE_HTML_CANVAS_DOM_MATRIX_H_ -#include "dom_matrix_readonly.h" +#include "dom_matrix_read_only.h" namespace webf { -class DOMMatrix : public DOMMatrixReadonly { +class DOMMatrix : public DOMMatrixReadOnly { DEFINE_WRAPPERTYPEINFO(); public: + using ImplType = DOMMatrix*; + static DOMMatrix* Create(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state); static DOMMatrix* Create(ExecutingContext* context, - const std::shared_ptr& init, ExceptionState& exception_state); DOMMatrix() = delete; explicit DOMMatrix(ExecutingContext* context, - const std::shared_ptr& init, ExceptionState& exception_state); + explicit DOMMatrix(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state); + explicit DOMMatrix(ExecutingContext* context, + NativeBindingObject* native_binding_object); + + [[nodiscard]] + bool IsDOMMatrix() const override { return true; } private: }; +template <> +struct DowncastTraits { + static bool AllowFrom(const DOMMatrixReadOnly& matrix) { return matrix.IsDOMMatrix(); } +}; } // namespace webf diff --git a/bridge/core/geometry/dom_matrix_init.d.ts b/bridge/core/geometry/dom_matrix_init.d.ts new file mode 100644 index 0000000000..c8c78ddb80 --- /dev/null +++ b/bridge/core/geometry/dom_matrix_init.d.ts @@ -0,0 +1,22 @@ +// @ts-ignore +@Dictionary() +export interface DOMMatrixInit { + m11: number, + m12: number, + m13: number, + m14: number, + m21: number, + m22: number, + m23: number, + m24: number, + m31: number, + m32: number, + m33: number, + m34: number, + m41: number, + m42: number, + m43: number, + m44: number, + is2D: boolean, + isIdentity: boolean, +} \ No newline at end of file diff --git a/bridge/core/geometry/dom_matrix_read_only.cc b/bridge/core/geometry/dom_matrix_read_only.cc new file mode 100644 index 0000000000..14a33b83ae --- /dev/null +++ b/bridge/core/geometry/dom_matrix_read_only.cc @@ -0,0 +1,510 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#include "dom_matrix_read_only.h" +#include "bindings/qjs/converter_impl.h" +#include "binding_call_methods.h" +#include "native_value_converter.h" +#include "core/executing_context.h" +#include "core/frame/module_manager.h" +#include "core/geometry/dom_matrix.h" +#include "core/geometry/dom_point.h" + +namespace webf { +DOMMatrixReadOnly* DOMMatrixReadOnly::Create(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state) { + return MakeGarbageCollected(context, init, exception_state); +} + +DOMMatrixReadOnly* DOMMatrixReadOnly::Create(webf::ExecutingContext* context, webf::ExceptionState& exception_state) { + return MakeGarbageCollected(context, exception_state); +} + +DOMMatrix* DOMMatrixReadOnly::fromMatrix(ExecutingContext* context, + DOMMatrixReadOnly* matrix, + ExceptionState& exception_state) { + NativeValue arguments[] = {NativeValueConverter>::ToNativeValue(matrix)}; + // auto* context = matrix->GetExecutingContext(); + AtomicString module_name = AtomicString(context->ctx(), "DOMMatrix"); + AtomicString method_name = AtomicString(context->ctx(), "fromMatrix"); + + NativeValue* dart_result = context->dartMethodPtr()->invokeModule( + context->isDedicated(), nullptr, context->contextId(), context->dartIsolateContext()->profiler()->link_id(), + module_name.ToNativeString(context->ctx()).release(), method_name.ToNativeString(context->ctx()).release(), + arguments, nullptr); + + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(*dart_result); + + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(context, native_binding_object); +} + +DOMMatrixReadOnly::DOMMatrixReadOnly(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state) + : BindingObject(context->ctx()) { + + if (init->IsSequenceDouble()) { + NativeValue arguments[1]; + arguments[0] = NativeValueConverter>::ToNativeValue(init->GetAsSequenceDouble()); + GetExecutingContext()->dartMethodPtr()->createBindingObject(GetExecutingContext()->isDedicated(), + GetExecutingContext()->contextId(), bindingObject(), + CreateBindingObjectType::kCreateDOMMatrix, arguments, 1); + } else if (init->IsDOMMatrixInit()) { + std::vector domMatrixInitVectorDouble; + auto domMatrixInit = init->GetAsDOMMatrixInit(); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m11()); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m12()); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m13()); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m14()); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m21()); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m22()); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m23()); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m24()); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m31()); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m32()); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m33()); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m34()); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m41()); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m42()); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m43()); + domMatrixInitVectorDouble.emplace_back(domMatrixInit->m44()); + NativeValue arguments[3]; + arguments[0] = NativeValueConverter>::ToNativeValue(domMatrixInitVectorDouble); + arguments[1] = NativeValueConverter::ToNativeValue(domMatrixInit->is2D()); + arguments[2] = NativeValueConverter::ToNativeValue(domMatrixInit->isIdentity()); + GetExecutingContext()->dartMethodPtr()->createBindingObject(GetExecutingContext()->isDedicated(), + GetExecutingContext()->contextId(), bindingObject(), + CreateBindingObjectType::kCreateDOMMatrix, arguments, 1); + } +} + +DOMMatrixReadOnly::DOMMatrixReadOnly(webf::ExecutingContext* context, webf::ExceptionState& exception_state) + : BindingObject(context->ctx()) { + GetExecutingContext()->dartMethodPtr()->createBindingObject(GetExecutingContext()->isDedicated(), + GetExecutingContext()->contextId(), bindingObject(), + CreateBindingObjectType::kCreateDOMMatrix, nullptr, 0); +} + +DOMMatrixReadOnly::DOMMatrixReadOnly(webf::ExecutingContext* context, webf::NativeBindingObject* native_binding_object) + : BindingObject(context->ctx(), native_binding_object) {} + +double DOMMatrixReadOnly::getMatrixProperty(const AtomicString& prop) const { + NativeValue dart_result = GetBindingProperty(prop, FlushUICommandReason::kDependentsOnElement, ASSERT_NO_EXCEPTION()); + return NativeValueConverter::FromNativeValue(dart_result); +} + +void DOMMatrixReadOnly::setMatrixProperty(const AtomicString& prop, double v, ExceptionState& exception_state) { + if (DynamicTo(this)) { + SetBindingProperty(prop, NativeValueConverter::ToNativeValue(v), exception_state); + } +} + +double DOMMatrixReadOnly::m11() const { + return getMatrixProperty(defined_properties::km11); +} +void DOMMatrixReadOnly::setM11(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km11, v, exception_state); +} +double DOMMatrixReadOnly::m12() const { + return getMatrixProperty(defined_properties::km12); +} +void DOMMatrixReadOnly::setM12(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km12, v, exception_state); +} +double DOMMatrixReadOnly::m13() const { + return getMatrixProperty(defined_properties::km13); +} +void DOMMatrixReadOnly::setM13(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km13, v, exception_state); +} +double DOMMatrixReadOnly::m14() const { + return getMatrixProperty(defined_properties::km14); +} +void DOMMatrixReadOnly::setM14(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km14, v, exception_state); +} + +double DOMMatrixReadOnly::m21() const { + return getMatrixProperty(defined_properties::km21); +} +void DOMMatrixReadOnly::setM21(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km21, v, exception_state); +} +double DOMMatrixReadOnly::m22() const { + return getMatrixProperty(defined_properties::km22); +} +void DOMMatrixReadOnly::setM22(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km22, v, exception_state); +} +double DOMMatrixReadOnly::m23() const { + return getMatrixProperty(defined_properties::km23); +} +void DOMMatrixReadOnly::setM23(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km23, v, exception_state); +} +double DOMMatrixReadOnly::m24() const { + return getMatrixProperty(defined_properties::km24); +} +void DOMMatrixReadOnly::setM24(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km24, v, exception_state); +} + +double DOMMatrixReadOnly::m31() const { + return getMatrixProperty(defined_properties::km31); +} +void DOMMatrixReadOnly::setM31(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km31, v, exception_state); +} +double DOMMatrixReadOnly::m32() const { + return getMatrixProperty(defined_properties::km32); +} +void DOMMatrixReadOnly::setM32(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km32, v, exception_state); +} +double DOMMatrixReadOnly::m33() const { + return getMatrixProperty(defined_properties::km33); +} +void DOMMatrixReadOnly::setM33(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km33, v, exception_state); +} +double DOMMatrixReadOnly::m34() const { + return getMatrixProperty(defined_properties::km34); +} +void DOMMatrixReadOnly::setM34(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km34, v, exception_state); +} +double DOMMatrixReadOnly::m41() const { + return getMatrixProperty(defined_properties::km41); +} +void DOMMatrixReadOnly::setM41(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km41, v, exception_state); +} +double DOMMatrixReadOnly::m42() const { + return getMatrixProperty(defined_properties::km42); +} +void DOMMatrixReadOnly::setM42(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km42, v, exception_state); +} +double DOMMatrixReadOnly::m43() const { + return getMatrixProperty(defined_properties::km43); +} +void DOMMatrixReadOnly::setM43(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km43, v, exception_state); +} +double DOMMatrixReadOnly::m44() const { + return getMatrixProperty(defined_properties::km44); +} +void DOMMatrixReadOnly::setM44(double v, ExceptionState& exception_state) { + setMatrixProperty(defined_properties::km44, v, exception_state); +} + +DOMMatrix* DOMMatrixReadOnly::flipX(ExceptionState& exception_state) const { + NativeValue value = InvokeBindingMethod(binding_call_methods::kflipX, 0, + nullptr, FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} +DOMMatrix* DOMMatrixReadOnly::flipY(ExceptionState& exception_state) const { + NativeValue value = InvokeBindingMethod(binding_call_methods::kflipY, 0, + nullptr, FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} +DOMMatrix* DOMMatrixReadOnly::inverse(ExceptionState& exception_state) const { + NativeValue value = InvokeBindingMethod(binding_call_methods::kinverse, 0, + nullptr, FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} + +DOMMatrix* DOMMatrixReadOnly::multiply(DOMMatrix* matrix, ExceptionState& exception_state) const { + NativeValue arguments[] = { + NativeValueConverter>::ToNativeValue(matrix) + }; + NativeValue value = InvokeBindingMethod(binding_call_methods::kmultiply, sizeof(arguments) / sizeof(NativeValue), + arguments, FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} + +DOMMatrix* DOMMatrixReadOnly::rotateAxisAngle(ExceptionState& exception_state) const { + return this->rotateAxisAngle(0, 0, 0, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::rotateAxisAngle(double x, ExceptionState& exception_state) const { + return this->rotateAxisAngle(x, 0, 0, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::rotateAxisAngle(double x, double y, ExceptionState& exception_state) const { + return this->rotateAxisAngle(x, y, 0, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::rotateAxisAngle(double x, double y, double z, ExceptionState& exception_state) const { + return this->rotateAxisAngle(x, y, z, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::rotateAxisAngle(double x, + double y, + double z, + double angle, + ExceptionState& exception_state) const { + NativeValue arguments[] = {NativeValueConverter::ToNativeValue(x), + NativeValueConverter::ToNativeValue(y), + NativeValueConverter::ToNativeValue(z), + NativeValueConverter::ToNativeValue(angle)}; + NativeValue value = + InvokeBindingMethod(binding_call_methods::krotateAxisAngle, sizeof(arguments) / sizeof(NativeValue), arguments, + FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} + +DOMMatrix* DOMMatrixReadOnly::rotate(ExceptionState& exception_state) const { + return this->rotate(0, 0, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::rotate(double x, ExceptionState& exception_state) const { + NativeValue arguments[] = {NativeValueConverter::ToNativeValue(x)}; + NativeValue value = InvokeBindingMethod(binding_call_methods::krotate, sizeof(arguments) / sizeof(NativeValue), + arguments, FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} +DOMMatrix* DOMMatrixReadOnly::rotate(double x, double y, ExceptionState& exception_state) const { + return this->rotate(x, y, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::rotate(double x, double y, double z, ExceptionState& exception_state) const { + NativeValue arguments[] = {NativeValueConverter::ToNativeValue(x), + NativeValueConverter::ToNativeValue(y), + NativeValueConverter::ToNativeValue(z)}; + NativeValue value = InvokeBindingMethod(binding_call_methods::krotate, sizeof(arguments) / sizeof(NativeValue), + arguments, FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} + +DOMMatrix* DOMMatrixReadOnly::rotateFromVector(ExceptionState& exception_state) const { + return this->rotateFromVector(0, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::rotateFromVector(double x, ExceptionState& exception_state) const { + return this->rotateFromVector(x, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::rotateFromVector(double x, double y, ExceptionState& exception_state) const { + NativeValue arguments[] = {NativeValueConverter::ToNativeValue(x), + NativeValueConverter::ToNativeValue(y)}; + NativeValue value = + InvokeBindingMethod(binding_call_methods::krotateFromVector, sizeof(arguments) / sizeof(NativeValue), arguments, + FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} + +DOMMatrix* DOMMatrixReadOnly::scale(ExceptionState& exception_state) const { + return this->scale(1, 1,1, 0, 0, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::scale(double sx, ExceptionState& exception_state) const { + return this->scale(sx, 1, 1, 0, 0, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::scale(double sx, double sy, ExceptionState& exception_state) const { + return this->scale(sx, sy, 1, 0, 0, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::scale(double sx, double sy, double sz, ExceptionState& exception_state) const { + return this->scale(sx, sy, sz, 0, 0, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::scale(double sx, double sy, double sz, double ox, ExceptionState& exception_state) const { + return this->scale(sx, sy, sz, ox, 0, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::scale(double sx, double sy, double sz, double ox, double oy, ExceptionState& exception_state) const { + return this->scale(sx, sy, sz, ox, oy, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::scale(double sx, + double sy, + double sz, + double ox, + double oy, + double oz, + ExceptionState& exception_state) const { + NativeValue arguments[] = {NativeValueConverter::ToNativeValue(sx), + NativeValueConverter::ToNativeValue(sy), + NativeValueConverter::ToNativeValue(sz), + NativeValueConverter::ToNativeValue(ox), + NativeValueConverter::ToNativeValue(oy), + NativeValueConverter::ToNativeValue(oz)}; + NativeValue value = InvokeBindingMethod(binding_call_methods::kscale, sizeof(arguments) / sizeof(NativeValue), + arguments, FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} + +DOMMatrix* DOMMatrixReadOnly::scale3d(ExceptionState& exception_state) const { + return this->scale3d(1, 0,0, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::scale3d(double scale, ExceptionState& exception_state) const { + return this->scale3d(scale, 0, 0, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::scale3d(double scale, double ox, ExceptionState& exception_state) const { + return this->scale3d(scale, ox, 0, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::scale3d(double scale, double ox, double oy, ExceptionState& exception_state) const { + return this->scale3d(scale, ox, oy, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::scale3d(double scale, + double ox, + double oy, + double oz, + ExceptionState& exception_state) const { + NativeValue arguments[] = {NativeValueConverter::ToNativeValue(scale), + NativeValueConverter::ToNativeValue(ox), + NativeValueConverter::ToNativeValue(oy), + NativeValueConverter::ToNativeValue(oz)}; + NativeValue value = InvokeBindingMethod(binding_call_methods::kscale3d, sizeof(arguments) / sizeof(NativeValue), + arguments, FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} + +DOMMatrix* DOMMatrixReadOnly::scaleNonUniform(ExceptionState& exception_state) const { + return this->scaleNonUniform(1, 1, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::scaleNonUniform(double sx, ExceptionState& exception_state) const { + return this->scaleNonUniform(sx, 1, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::scaleNonUniform(double sx, double sy, ExceptionState& exception_state) const { + NativeValue arguments[] = {NativeValueConverter::ToNativeValue(sx), + NativeValueConverter::ToNativeValue(sy)}; + NativeValue value = + InvokeBindingMethod(binding_call_methods::kscaleNonUniform, sizeof(arguments) / sizeof(NativeValue), arguments, + FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} + +DOMMatrix* DOMMatrixReadOnly::skewX(ExceptionState& exception_state) const { + return this->skewX(0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::skewX(double sx, ExceptionState& exception_state) const { + NativeValue arguments[] = {NativeValueConverter::ToNativeValue(sx)}; + NativeValue value = InvokeBindingMethod(binding_call_methods::kskewX, sizeof(arguments) / sizeof(NativeValue), + arguments, FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} + +DOMMatrix* DOMMatrixReadOnly::skewY(ExceptionState& exception_state) const { + return this->skewY(0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::skewY(double sy, ExceptionState& exception_state) const { + NativeValue arguments[] = {NativeValueConverter::ToNativeValue(sy)}; + NativeValue value = InvokeBindingMethod(binding_call_methods::kskewY, sizeof(arguments) / sizeof(NativeValue), + arguments, FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} +// std::vector& DOMMatrixReadOnly::toFloat32Array(ExceptionState& exception_state) const { +// std::vector float32Vector; +// // NativeValue arguments[0]; +// // NativeValue value = InvokeBindingMethod(binding_call_methods::ktoFloat32Array, sizeof(arguments) / +// sizeof(NativeValue), +// // arguments, FlushUICommandReason::kDependentsOnElement, exception_state); +// // auto&& arr = NativeValueConverter>::FromNativeValue(ctx(), value); +// // if (native_binding_object == nullptr) +// // return float32Vector; +// // // return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +// return float32Vector; +// } +// std::vector& DOMMatrixReadOnly::toFloat64Array(ExceptionState& exception_state) const { +// std::vector float64Vector; +// return float64Vector; +// } +// toJSON(): DartImpl; +AtomicString DOMMatrixReadOnly::toString(ExceptionState& exception_state) const { + NativeValue dart_result = + InvokeBindingMethod(binding_call_methods::ktoString, 0, nullptr, + FlushUICommandReason::kDependentsOnElement, exception_state); + return NativeValueConverter::FromNativeValue(ctx(), std::move(dart_result)); +} + +DOMPoint* DOMMatrixReadOnly::transformPoint(DOMPoint* point, ExceptionState& exception_state) const { + NativeValue arguments[] = { + NativeValueConverter>::ToNativeValue(point) + }; + NativeValue value = InvokeBindingMethod(binding_call_methods::ktransformPoint, sizeof(arguments) / sizeof(NativeValue), + arguments, FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} + +DOMMatrix* DOMMatrixReadOnly::translate(ExceptionState& exception_state) const { + return this->translate(0, 0, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::translate(double tx, ExceptionState& exception_state) const { + return this->translate(tx, 0, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::translate(double tx, double ty, ExceptionState& exception_state) const { + return this->translate(tx, ty, 0, exception_state); +} +DOMMatrix* DOMMatrixReadOnly::translate(double tx, double ty, double tz, ExceptionState& exception_state) const { + NativeValue arguments[] = {NativeValueConverter::ToNativeValue(tx), + NativeValueConverter::ToNativeValue(ty), + NativeValueConverter::ToNativeValue(tz)}; + NativeValue value = InvokeBindingMethod(binding_call_methods::ktranslate, sizeof(arguments) / sizeof(NativeValue), + arguments, FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} + +NativeValue DOMMatrixReadOnly::HandleCallFromDartSide(const AtomicString& method, + int32_t argc, + const NativeValue* argv, + Dart_Handle dart_object) { + return Native_NewNull(); +} + +} // namespace webf \ No newline at end of file diff --git a/bridge/core/geometry/dom_matrix_read_only.d.ts b/bridge/core/geometry/dom_matrix_read_only.d.ts new file mode 100644 index 0000000000..a81735efb2 --- /dev/null +++ b/bridge/core/geometry/dom_matrix_read_only.d.ts @@ -0,0 +1,53 @@ +import {DOMMatrixInit} from "./dom_matrix_init"; +import {DOMMatrix} from "./dom_matrix"; +import {DOMPoint} from "./dom_point"; + +interface DOMMatrixReadOnly { + readonly is2D: DartImpl; + readonly isIdentity: DartImpl; + m11: number; + m12: number; + m13: number; + m14: number; + m21: number; + m22: number; + m23: number; + m24: number; + m31: number; + m32: number; + m33: number; + m34: number; + m41: number; + m42: number; + m43: number; + m44: number; + a: number; + b: number; + c: number; + d: number; + e: number; + f: number; + flipX(): DOMMatrix; + flipY(): DOMMatrix; + inverse(): DOMMatrix; + multiply(matrix: DOMMatrix): DOMMatrix; + rotateAxisAngle(x?:number, y?:number, z?:number, angle?:number): DOMMatrix; + rotate(x?:number, y?:number, z?:number): DOMMatrix; + rotateFromVector(x?:number, y?:number): DOMMatrix; + scale(sx?: number, sy?: number, sz?: number, ox?: number, oy?: number, oz?: number): DOMMatrix; + scale3d(scale?: number, ox?: number, oy?: number, oz?: number): DOMMatrix; + scaleNonUniform(sx?: number, sy?: number): DOMMatrix; + skewX(sx?: number): DOMMatrix; + skewY(sy?: number): DOMMatrix; + // toFloat32Array(): number[]; + // toFloat64Array(): number[]; + // TODO + // toJSON(): DartImpl; + toString(): string; + transformPoint(point: DOMPoint): DOMPoint; + translate(tx?:number, ty?:number, tz?:number): DOMMatrix; + // fromFloat32Array(): StaticMethod; + // fromFloat64Array(): StaticMethod; + fromMatrix(matrix: DOMMatrix): StaticMethod; + new(init?: number[] | DOMMatrixInit): DOMMatrixReadOnly; +} \ No newline at end of file diff --git a/bridge/core/geometry/dom_matrix_read_only.h b/bridge/core/geometry/dom_matrix_read_only.h new file mode 100644 index 0000000000..dc489f6e49 --- /dev/null +++ b/bridge/core/geometry/dom_matrix_read_only.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#ifndef WEBF_CORE_GEOMETRY_DOM_MATRIX_READONLY_H_ +#define WEBF_CORE_GEOMETRY_DOM_MATRIX_READONLY_H_ + +#include "bindings/qjs/script_wrappable.h" +#include "core/binding_object.h" +#include "qjs_dom_matrix_init.h" +#include "qjs_union_sequencedoubledom_matrix_init.h" + +namespace webf { + +class DOMMatrix; +class DOMPoint; + +class DOMMatrixReadOnly : public BindingObject { + DEFINE_WRAPPERTYPEINFO(); + + public: + using ImplType = DOMMatrixReadOnly*; + static DOMMatrixReadOnly* Create(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state); + static DOMMatrixReadOnly* Create(ExecutingContext* context, + ExceptionState& exception_state); + static DOMMatrix* fromMatrix(ExecutingContext* context, DOMMatrixReadOnly* matrix, ExceptionState& exception_state); + + DOMMatrixReadOnly() = delete; + explicit DOMMatrixReadOnly(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state); + explicit DOMMatrixReadOnly(ExecutingContext* context, ExceptionState& exception_state); + + virtual bool IsDOMMatrix() const { return false; } + double m11() const; + void setM11(double v, ExceptionState& exception_state); + double m12() const; + void setM12(double v, ExceptionState& exception_state); + double m13() const; + void setM13(double v, ExceptionState& exception_state); + double m14() const; + void setM14(double v, ExceptionState& exception_state); + double m21() const; + void setM21(double v, ExceptionState& exception_state); + double m22() const; + void setM22(double v, ExceptionState& exception_state); + double m23() const; + void setM23(double v, ExceptionState& exception_state); + double m24() const; + void setM24(double v, ExceptionState& exception_state); + double m31() const; + void setM31(double v, ExceptionState& exception_state); + double m32() const; + void setM32(double v, ExceptionState& exception_state); + double m33() const; + void setM33(double v, ExceptionState& exception_state); + double m34() const; + void setM34(double v, ExceptionState& exception_state); + double m41() const; + void setM41(double v, ExceptionState& exception_state); + double m42() const; + void setM42(double v, ExceptionState& exception_state); + double m43() const; + void setM43(double v, ExceptionState& exception_state); + double m44() const; + void setM44(double v, ExceptionState& exception_state); + double a() const { return m11(); } + void setA(double v, ExceptionState& exception_state) { setM11(v, exception_state); } + double b() const { return m12(); } + void setB(double v, ExceptionState& exception_state) { setM12(v, exception_state); } + double c() const { return m21(); } + void setC(double v, ExceptionState& exception_state) { setM21(v, exception_state); } + double d() const { return m22(); } + void setD(double v, ExceptionState& exception_state) { setM22(v, exception_state); } + double e() const { return m41(); } + void setE(double v, ExceptionState& exception_state) { setM41(v, exception_state); } + double f() const { return m42(); } + void setF(double v, ExceptionState& exception_state) { setM42(v, exception_state); } + + DOMMatrix* flipX(ExceptionState& exception_state) const; + DOMMatrix* flipY(ExceptionState& exception_state) const; + DOMMatrix* inverse(ExceptionState& exception_state) const; + DOMMatrix* multiply(DOMMatrix* matrix, ExceptionState& exception_state) const; + DOMMatrix* rotateAxisAngle(ExceptionState& exception_state) const; + DOMMatrix* rotateAxisAngle(double x, ExceptionState& exception_state) const; + DOMMatrix* rotateAxisAngle(double x, double y, ExceptionState& exception_state) const; + DOMMatrix* rotateAxisAngle(double x, double y, double z, ExceptionState& exception_state) const; + DOMMatrix* rotateAxisAngle(double x, double y, double z, double angle, ExceptionState& exception_state) const; + DOMMatrix* rotate(ExceptionState& exception_state) const; + DOMMatrix* rotate(double x, ExceptionState& exception_state) const; + DOMMatrix* rotate(double x, double y, ExceptionState& exception_state) const; + DOMMatrix* rotate(double x, double y, double z, ExceptionState& exception_state) const; + DOMMatrix* rotateFromVector(ExceptionState& exception_state) const; + DOMMatrix* rotateFromVector(double x, ExceptionState& exception_state) const; + DOMMatrix* rotateFromVector(double x, double y, ExceptionState& exception_state) const; + DOMMatrix* scale(ExceptionState& exception_state) const; + DOMMatrix* scale(double sx, ExceptionState& exception_state) const; + DOMMatrix* scale(double sx, double sy, ExceptionState& exception_state) const; + DOMMatrix* scale(double sx, double sy, double sz, ExceptionState& exception_state) const; + DOMMatrix* scale(double sx, double sy, double sz, double ox, ExceptionState& exception_state) const; + DOMMatrix* scale(double sx, double sy, double sz, double ox, double oy, ExceptionState& exception_state) const; + DOMMatrix* scale(double sx, double sy, double sz, double ox, double oy, double oz, ExceptionState& exception_state) const; + DOMMatrix* scale3d(ExceptionState& exception_state) const; + DOMMatrix* scale3d(double scale, ExceptionState& exception_state) const; + DOMMatrix* scale3d(double scale, double ox, ExceptionState& exception_state) const; + DOMMatrix* scale3d(double scale, double ox, double oy, ExceptionState& exception_state) const; + DOMMatrix* scale3d(double scale, double ox, double oy, double oz, ExceptionState& exception_state) const; + DOMMatrix* scaleNonUniform(ExceptionState& exception_state) const; + DOMMatrix* scaleNonUniform(double sx, ExceptionState& exception_state) const; + DOMMatrix* scaleNonUniform(double sx, double sy, ExceptionState& exception_state) const; + DOMMatrix* skewX(ExceptionState& exception_state) const; + DOMMatrix* skewX(double sx, ExceptionState& exception_state) const; + DOMMatrix* skewY(ExceptionState& exception_state) const; + DOMMatrix* skewY(double sy, ExceptionState& exception_state) const; + // toJSON(): DartImpl; + + AtomicString toString(ExceptionState& exception_state) const; + DOMPoint* transformPoint(DOMPoint* point, ExceptionState& exception_state) const; + DOMMatrix* translate(ExceptionState& exception_state) const; + DOMMatrix* translate(double tx, ExceptionState& exception_state) const; + DOMMatrix* translate(double tx, double ty, ExceptionState& exception_state) const; + DOMMatrix* translate(double tx, double ty, double tz, ExceptionState& exception_state) const; + + NativeValue HandleCallFromDartSide(const AtomicString& method, + int32_t argc, + const NativeValue* argv, + Dart_Handle dart_object) override; + protected: + explicit DOMMatrixReadOnly(ExecutingContext* context, NativeBindingObject* native_binding_object); + +private: + [[nodiscard]] double getMatrixProperty(const AtomicString& prop) const; + void setMatrixProperty(const AtomicString& prop, double v, ExceptionState& exception_state); + +}; + +} // namespace webf + +#endif // WEBF_CORE_GEOMETRY_DOM_MATRIX_READONLY_H_ diff --git a/bridge/core/geometry/dom_matrix_readonly.cc b/bridge/core/geometry/dom_matrix_readonly.cc deleted file mode 100644 index 53e8f388a2..0000000000 --- a/bridge/core/geometry/dom_matrix_readonly.cc +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2022-present The WebF authors. All rights reserved. - */ - -#include "dom_matrix_readonly.h" -#include "foundation/native_value_converter.h" - -namespace webf { - -DOMMatrixReadonly* DOMMatrixReadonly::Create(ExecutingContext* context, - const std::shared_ptr& init, - ExceptionState& exception_state) { - return MakeGarbageCollected(context, init, exception_state); -} - -DOMMatrixReadonly::DOMMatrixReadonly(ExecutingContext* context, - const std::shared_ptr& init, - ExceptionState& exception_state) - : BindingObject(context->ctx()) { - NativeValue arguments[1]; - if (init->IsDomString()) { - arguments[0] = NativeValueConverter::ToNativeValue(ctx(), init->GetAsDomString()); - } else if (init->IsSequenceDouble()) { - arguments[0] = NativeValueConverter>::ToNativeValue(init->GetAsSequenceDouble()); - } - GetExecutingContext()->dartMethodPtr()->createBindingObject(GetExecutingContext()->isDedicated(), - GetExecutingContext()->contextId(), bindingObject(), - CreateBindingObjectType::kCreateDOMMatrix, arguments, 1); -} - -NativeValue DOMMatrixReadonly::HandleCallFromDartSide(const AtomicString& method, - int32_t argc, - const NativeValue* argv, - Dart_Handle dart_object) { - return Native_NewNull(); -} - -} // namespace webf \ No newline at end of file diff --git a/bridge/core/geometry/dom_matrix_readonly.d.ts b/bridge/core/geometry/dom_matrix_readonly.d.ts deleted file mode 100644 index 1bd87e7dc3..0000000000 --- a/bridge/core/geometry/dom_matrix_readonly.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -interface DOMMatrixReadonly { - new(init: string | double[]): DOMMatrix; -} \ No newline at end of file diff --git a/bridge/core/geometry/dom_matrix_readonly.h b/bridge/core/geometry/dom_matrix_readonly.h deleted file mode 100644 index 382643bdbd..0000000000 --- a/bridge/core/geometry/dom_matrix_readonly.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2022-present The WebF authors. All rights reserved. - */ - -#ifndef WEBF_CORE_GEOMETRY_DOM_MATRIX_READONLY_H_ -#define WEBF_CORE_GEOMETRY_DOM_MATRIX_READONLY_H_ - -#include "bindings/qjs/script_wrappable.h" -#include "core/binding_object.h" -#include "qjs_union_dom_string_sequencedouble.h" - -namespace webf { - -class DOMMatrixReadonly : public BindingObject { - DEFINE_WRAPPERTYPEINFO(); - - public: - using ImplType = DOMMatrixReadonly*; - static DOMMatrixReadonly* Create(ExecutingContext* context, - const std::shared_ptr& init, - ExceptionState& exception_state); - - DOMMatrixReadonly() = delete; - explicit DOMMatrixReadonly(ExecutingContext* context, - const std::shared_ptr& init, - ExceptionState& exception_state); - - NativeValue HandleCallFromDartSide(const AtomicString& method, - int32_t argc, - const NativeValue* argv, - Dart_Handle dart_object) override; -}; - -} // namespace webf - -#endif // WEBF_CORE_GEOMETRY_DOM_MATRIX_READONLY_H_ diff --git a/bridge/core/geometry/dom_point.cc b/bridge/core/geometry/dom_point.cc new file mode 100644 index 0000000000..19507d1613 --- /dev/null +++ b/bridge/core/geometry/dom_point.cc @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#include "dom_point.h" +#include "core/executing_context.h" + +namespace webf { + +DOMPoint* DOMPoint::Create(webf::ExecutingContext* context, webf::ExceptionState& exception_state) { + return MakeGarbageCollected(context, exception_state); +} +DOMPoint* DOMPoint::Create(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state) { + return MakeGarbageCollected(context, init, exception_state); +} +DOMPoint* DOMPoint::Create(ExecutingContext* context, + const std::shared_ptr& init, + double y, + ExceptionState& exception_state) { + return MakeGarbageCollected(context, init, y, exception_state); +} +DOMPoint* DOMPoint::Create(ExecutingContext* context, + const std::shared_ptr& init, + double y, + double z, + ExceptionState& exception_state) { + return MakeGarbageCollected(context, init, y, z, exception_state); +} +DOMPoint* DOMPoint::Create(ExecutingContext* context, + const std::shared_ptr& init , + double y, + double z, + double w, + ExceptionState& exception_state) { + return MakeGarbageCollected(context, init, y, z, w, exception_state); +} + +DOMPoint::DOMPoint(webf::ExecutingContext* context, webf::ExceptionState& exception_state) + : DOMPointReadOnly(context, exception_state) {} +DOMPoint::DOMPoint(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state) + : DOMPointReadOnly(context, init, exception_state) {} +DOMPoint::DOMPoint(ExecutingContext* context, + const std::shared_ptr& init, + double y, + ExceptionState& exception_state) + : DOMPointReadOnly(context, init, y, exception_state) {} +DOMPoint::DOMPoint(ExecutingContext* context, + const std::shared_ptr& init, + double y, + double z, + ExceptionState& exception_state) + : DOMPointReadOnly(context, init, y, z, exception_state) {} +DOMPoint::DOMPoint(ExecutingContext* context, + const std::shared_ptr& init, + double y, + double z, + double w, + ExceptionState& exception_state) + : DOMPointReadOnly(context, init, y, z, w, exception_state) {} +DOMPoint::DOMPoint(webf::ExecutingContext* context, webf::NativeBindingObject* native_binding_object) + : DOMPointReadOnly(context, native_binding_object) {} + +} // namespace webf diff --git a/bridge/core/geometry/dom_point.d.ts b/bridge/core/geometry/dom_point.d.ts new file mode 100644 index 0000000000..380d0c6897 --- /dev/null +++ b/bridge/core/geometry/dom_point.d.ts @@ -0,0 +1,7 @@ +import {DOMPointInit} from "./dom_point_init"; +import {DOMPointReadOnly} from "./dom_point_read_only"; + +interface DOMPoint extends DOMPointReadOnly { + fromPoint(point: DOMPoint): StaticMethod; + new(x?: number | DOMPointInit, y?: number, z?: number, w?: number): DOMPoint; +} \ No newline at end of file diff --git a/bridge/core/geometry/dom_point.h b/bridge/core/geometry/dom_point.h new file mode 100644 index 0000000000..d62b31d797 --- /dev/null +++ b/bridge/core/geometry/dom_point.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#ifndef WEBF_CORE_GEOMETRY_DOM_POINT_H_ +#define WEBF_CORE_GEOMETRY_DOM_POINT_H_ + +#include "dom_point_read_only.h" + +namespace webf { + +class DOMPoint : public DOMPointReadOnly { + DEFINE_WRAPPERTYPEINFO(); + + public: + using ImplType = DOMPoint*; + static DOMPoint* Create(ExecutingContext* context, ExceptionState& exception_state); + static DOMPoint* Create(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state); + static DOMPoint* Create(ExecutingContext* context, + const std::shared_ptr& init, + double y, + ExceptionState& exception_state); + static DOMPoint* Create(ExecutingContext* context, + const std::shared_ptr& init, + double y, + double z, + ExceptionState& exception_state); + static DOMPoint* Create(ExecutingContext* context, + const std::shared_ptr& init, + double y, + double z, + double w, + ExceptionState& exception_state); + DOMPoint() = delete; + explicit DOMPoint(ExecutingContext* context, ExceptionState& exception_state); + explicit DOMPoint(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state); + explicit DOMPoint(ExecutingContext* context, + const std::shared_ptr& init, + double y, + ExceptionState& exception_state); + explicit DOMPoint(ExecutingContext* context, + const std::shared_ptr& init, + double y, + double z, + ExceptionState& exception_state); + explicit DOMPoint(ExecutingContext* context, + const std::shared_ptr& init, + double y, + double z, + double w, + ExceptionState& exception_state); + explicit DOMPoint(ExecutingContext* context, NativeBindingObject* native_binding_object); + + [[nodiscard]] bool IsDOMPoint() const override { return true; } +}; +template <> +struct DowncastTraits { + static bool AllowFrom(const DOMPointReadOnly& matrix) { return matrix.IsDOMPoint(); } +}; +} // namespace webf + +#endif // WEBF_CORE_GEOMETRY_DOM_POINT_H_ diff --git a/bridge/core/geometry/dom_point_init.d.ts b/bridge/core/geometry/dom_point_init.d.ts new file mode 100644 index 0000000000..006739526a --- /dev/null +++ b/bridge/core/geometry/dom_point_init.d.ts @@ -0,0 +1,8 @@ +// @ts-ignore +@Dictionary() +export interface DOMPointInit { + x?: number, + y?: number, + z?: number, + w?: number, +} \ No newline at end of file diff --git a/bridge/core/geometry/dom_point_read_only.cc b/bridge/core/geometry/dom_point_read_only.cc new file mode 100644 index 0000000000..c586f6cb58 --- /dev/null +++ b/bridge/core/geometry/dom_point_read_only.cc @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#include "dom_point_read_only.h" +#include "binding_call_methods.h" +#include "native_value_converter.h" +#include "bindings/qjs/converter_impl.h" +#include "core/executing_context.h" +#include "core/geometry/dom_point.h" +#include "core/geometry/dom_matrix.h" + +namespace webf { + +DOMPointReadOnly* DOMPointReadOnly::Create(webf::ExecutingContext* context, webf::ExceptionState& exception_state) { + return MakeGarbageCollected(context, exception_state); +} +DOMPointReadOnly* DOMPointReadOnly::Create(webf::ExecutingContext* context, + const std::shared_ptr& init, + webf::ExceptionState& exception_state) { + return MakeGarbageCollected(context, init, exception_state); +} +DOMPointReadOnly* DOMPointReadOnly::Create(webf::ExecutingContext* context, + const std::shared_ptr& init, + double y, + webf::ExceptionState& exception_state) { + return MakeGarbageCollected(context, init, y, exception_state); +} +DOMPointReadOnly* DOMPointReadOnly::Create(webf::ExecutingContext* context, + const std::shared_ptr& init, + double y, + double z, + webf::ExceptionState& exception_state) { + return MakeGarbageCollected(context, init, y, z, exception_state); +} +DOMPointReadOnly* DOMPointReadOnly::Create(webf::ExecutingContext* context, + const std::shared_ptr& init, + double y, + double z, + double w, + webf::ExceptionState& exception_state) { + return MakeGarbageCollected(context, init, y, z, w, exception_state); +} + +DOMPoint* DOMPointReadOnly::fromPoint(ExecutingContext* context, DOMPointReadOnly* point, ExceptionState& exception_state) { + NativeValue arguments[] = {NativeValueConverter>::ToNativeValue(point)}; + AtomicString module_name = AtomicString(context->ctx(), "DOMPoint"); + AtomicString method_name = AtomicString(context->ctx(), "fromPoint"); + + NativeValue* dart_result = context->dartMethodPtr()->invokeModule( + context->isDedicated(), nullptr, context->contextId(), context->dartIsolateContext()->profiler()->link_id(), + module_name.ToNativeString(context->ctx()).release(), method_name.ToNativeString(context->ctx()).release(), + arguments, nullptr); + + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(*dart_result); + + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(context, native_binding_object); +} + +DOMPointReadOnly::DOMPointReadOnly(webf::ExecutingContext* context, webf::ExceptionState& exception_state) + : BindingObject(context->ctx()) { + GetExecutingContext()->dartMethodPtr()->createBindingObject(GetExecutingContext()->isDedicated(), + GetExecutingContext()->contextId(), bindingObject(), + CreateBindingObjectType::kCreateDOMPoint, nullptr, 0); +} + +DOMPointReadOnly::DOMPointReadOnly(webf::ExecutingContext* context, const std::shared_ptr& init, webf::ExceptionState& exception_state) + : BindingObject(context->ctx()) { + this->createWithDOMPointInit(context, exception_state, init); +} +DOMPointReadOnly::DOMPointReadOnly(webf::ExecutingContext* context, + const std::shared_ptr& init, + double y, + webf::ExceptionState& exception_state) + : BindingObject(context->ctx()) { + this->createWithDOMPointInit(context, exception_state, init, y); +} +DOMPointReadOnly::DOMPointReadOnly(webf::ExecutingContext* context, + const std::shared_ptr& init, + double y, + double w, + webf::ExceptionState& exception_state) + : BindingObject(context->ctx()) { + this->createWithDOMPointInit(context, exception_state, init, y, w); +} + +DOMPointReadOnly::DOMPointReadOnly(webf::ExecutingContext* context, + const std::shared_ptr& init, + double y, + double w, + double z, + webf::ExceptionState& exception_state) + : BindingObject(context->ctx()) { + this->createWithDOMPointInit(context, exception_state, init, y, w, z); +} + +DOMPointReadOnly::DOMPointReadOnly(webf::ExecutingContext* context, webf::NativeBindingObject* native_binding_object) + : BindingObject(context->ctx(), native_binding_object) {} + +void DOMPointReadOnly::createWithDOMPointInit(ExecutingContext* context, + ExceptionState& exception_state, + const std::shared_ptr& init, + double y, + double w, + double z) { + if (init->IsDOMPointInit()) { + // DOMPointReadOnly({x:1,y:2,z:3,w:4}) + auto domPointInit = init->GetAsDOMPointInit(); + double x = domPointInit->hasX() ? domPointInit->x() : 0; + double y = domPointInit->hasY() ? domPointInit->y() : 0; + double z = domPointInit->hasZ() ? domPointInit->z() : 0; + double w = domPointInit->hasW() ? domPointInit->w() : 1; + + NativeValue arguments[] = { + NativeValueConverter::ToNativeValue(x), + NativeValueConverter::ToNativeValue(y), + NativeValueConverter::ToNativeValue(z), + NativeValueConverter::ToNativeValue(w), + }; + GetExecutingContext()->dartMethodPtr()->createBindingObject( + GetExecutingContext()->isDedicated(), GetExecutingContext()->contextId(), bindingObject(), + CreateBindingObjectType::kCreateDOMPoint, arguments, sizeof(arguments) / sizeof(NativeValue)); + } else if (init->IsDouble()) { + // DOMPointReadOnly(x,y,z,w) + NativeValue arguments[] = {NativeValueConverter::ToNativeValue(init->GetAsDouble()), + NativeValueConverter::ToNativeValue(y), + NativeValueConverter::ToNativeValue(w), + NativeValueConverter::ToNativeValue(z)}; + GetExecutingContext()->dartMethodPtr()->createBindingObject( + GetExecutingContext()->isDedicated(), GetExecutingContext()->contextId(), bindingObject(), + CreateBindingObjectType::kCreateDOMPoint, arguments, sizeof(arguments) / sizeof(NativeValue)); + } +} + +double DOMPointReadOnly::getPointProperty(const AtomicString& prop) const { + NativeValue dart_result = GetBindingProperty(prop, FlushUICommandReason::kDependentsOnElement, ASSERT_NO_EXCEPTION()); + return NativeValueConverter::FromNativeValue(dart_result); +} + +void DOMPointReadOnly::setPointProperty(const AtomicString& prop, double v, ExceptionState& exception_state) { + if (DynamicTo(this)) { + SetBindingProperty(prop, NativeValueConverter::ToNativeValue(v), exception_state); + } +} + +double DOMPointReadOnly::x() const { + return getPointProperty(defined_properties::kx); +} +void DOMPointReadOnly::setX(double v, ExceptionState& exception_state) { + setPointProperty(defined_properties::kx, v, exception_state); +} +double DOMPointReadOnly::y() { + return getPointProperty(defined_properties::ky); +} +void DOMPointReadOnly::setY(double v, ExceptionState& exception_state) { + setPointProperty(defined_properties::ky, v, exception_state); +} +double DOMPointReadOnly::z() const { + return getPointProperty(defined_properties::kz); +} +void DOMPointReadOnly::setZ(double v, ExceptionState& exception_state) { + setPointProperty(defined_properties::kz, v, exception_state); +} +double DOMPointReadOnly::w() const { + return getPointProperty(defined_properties::kw); +} +void DOMPointReadOnly::setW(double v, ExceptionState& exception_state) { + setPointProperty(defined_properties::kw, v, exception_state); +} + +DOMPoint* DOMPointReadOnly::matrixTransform(DOMMatrix* matrix, ExceptionState& exception_state) const { + NativeValue arguments[] = { + NativeValueConverter>::ToNativeValue(matrix) + }; + NativeValue value = InvokeBindingMethod(binding_call_methods::kmatrixTransform, sizeof(arguments) / sizeof(NativeValue), + arguments, FlushUICommandReason::kDependentsOnElement, exception_state); + NativeBindingObject* native_binding_object = + NativeValueConverter>::FromNativeValue(value); + + if (native_binding_object == nullptr) + return nullptr; + return MakeGarbageCollected(GetExecutingContext(), native_binding_object); +} + +NativeValue DOMPointReadOnly::HandleCallFromDartSide(const AtomicString& method, + int32_t argc, + const NativeValue* argv, + Dart_Handle dart_object) { + return Native_NewNull(); +} + +} // namespace webf diff --git a/bridge/core/geometry/dom_point_read_only.d.ts b/bridge/core/geometry/dom_point_read_only.d.ts new file mode 100644 index 0000000000..874275b235 --- /dev/null +++ b/bridge/core/geometry/dom_point_read_only.d.ts @@ -0,0 +1,13 @@ +import {DOMPointInit} from "./dom_point_init"; +import {DOMMatrix} from "./dom_matrix"; +import {DOMPoint} from "./dom_point"; + +interface DOMPointReadOnly { + x: number; + y: number; + z: number; + w: number; + matrixTransform(matrix: DOMMatrix): DOMPoint; + fromPoint(point: DOMPoint): StaticMethod; + new(x?: number | DOMPointInit, y?: number, z?: number, w?: number): DOMPointReadOnly; +} \ No newline at end of file diff --git a/bridge/core/geometry/dom_point_read_only.h b/bridge/core/geometry/dom_point_read_only.h new file mode 100644 index 0000000000..833574fd86 --- /dev/null +++ b/bridge/core/geometry/dom_point_read_only.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#ifndef WEBF_CORE_GEOMETRY_DOM_POINT_READONLY_H_ +#define WEBF_CORE_GEOMETRY_DOM_POINT_READONLY_H_ + +#include "bindings/qjs/script_wrappable.h" +#include "core/binding_object.h" +#include "qjs_dom_point_init.h" +#include "qjs_union_doubledom_point_init.h" + +namespace webf { + +class DOMPoint; +class DOMMatrix; + +class DOMPointReadOnly : public BindingObject { + DEFINE_WRAPPERTYPEINFO(); + + public: + using ImplType = DOMPointReadOnly*; + + static DOMPointReadOnly* Create(ExecutingContext* context, ExceptionState& exception_state); + static DOMPointReadOnly* Create(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state); + static DOMPointReadOnly* Create(ExecutingContext* context, + const std::shared_ptr& init, + double y, + ExceptionState& exception_state); + static DOMPointReadOnly* Create(ExecutingContext* context, + const std::shared_ptr& init, + double y, + double z, + ExceptionState& exception_state); + static DOMPointReadOnly* Create(ExecutingContext* context, + const std::shared_ptr& init, + double y, + double z, + double w, + ExceptionState& exception_state); + static DOMPoint* fromPoint(ExecutingContext* context, DOMPointReadOnly* point, ExceptionState& exception_state); + + DOMPointReadOnly() = delete; + + explicit DOMPointReadOnly(ExecutingContext* context, ExceptionState& exception_state); + explicit DOMPointReadOnly(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state); + explicit DOMPointReadOnly(ExecutingContext* context, + const std::shared_ptr& init, + double y, + ExceptionState& exception_state); + explicit DOMPointReadOnly(ExecutingContext* context, + const std::shared_ptr& init, + double y, + double z, + ExceptionState& exception_state); + explicit DOMPointReadOnly(ExecutingContext* context, + const std::shared_ptr& init, + double y, + double z, + double w, + ExceptionState& exception_state); + + virtual bool IsDOMPoint() const { return false; } + + double x() const; + void setX(double v, ExceptionState& exception_state); + double y(); + void setY(double v, ExceptionState& exception_state); + double z() const; + void setZ(double v, ExceptionState& exception_state); + double w() const; + void setW(double v, ExceptionState& exception_state); + + DOMPoint* matrixTransform(DOMMatrix* matrix, ExceptionState& exception_state) const; + + NativeValue HandleCallFromDartSide(const AtomicString& method, + int32_t argc, + const NativeValue* argv, + Dart_Handle dart_object) override; +protected: + explicit DOMPointReadOnly(ExecutingContext* context, NativeBindingObject* native_binding_object); + + private: + void createWithDOMPointInit(ExecutingContext* context, + ExceptionState& exception_state, + const std::shared_ptr& init, + double y = 0, + double w = 0, + double z = 1); + + [[nodiscard]] double getPointProperty(const AtomicString& prop) const; + void setPointProperty(const AtomicString& prop, double v, ExceptionState& exception_state); +}; + +} // namespace webf + +#endif // WEBF_CORE_GEOMETRY_DOM_POINT_READONLY_H_ diff --git a/bridge/core/html/canvas/canvas_pattern.cc b/bridge/core/html/canvas/canvas_pattern.cc index 85c143c4c8..0ebeadaeff 100644 --- a/bridge/core/html/canvas/canvas_pattern.cc +++ b/bridge/core/html/canvas/canvas_pattern.cc @@ -4,6 +4,7 @@ #include "canvas_pattern.h" #include "binding_call_methods.h" +#include "core/executing_context.h" #include "foundation/native_value_converter.h" namespace webf { diff --git a/bridge/core/html/canvas/canvas_rendering_context_2d.cc b/bridge/core/html/canvas/canvas_rendering_context_2d.cc index cb193ffcbb..2bd4ca9722 100644 --- a/bridge/core/html/canvas/canvas_rendering_context_2d.cc +++ b/bridge/core/html/canvas/canvas_rendering_context_2d.cc @@ -137,6 +137,61 @@ void CanvasRenderingContext2D::setStrokeStyle(const std::shared_ptr radii, + ExceptionState& exception_state) { + std::vector radii_vector; + if (radii->IsDouble()) { + radii_vector.emplace_back(radii->GetAsDouble()); + } else if (radii->IsSequenceDouble()) { + std::vector radii_sequence = radii->GetAsSequenceDouble(); + radii_vector.assign(radii_sequence.begin(), radii_sequence.end()); + } + + NativeValue arguments[] = {NativeValueConverter::ToNativeValue(x), + NativeValueConverter::ToNativeValue(y), + NativeValueConverter::ToNativeValue(w), + NativeValueConverter::ToNativeValue(h), + NativeValueConverter>::ToNativeValue(radii_vector)}; + + InvokeBindingMethod(binding_call_methods::kroundRect, sizeof(arguments) / sizeof(NativeValue), arguments, + FlushUICommandReason::kDependentsOnElement, exception_state); +} + +void CanvasRenderingContext2D::fill(webf::ExceptionState& exception_state) { + InvokeBindingMethod(binding_call_methods::kfill, 0, nullptr, FlushUICommandReason::kDependentsOnElement, + exception_state); +} + +void CanvasRenderingContext2D::fill(std::shared_ptr pathOrPattern, + webf::ExceptionState& exception_state) { + if (pathOrPattern->IsDomString()) { + NativeValue arguments[] = { + NativeValueConverter::ToNativeValue(ctx(), pathOrPattern->GetAsDomString())}; + InvokeBindingMethod(binding_call_methods::kfill, sizeof(arguments) / sizeof(NativeValue), arguments, + FlushUICommandReason::kDependentsOnElement, exception_state); + } else if (pathOrPattern->IsPath2D()) { + NativeValue arguments[] = { + NativeValueConverter>::ToNativeValue(pathOrPattern->GetAsPath2D())}; + InvokeBindingMethod(binding_call_methods::kfill, sizeof(arguments) / sizeof(NativeValue), arguments, + FlushUICommandReason::kDependentsOnElement, exception_state); + } +} + +void CanvasRenderingContext2D::fill(std::shared_ptr pathOrPattern, + const webf::AtomicString& fillRule, + webf::ExceptionState& exception_state) { + assert(pathOrPattern->IsPath2D()); + NativeValue arguments[] = { + NativeValueConverter>::ToNativeValue(pathOrPattern->GetAsPath2D()), + NativeValueConverter::ToNativeValue(ctx(), fillRule)}; + InvokeBindingMethod(binding_call_methods::kfill, sizeof(arguments) / sizeof(NativeValue), arguments, + FlushUICommandReason::kDependentsOnElement, exception_state); +} + void CanvasRenderingContext2D::Trace(GCVisitor* visitor) const { if (fill_style_ != nullptr) fill_style_->Trace(visitor); diff --git a/bridge/core/html/canvas/canvas_rendering_context_2d.d.ts b/bridge/core/html/canvas/canvas_rendering_context_2d.d.ts index 741b7d602e..215c757518 100644 --- a/bridge/core/html/canvas/canvas_rendering_context_2d.d.ts +++ b/bridge/core/html/canvas/canvas_rendering_context_2d.d.ts @@ -21,12 +21,12 @@ interface CanvasRenderingContext2D extends CanvasRenderingContext { bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): DartImpl; clearRect(x: number, y: number, w: number, h: number): DartImpl; closePath(): DartImpl; - clip(path?: string): DartImpl; + clip(path?: Path2D, fillRule?: string): DartImpl; drawImage(image: HTMLImageElement, sx: number, sy: number, sw: number, sh: number, dx: number, dy: number, dw: number, dh: number): DartImpl; drawImage(image: HTMLImageElement, dx: number, dy: number, dw: number, dh: number): DartImpl; drawImage(image: HTMLImageElement, dx: number, dy: number): DartImpl; ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, anticlockwise?: boolean): DartImpl; - fill(path?: string): DartImpl; + fill(path?: Path2D | string, fillRule?: string): void; fillRect(x: number, y: number, w: number, h: number): DartImpl; fillText(text: string, x: number, y: number, maxWidth?: number): DartImpl; lineTo(x: number, y: number): DartImpl; @@ -35,8 +35,9 @@ interface CanvasRenderingContext2D extends CanvasRenderingContext { restore(): DartImpl; resetTransform(): DartImpl; rotate(angle: number): DartImpl; + roundRect(x: number, y: number, w: number, h: number, radii: number | number[]): void; quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): DartImpl; - stroke(): DartImpl; + stroke(path?: Path2D): DartImpl; strokeRect(x: number, y: number, w: number, h: number): DartImpl; save(): DartImpl; scale(x: number, y: number): DartImpl; diff --git a/bridge/core/html/canvas/canvas_rendering_context_2d.h b/bridge/core/html/canvas/canvas_rendering_context_2d.h index 07c15c4ade..43bc0b76d7 100644 --- a/bridge/core/html/canvas/canvas_rendering_context_2d.h +++ b/bridge/core/html/canvas/canvas_rendering_context_2d.h @@ -7,9 +7,11 @@ #include "canvas_gradient.h" #include "canvas_pattern.h" +#include "path_2d.h" #include "canvas_rendering_context.h" #include "qjs_union_dom_stringcanvas_gradient.h" #include "qjs_unionhtml_image_elementhtml_canvas_element.h" +#include "qjs_unionpath_2_d_dom_string.h" namespace webf { @@ -44,9 +46,20 @@ class CanvasRenderingContext2D : public CanvasRenderingContext { void setFillStyle(const std::shared_ptr& style, ExceptionState& exception_state); bool IsCanvas2d() const override; + void fill(ExceptionState& exception_state); + void fill(std::shared_ptr pathOrPattern, ExceptionState& exception_state); + void fill(std::shared_ptr pathOrPattern, const AtomicString& fillRule, ExceptionState& exception_state); + std::shared_ptr strokeStyle(); void setStrokeStyle(const std::shared_ptr& style, ExceptionState& exception_state); + void roundRect(double x, + double y, + double w, + double h, + std::shared_ptr radii, + ExceptionState& exception_state); + void Trace(GCVisitor* visitor) const override; private: diff --git a/bridge/core/html/canvas/path_2d.cc b/bridge/core/html/canvas/path_2d.cc new file mode 100644 index 0000000000..88446f0d75 --- /dev/null +++ b/bridge/core/html/canvas/path_2d.cc @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#include "path_2d.h" +#include "binding_call_methods.h" +#include "foundation/native_value_converter.h" + +namespace webf { + +Path2D* Path2D::Create(ExecutingContext* context, ExceptionState& exception_state) { + return MakeGarbageCollected(context, exception_state); +} + +Path2D* Path2D::Create(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state) { + return MakeGarbageCollected(context, init, exception_state); +} + +Path2D::Path2D(ExecutingContext* context, ExceptionState& exception_state) + : BindingObject(context->ctx()) { + GetExecutingContext()->dartMethodPtr()->createBindingObject(GetExecutingContext()->isDedicated(), + GetExecutingContext()->contextId(), bindingObject(), + CreateBindingObjectType::kCreatePath2D, nullptr, 0); +} + +Path2D::Path2D(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state) + : BindingObject(context->ctx()) { + NativeValue arguments[1]; + if (init->IsDomString()) { + arguments[0] = NativeValueConverter::ToNativeValue(ctx(), init->GetAsDomString()); + } else if (init->IsPath2D()) { + arguments[0] = NativeValueConverter>::ToNativeValue(init->GetAsPath2D()); + } + + GetExecutingContext()->dartMethodPtr()->createBindingObject(GetExecutingContext()->isDedicated(), + GetExecutingContext()->contextId(), bindingObject(), + CreateBindingObjectType::kCreatePath2D, arguments, 1); +} + +void Path2D::addPath(Path2D* path, DOMMatrixReadOnly* dom_matrix, ExceptionState& exception_state) { + NativeValue arguments[] = {NativeValueConverter>::ToNativeValue(path), + NativeValueConverter>::ToNativeValue(dom_matrix)}; + InvokeBindingMethod(binding_call_methods::kaddPath, 2, arguments, FlushUICommandReason::kDependentsOnElement, + exception_state); +} + +void Path2D::addPath(webf::Path2D* path, webf::ExceptionState& exception_state) { + NativeValue arguments[] = {NativeValueConverter>::ToNativeValue(path)}; + InvokeBindingMethod(binding_call_methods::kaddPath, 1, arguments, FlushUICommandReason::kDependentsOnElement, + exception_state); +} + +void Path2D::roundRect(double x, + double y, + double w, + double h, + std::shared_ptr radii, + ExceptionState& exception_state) { + std::vector radii_vector; + if (radii->IsDouble()) { + radii_vector.emplace_back(radii->GetAsDouble()); + } else if (radii->IsSequenceDouble()) { + std::vector radii_sequence = radii->GetAsSequenceDouble(); + radii_vector.assign(radii_sequence.begin(), radii_sequence.end()); + } + + NativeValue arguments[] = {NativeValueConverter::ToNativeValue(x), + NativeValueConverter::ToNativeValue(y), + NativeValueConverter::ToNativeValue(w), + NativeValueConverter::ToNativeValue(h), + NativeValueConverter>::ToNativeValue(radii_vector)}; + + InvokeBindingMethod(binding_call_methods::kroundRect, sizeof(arguments) / sizeof(NativeValue), arguments, + FlushUICommandReason::kDependentsOnElement, exception_state); +} + +NativeValue Path2D::HandleCallFromDartSide(const AtomicString& method, + int32_t argc, + const NativeValue* argv, + Dart_Handle dart_object) { + return Native_NewNull(); +} + +} // namespace webf diff --git a/bridge/core/html/canvas/path_2d.d.ts b/bridge/core/html/canvas/path_2d.d.ts new file mode 100644 index 0000000000..882ea6ae30 --- /dev/null +++ b/bridge/core/html/canvas/path_2d.d.ts @@ -0,0 +1,14 @@ +interface Path2D { + closePath(): DartImpl; + moveTo(x: number, y: number): DartImpl; + lineTo(x: number, y: number): DartImpl; + bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): DartImpl; + quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): DartImpl; + arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean): DartImpl; + arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): DartImpl; + ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, anticlockwise?: boolean): DartImpl; + rect(x: number, y: number, w: number, h: number): DartImpl; + roundRect(x: number, y: number, w: number, h: number, radii: number | number[]): void; + addPath(path: Path2D, matrix?: DOMMatrix): void; + new(init?: Path2D | string): Path2D; +} \ No newline at end of file diff --git a/bridge/core/html/canvas/path_2d.h b/bridge/core/html/canvas/path_2d.h new file mode 100644 index 0000000000..65eed77122 --- /dev/null +++ b/bridge/core/html/canvas/path_2d.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#ifndef WEBF_CORE_HTML_CANVAS_CANVAS_PATH_2D_H_ +#define WEBF_CORE_HTML_CANVAS_CANVAS_PATH_2D_H_ + +#include "bindings/qjs/script_wrappable.h" +#include "core/binding_object.h" +#include "core/geometry/dom_matrix.h" +#include "qjs_unionpath_2_d_dom_string.h" +#include "qjs_union_double_sequencedouble.h" + +namespace webf { + +class Path2D : public BindingObject { + DEFINE_WRAPPERTYPEINFO(); + + public: + using ImplType = Path2D*; + static Path2D* Create(ExecutingContext* context, ExceptionState& exception_state); + static Path2D* Create(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state); + Path2D() = delete; + + explicit Path2D(ExecutingContext* context, ExceptionState& exception_state); + explicit Path2D(ExecutingContext* context, + const std::shared_ptr& init, + ExceptionState& exception_state); + + void addPath(Path2D* path, DOMMatrixReadOnly* dom_matrix, ExceptionState& exception_state); + void addPath(Path2D* path, ExceptionState& exception_state); + + void roundRect(double x, + double y, + double w, + double h, + std::shared_ptr radii, + ExceptionState& exception_state); + + NativeValue HandleCallFromDartSide(const AtomicString& method, + int32_t argc, + const NativeValue* argv, + Dart_Handle dart_object) override; + + private: +}; // namespace webf + +} + +#endif // WEBF_CORE_HTML_CANVAS_CANVAS_PATH_2D_H_a \ No newline at end of file diff --git a/bridge/scripts/code_generator/src/idl/analyzer.ts b/bridge/scripts/code_generator/src/idl/analyzer.ts index afc5838b7d..7a32fb455d 100644 --- a/bridge/scripts/code_generator/src/idl/analyzer.ts +++ b/bridge/scripts/code_generator/src/idl/analyzer.ts @@ -304,6 +304,9 @@ function walkProgram(blob: IDLBlob, statement: ts.Statement, definedPropertyColl let mode = new ParameterMode(); f.returnType = getParameterType(m.type, unionTypeCollector, mode); f.returnTypeMode = mode; + if (f.returnTypeMode.staticMethod) { + obj.staticMethods.push(f); + } } break; } diff --git a/bridge/scripts/code_generator/src/idl/declaration.ts b/bridge/scripts/code_generator/src/idl/declaration.ts index c768b2b178..87378c4787 100644 --- a/bridge/scripts/code_generator/src/idl/declaration.ts +++ b/bridge/scripts/code_generator/src/idl/declaration.ts @@ -71,6 +71,7 @@ export class ClassObject { props: PropsDeclaration[] = []; indexedProp?: IndexedPropertyDeclaration; methods: FunctionDeclaration[] = []; + staticMethods: FunctionDeclaration[] = []; construct?: FunctionDeclaration; kind: ClassObjectKind = ClassObjectKind.interface; } diff --git a/bridge/scripts/code_generator/src/idl/generateSource.ts b/bridge/scripts/code_generator/src/idl/generateSource.ts index ef15cc0a10..d7922f60f1 100644 --- a/bridge/scripts/code_generator/src/idl/generateSource.ts +++ b/bridge/scripts/code_generator/src/idl/generateSource.ts @@ -316,7 +316,7 @@ function generateNativeValueTypeConverter(type: ParameterType): string { function generateRequiredInitBody(argument: FunctionArguments, argsIndex: number) { let type = generateIDLTypeConverter(argument.type, !argument.required); - let hasArgumentCheck = type.indexOf('Element') >= 0 || type.indexOf('Node') >= 0 || type === 'EventTarget' || type.indexOf('DOMMatrix') >= 0; + let hasArgumentCheck = type.indexOf('Element') >= 0 || type.indexOf('Node') >= 0 || type === 'EventTarget' || type.indexOf('DOMMatrix') >= 0 || type.indexOf('Path2D') >= 0; let body = ''; if (argument.isDotDotDot) { @@ -359,7 +359,7 @@ ${returnValueAssignment.length > 0 ? `return Converter<${generateIDLTypeConverte `.trim(); } -function generateOptionalInitBody(blob: IDLBlob, declare: FunctionDeclaration, argument: FunctionArguments, argsIndex: number, previousArguments: string[], options: GenFunctionBodyOptions) { +function generateOptionalInitBody(blob: IDLBlob, declare: FunctionDeclaration, argument: FunctionArguments, argsIndex: number, argsLength: number, previousArguments: string[], options: GenFunctionBodyOptions) { let call = ''; let returnValueAssignment = ''; if (declare.returnType.value != FunctionArgumentType.void) { @@ -383,7 +383,9 @@ if (UNLIKELY(exception_state.HasException())) { if (argc <= ${argsIndex + 1}) { ${call} break; -}`; +} +${(argsIndex) + 1 == argsLength ? call : ''} +`; } function generateFunctionCallBody(blob: IDLBlob, declaration: FunctionDeclaration, options: GenFunctionBodyOptions = { @@ -412,7 +414,7 @@ function generateFunctionCallBody(blob: IDLBlob, declaration: FunctionDeclaratio let totalArguments: string[] = requiredArguments.slice(); for (let i = minimalRequiredArgc; i < declaration.args.length; i++) { - optionalArgumentsInit.push(generateOptionalInitBody(blob, declaration, declaration.args[i], i, totalArguments, options)); + optionalArgumentsInit.push(generateOptionalInitBody(blob, declaration, declaration.args[i], i, declaration.args.length, totalArguments, options)); totalArguments.push(`args_${declaration.args[i].name}`); } @@ -587,11 +589,16 @@ export function generateCppSource(blob: IDLBlob, options: GenerateOptions) { } } + function addObjectStaticMethods(method: FunctionDeclaration, i: number) { + options.staticMethodsInstallList.push(`{"${method.name}", ${method.name}, ${method.args.length}}`); + } + object.props.forEach(addObjectProps); let overloadMethods: {[key: string]: FunctionDeclaration[] } = {}; let filtedMethods: FunctionDeclaration[] = []; object.methods.forEach(addObjectMethods); + object.staticMethods.forEach(addObjectStaticMethods); if (object.construct) { options.constructorInstallList.push(`{defined_properties::k${className}.Impl(), nullptr, nullptr, constructor}`) diff --git a/bridge/scripts/code_generator/src/idl/generator.ts b/bridge/scripts/code_generator/src/idl/generator.ts index 7499721da0..210f49378d 100644 --- a/bridge/scripts/code_generator/src/idl/generator.ts +++ b/bridge/scripts/code_generator/src/idl/generator.ts @@ -7,6 +7,7 @@ export function generateSupportedOptions(): GenerateOptions { let classMethodsInstallList: string[] = []; let constructorInstallList: string[] = []; let classPropsInstallList: string[] = []; + let staticMethodsInstallList: string[] = []; let indexedProperty: string = ''; let wrapperTypeInfoInit = ''; @@ -14,6 +15,7 @@ export function generateSupportedOptions(): GenerateOptions { globalFunctionInstallList, classPropsInstallList, classMethodsInstallList, + staticMethodsInstallList, constructorInstallList, indexedProperty, wrapperTypeInfoInit @@ -25,6 +27,7 @@ export type GenerateOptions = { classMethodsInstallList: string[]; constructorInstallList: string[]; classPropsInstallList: string[]; + staticMethodsInstallList: string[]; wrapperTypeInfoInit: string; indexedProperty: string; }; diff --git a/bridge/scripts/code_generator/src/idl/utils.ts b/bridge/scripts/code_generator/src/idl/utils.ts index d031e3775f..fb968f047f 100644 --- a/bridge/scripts/code_generator/src/idl/utils.ts +++ b/bridge/scripts/code_generator/src/idl/utils.ts @@ -15,6 +15,11 @@ export function addIndent(str: String, space: number) { export function getClassName(blob: IDLBlob) { let raw = camelCase(blob.filename[4].toUpperCase() + blob.filename.slice(5)); if (raw.slice(0, 3) == 'dom') { + if (raw === 'domMatrixReadonly') { + return `DOMMatrixReadOnly`; + } else if (raw === 'domPointReadonly') { + return `DOMPointReadOnly`; + } return 'DOM' + raw.slice(3); } if (raw.slice(0, 4) == 'html') { diff --git a/bridge/scripts/code_generator/templates/idl_templates/base.cc.tpl b/bridge/scripts/code_generator/templates/idl_templates/base.cc.tpl index 3ad1eb6eaa..7368244cf0 100644 --- a/bridge/scripts/code_generator/templates/idl_templates/base.cc.tpl +++ b/bridge/scripts/code_generator/templates/idl_templates/base.cc.tpl @@ -18,6 +18,8 @@ #include "core/dom/document.h" #include "core/dom/document_fragment.h" #include "core/dom/comment.h" +#include "core/geometry/dom_matrix.h" +#include "core/geometry/dom_point.h" #include "core/input/touch_list.h" #include "core/dom/static_node_list.h" #include "core/html/html_all_collection.h" @@ -36,6 +38,7 @@ void QJS<%= className %>::Install(ExecutingContext* context) { <% if(classPropsInstallList.length > 0) { %> InstallPrototypeProperties(context); <% } %> <% if(classMethodsInstallList.length > 0) { %> InstallPrototypeMethods(context); <% } %> <% if(constructorInstallList.length > 0) { %> InstallConstructor(context); <% } %> + <% if (staticMethodsInstallList.length > 0) { %> InstallStaticMethods(context); <% } %> } <% } %> @@ -73,6 +76,17 @@ void QJS<%= className %>::InstallPrototypeMethods(ExecutingContext* context) { } <% } %> +<% if(staticMethodsInstallList.length > 0) { %> +void QJS<%= className %>::InstallStaticMethods(ExecutingContext* context) { + const WrapperTypeInfo* wrapperTypeInfo = GetWrapperTypeInfo(); + JSValue constructor = context->contextData()->constructorForType(wrapperTypeInfo); + std::initializer_list functionConfig { + <%= staticMethodsInstallList.join(',\n') %> + }; + MemberInstaller::InstallFunctions(context, constructor, functionConfig); +} +<% } %> + <% if (constructorInstallList.length > 0) { %> void QJS<%= className %>::InstallConstructor(ExecutingContext* context) { const WrapperTypeInfo* wrapperTypeInfo = GetWrapperTypeInfo(); diff --git a/bridge/scripts/code_generator/templates/idl_templates/interface.cc.tpl b/bridge/scripts/code_generator/templates/idl_templates/interface.cc.tpl index fa62c5be51..3e88a8bd7f 100644 --- a/bridge/scripts/code_generator/templates/idl_templates/interface.cc.tpl +++ b/bridge/scripts/code_generator/templates/idl_templates/interface.cc.tpl @@ -150,8 +150,11 @@ static JSValue <%= overloadMethod.name %>_overload_<%= index %>(JSContext* ctx, static JSValue <%= method.name %>(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { <%= generateOverLoadSwitchBody(overloadMethods[method.name]) %> } + <% } else if (method.returnTypeMode && method.returnTypeMode.staticMethod) { %> + static JSValue <%= method.name %>(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { + <%= generateFunctionBody(blob, method, {isInstanceMethod: false}) %> + } <% } else { %> - static JSValue <%= method.name %>(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { <%= generateFunctionBody(blob, method, {isInstanceMethod: true}) %> } diff --git a/bridge/scripts/code_generator/templates/idl_templates/interface.h.tpl b/bridge/scripts/code_generator/templates/idl_templates/interface.h.tpl index 2703696066..2e92ca19a4 100644 --- a/bridge/scripts/code_generator/templates/idl_templates/interface.h.tpl +++ b/bridge/scripts/code_generator/templates/idl_templates/interface.h.tpl @@ -46,6 +46,7 @@ class QJS<%= className %> : public QJSInterfaceBridge, <%= c <% if (classMethodsInstallList.length > 0) { %> static void InstallPrototypeMethods(ExecutingContext* context); <% } %> <% if (classPropsInstallList.length > 0) { %> static void InstallPrototypeProperties(ExecutingContext* context); <% } %> <% if (object.construct) { %> static void InstallConstructor(ExecutingContext* context); <% } %> + <% if (staticMethodsInstallList.length > 0) { %> static void InstallStaticMethods(ExecutingContext* context); <% } %> <% if (object.indexedProp) { %> static int PropertyEnumerateCallback(JSContext* ctx, JSPropertyEnum** ptab, uint32_t* plen, JSValueConst obj); diff --git a/bridge/scripts/code_generator/templates/idl_templates/union.cc.tpl b/bridge/scripts/code_generator/templates/idl_templates/union.cc.tpl index 5d01036658..730c31db3f 100644 --- a/bridge/scripts/code_generator/templates/idl_templates/union.cc.tpl +++ b/bridge/scripts/code_generator/templates/idl_templates/union.cc.tpl @@ -10,6 +10,8 @@ #include "bindings/qjs/converter_impl.h" #include "core/html/html_image_element.h" #include "core/html/canvas/html_canvas_element.h" +#include "core/html/canvas/path_2d.h" +#include "qjs_dom_point_init.h" namespace webf { diff --git a/integration_tests/runtime/global.ts b/integration_tests/runtime/global.ts index 8a312c8c01..fefa34b687 100644 --- a/integration_tests/runtime/global.ts +++ b/integration_tests/runtime/global.ts @@ -50,6 +50,15 @@ function xtest(fn, title) { } function assert_equals(a: any, b: any, message?: string) { + if(typeof a != typeof b) { + fail(message) + return; + } + if (b !== b) { + // NaN case + expect(a !== a).toBe(true, message); + return; + } expect(a).toBe(b, message) } @@ -76,6 +85,31 @@ function assert_false(value: any, message?: string) { expect(value).toBe(false, message) } +/** + * Assert that ``actual`` is within ± ``epsilon`` of ``expected``. + * + * @param {number} actual - Test value. + * @param {number} expected - Value number is expected to be close to. + * @param {number} epsilon - Magnitude of allowed difference between ``actual`` and ``expected``. + * @param {string} [description] - Description of the condition being tested. + */ +function assert_approx_equals(actual, expected, epsilon, description) +{ + /* + * Test if two primitive numbers are equal within +/- epsilon + */ + description = description + ", actual value is " + actual; + assert_true(typeof actual === "number", description); + + // The epsilon math below does not place nice with NaN and Infinity + // But in this case Infinity = Infinity and NaN = NaN + if (isFinite(actual) || isFinite(expected)) { + assert_true(Math.abs(actual - expected) <= epsilon, description); + } else { + assert_equals(actual, expected); + } +} + function format_value(v: any) { return JSON.stringify(v) } @@ -466,6 +500,7 @@ Object.assign(global, { assert_not_equals, assert_throws_exactly, assert_class_string, + assert_approx_equals, simulatePointDown, simulatePointUp, simulatePointRemove, diff --git a/integration_tests/snapshots/dom/elements/canvas/context2d.ts.5dbcc2e51.png b/integration_tests/snapshots/dom/elements/canvas/context2d.ts.5dbcc2e51.png new file mode 100644 index 0000000000..6fead6ed4d Binary files /dev/null and b/integration_tests/snapshots/dom/elements/canvas/context2d.ts.5dbcc2e51.png differ diff --git a/integration_tests/snapshots/dom/elements/canvas/context2d.ts.6243ab871.png b/integration_tests/snapshots/dom/elements/canvas/context2d.ts.6243ab871.png new file mode 100644 index 0000000000..cbbecb0990 Binary files /dev/null and b/integration_tests/snapshots/dom/elements/canvas/context2d.ts.6243ab871.png differ diff --git a/integration_tests/snapshots/dom/elements/canvas/context2d.ts.6610623b1.png b/integration_tests/snapshots/dom/elements/canvas/context2d.ts.6610623b1.png new file mode 100644 index 0000000000..50fb1705bf Binary files /dev/null and b/integration_tests/snapshots/dom/elements/canvas/context2d.ts.6610623b1.png differ diff --git a/integration_tests/snapshots/dom/elements/canvas/context2d.ts.a92fbfdb1.png b/integration_tests/snapshots/dom/elements/canvas/context2d.ts.a92fbfdb1.png new file mode 100644 index 0000000000..7e81aed887 Binary files /dev/null and b/integration_tests/snapshots/dom/elements/canvas/context2d.ts.a92fbfdb1.png differ diff --git a/integration_tests/snapshots/dom/elements/canvas/context2d.ts.ae9f854b1.png b/integration_tests/snapshots/dom/elements/canvas/context2d.ts.ae9f854b1.png new file mode 100644 index 0000000000..41ce5f2b40 Binary files /dev/null and b/integration_tests/snapshots/dom/elements/canvas/context2d.ts.ae9f854b1.png differ diff --git a/integration_tests/snapshots/dom/elements/canvas/context2d.ts.f82e97551.png b/integration_tests/snapshots/dom/elements/canvas/context2d.ts.f82e97551.png new file mode 100644 index 0000000000..d2fbc9415a Binary files /dev/null and b/integration_tests/snapshots/dom/elements/canvas/context2d.ts.f82e97551.png differ diff --git a/integration_tests/specs/dom/elements/canvas/context2d.ts b/integration_tests/specs/dom/elements/canvas/context2d.ts index 6e2d475984..3cde58f429 100644 --- a/integration_tests/specs/dom/elements/canvas/context2d.ts +++ b/integration_tests/specs/dom/elements/canvas/context2d.ts @@ -219,6 +219,48 @@ describe('Canvas context 2d', () => { await snapshot(canvas); }); + it('should work with roundRect', async (done) => { + const canvas = ; + document.body.appendChild(canvas); + const ctx = canvas.getContext('2d'); + + ctx.scale(0.6, 0.6); + + // 半径为零的圆角矩形(指定为数字) + ctx.strokeStyle = "red"; + ctx.beginPath(); + ctx.roundRect(10, 20, 150, 100, 0); + ctx.stroke(); + + // 半径为 40px 的圆角矩形(单元素列表) + ctx.strokeStyle = "blue"; + ctx.beginPath(); + ctx.roundRect(10, 20, 150, 100, [40]); + ctx.stroke(); + + // 具有两个不同半径的圆角矩形 + ctx.strokeStyle = "orange"; + ctx.beginPath(); + ctx.roundRect(10, 150, 150, 100, [10, 40]); + ctx.stroke(); + + + // 具有四个不同半径的圆角矩形 + ctx.strokeStyle = "green"; + ctx.beginPath(); + ctx.roundRect(400, 20, 200, 100, [0, 30, 50, 60]); + ctx.stroke(); + + // 向后绘制的相同矩形 + ctx.strokeStyle = "magenta"; + ctx.beginPath(); + ctx.roundRect(400, 150, -200, -100, [0, 30, 50, 60]); + ctx.stroke(); + + await snapshot(canvas); + done(); + }); + it('should work with transform and resetTransform', async () => { const canvas = ; document.body.appendChild(canvas); @@ -712,4 +754,107 @@ describe('Canvas context 2d', () => { }) + + it('should work with create default Path2D', async (done) => { + const canvas = ; + document.body.appendChild(canvas); + + var context = canvas.getContext('2d'); + let path1 = new Path2D(); + path1.rect(10, 10, 100, 100); + context.stroke(path1) + await snapshot(canvas); + done(); + + }) + + it('should work with create Path2D with another Path2D instance', async (done) => { + const canvas = ; + document.body.appendChild(canvas); + + var context = canvas.getContext('2d'); + let path1 = new Path2D(); + path1.rect(10, 10, 100, 100); + + let path2 = new Path2D(path1); + path2.moveTo(220, 60); + path2.arc(170, 60, 50, 0, 2 * Math.PI); + context.stroke(path2); + await snapshot(canvas); + done(); + + }) + + it('should work with create Path2D with SVG path data', async (done) => { + const canvas = ; + document.body.appendChild(canvas); + + var context = canvas.getContext('2d'); + let path = new Path2D("M10 10 h 80 v 80 h -80 Z"); + context.fill(path); + await snapshot(canvas); + done(); + + }) + + it('should work with Path2D addPath(path)', async (done) => { + const canvas = ; + document.body.appendChild(canvas); + + var context = canvas.getContext('2d'); + + // Create first path and add a rectangle + let p1 = new Path2D(); + p1.rect(10, 10, 100, 150); + + // Create second path and add a rectangle + let p2 = new Path2D(); + p2.rect(150, 10, 100, 75); + + // Add second path to the first path + p1.addPath(p2); + + // Draw the first path + context.fill(p1); + + await snapshot(canvas); + done(); + + }) + + + it('should work with create Path2D addPath with DOMMatrix', async (done) => { + const canvas = ; + document.body.appendChild(canvas); + + var context = canvas.getContext('2d'); + + // Create first path and add a rectangle + let p1 = new Path2D(); + p1.rect(0, 0, 100, 150); + + // Create second path and add a rectangle + let p2 = new Path2D(); + p2.rect(0, 0, 100, 75); + + // Create transformation matrix that moves 200 points to the right + let m = new DOMMatrix(); + m.a = 1; + m.b = 0; + m.c = 0; + m.d = 1; + m.e = 200; + m.f = 0; + + // Add second path to the first path + p1.addPath(p2, m); + + // Draw the first path + context.fill(p1); + + await snapshot(canvas); + done(); + + }) + }); diff --git a/integration_tests/specs/dom/geometry/domMatrix001.ts b/integration_tests/specs/dom/geometry/domMatrix001.ts new file mode 100644 index 0000000000..79c1632e79 --- /dev/null +++ b/integration_tests/specs/dom/geometry/domMatrix001.ts @@ -0,0 +1,238 @@ +function checkMatrix(actual, expected, { epsilon = Number.MIN_VALUE } = {}) { + for (let member in expected) { + if (epsilon && typeof expected[member] === "number") { + assert_approx_equals(actual[member], expected[member], epsilon, member); + } else { + assert_equals(actual[member], expected[member], member); + } + } +} + +function checkDOMMatrix(m: any, exp: any, is2D: any = undefined) { + if (is2D === undefined) { + is2D = exp.is2D; + } + assert_equals(m.m11, exp.m11, "Expected value for m11 is " + exp.m11); + assert_equals(m.m12, exp.m12, "Expected value for m12 is " + exp.m12); + assert_equals(m.m13, exp.m13, "Expected value for m13 is " + exp.m13); + assert_equals(m.m14, exp.m14, "Expected value for m14 is " + exp.m14); + assert_equals(m.m21, exp.m21, "Expected value for m21 is " + exp.m21); + assert_equals(m.m22, exp.m22, "Expected value for m22 is " + exp.m22); + assert_equals(m.m23, exp.m23, "Expected value for m23 is " + exp.m23); + assert_equals(m.m24, exp.m24, "Expected value for m24 is " + exp.m24); + assert_equals(m.m31, exp.m31, "Expected value for m31 is " + exp.m31); + assert_equals(m.m32, exp.m32, "Expected value for m32 is " + exp.m32); + assert_equals(m.m33, exp.m33, "Expected value for m33 is " + exp.m33); + assert_equals(m.m34, exp.m34, "Expected value for m34 is " + exp.m34); + assert_equals(m.m41, exp.m41, "Expected value for m41 is " + exp.m41); + assert_equals(m.m42, exp.m42, "Expected value for m42 is " + exp.m42); + assert_equals(m.m43, exp.m43, "Expected value for m43 is " + exp.m43); + assert_equals(m.m44, exp.m44, "Expected value for m44 is " + exp.m44); + assert_equals(m.is2D, is2D, "Expected value for is2D is " + is2D); + assert_equals(m.isIdentity, exp.isIdentity, "Expected value for isIdentity is " + exp.isIdentity); +} + +function identity() { + return new DOMMatrix( + [1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1]); +} + +var initial = { + m11: 1, m21: 0, m31: 0, m41: 0, + m12: 0, m22: 1, m32: 0, m42: 0, + m13: 0, m23: 0, m33: 1, m43: 0, + m14: 0, m24: 0, m34: 0, m44: 1, + is2D: true, + isIdentity: true +}; +var scaleTranslate2D = { + m11: 2, m21: 0, m31: 0, m41: 10, + m12: 0, m22: 2, m32: 0, m42: 10, + m13: 0, m23: 0, m33: 1, m43: 0, + m14: 0, m24: 0, m34: 0, m44: 1, + is2D: true, + isIdentity: false +}; + + + +["DOMMatrix", "DOMMatrixReadOnly"].forEach(function (constr) { + // constructor + test(function () { + checkDOMMatrix(new self[constr](), initial); + }, `new ${constr}()`); + + // test(function () { + // checkDOMMatrix(new self[constr](undefined), initial); + // }, `new ${constr}(undefined)`); + + // test(function () { + // checkDOMMatrix(new self[constr](new self[constr]()), initial); + // }, `new ${constr}(new ${constr}())`); + + test(function () { + var array = [ + 2.0, 0.0, 0.0, 0.0, + 0.0, 2.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 10.0, 10.0, 0.0, 1.0]; + checkDOMMatrix(new self[constr](array), scaleTranslate2D, false); + }, `new ${constr}(array) 16 elements`); + + test(function () { + var array = [2.0, 0.0, 0.0, 2.0, 10.0, 10.0]; + checkDOMMatrix(new self[constr](array), scaleTranslate2D); + }, `new ${constr}(array) 6 elements`); + + test(function () { + var array = [ + 2.0, 0.0, 0.0, 0.0, + 0.0, 2.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 10.0, 10.0, 0.0, 1.0]; + checkDOMMatrix(new self[constr](array), scaleTranslate2D, false); + }, `new ${constr}(array) 16 elements`); + + test(function () { + var array = [2.0, 0.0, 0.0, 2.0, 10.0, 10.0]; + checkDOMMatrix(new self[constr](array), scaleTranslate2D); + }, `new ${constr}((array) 6 elements`); + + // [ + // [2.0, 0.0, 0.0, 0.0, + // 0.0, 2.0, 0.0, 0.0, + // 0.0, 0.0, 1.0, 0.0, + // 10.0, 10.0, 0.0, 1.0], + // [2.0, 0.0, 0.0, 2.0, 10.0, 10.0], + // ].forEach(function (sequence) { + // test(function () { + // checkDOMMatrix(new self[constr](sequence), scaleTranslate2D, + // sequence.length == 6); + // }, `new ${constr}(sequence) ${sequence.length} elements`); + // }); + + [ + [2.0, 0.0, 0.0, 0.0, + 0.0, 2.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 10.0, 10.0, 0.0, 1.0], + [2.0, 0.0, 0.0, 2.0, 10.0, 10.0], + ].forEach(function (sequence) { + test(function () { + checkDOMMatrix(new self[constr](sequence), scaleTranslate2D, + sequence.length == 6); + }, `new ${constr}(sequence) ${sequence.length} elements`); + }); + + + + // TODO Test DOMMatrixReadOnly methods do not mutate the object + function initialMatrix(){ + // return DOMMatrixReadOnly.fromMatrix( + // { + // m11:1, m12:-0.5, m13: 0.5, m14:0, + // m21:0.5, m22:2, m23: -0.5, m24:0, + // m31:0, m32:0, m33: 1, m34:0, + // m41:0, m42:0, m43: 0, m44:1, + // is2D: false + // } + // ); + return new DOMMatrixReadOnly( + [ + 1, -0.5, 0.5, 0, + 0.5,2,-0.5,0, + 0,0,1,0, + 0,0,0,1 + ] + ); + } + + test(function() { + var matrix = initialMatrix(); + matrix.translate(1,5,3); + checkDOMMatrix(matrix, initialMatrix()); + },"test translate() doesn't mutate"); + + test(function() { + var matrix = initialMatrix(); + matrix.scale(1,5,3,0,1,3); + checkDOMMatrix(matrix, initialMatrix()); + },"test scale() doesn't mutate"); + + test(function() { + var matrix = initialMatrix(); + matrix.scaleNonUniform(1,5); + checkDOMMatrix(matrix, initialMatrix()); + },"test scaleNonUniform() doesn't mutate"); + + test(function() { + var matrix = initialMatrix(); + matrix.scale3d(3,2,1,1); + checkDOMMatrix(matrix, initialMatrix()); + },"test scale3d() doesn't mutate"); + + test(function() { + var matrix = initialMatrix(); + matrix.rotate(Math.PI, Math.PI/2, Math.PI/6); + checkDOMMatrix(matrix, initialMatrix()); + },"test rotate() doesn't mutate"); + + test(function() { + var matrix = initialMatrix(); + matrix.rotateFromVector(10,-4); + checkDOMMatrix(matrix, initialMatrix()); + },"test rotateFromVector() doesn't mutate"); + + test(function() { + var matrix = initialMatrix(); + matrix.rotateAxisAngle(3,4,5, Math.PI/6); + checkDOMMatrix(matrix, initialMatrix()); + },"test rotateAxisAngle() doesn't mutate"); + + test(function() { + var matrix = initialMatrix(); + matrix.skewX(20); + checkDOMMatrix(matrix, initialMatrix()); + },"test skewX() doesn't mutate"); + + test(function() { + var matrix = initialMatrix(); + matrix.skewY(20); + checkDOMMatrix(matrix, initialMatrix()); + },"test skewY() doesn't mutate"); + + test(function() { + var matrix = initialMatrix(); + var m = new DOMMatrix([1,2,0,0,-1,2,-1,0,0,0,1,0,5,0,2,1]) + // matrix.multiply({ m11:1, m12:2, m13: 0, m14:0, + // m21:-1, m22:2, m23: -1, m24:0, + // m31:0, m32:0, m33: 1, m34:0, + // m41:5, m42:0, m43: 2, m44:1, + // is2D: false, + // isIdentity:false }); + matrix.multiply(m) + checkDOMMatrix(matrix, initialMatrix()); + },"test multiply() doesn't mutate"); + + test(function() { + var matrix = initialMatrix(); + matrix.flipX(); + checkDOMMatrix(matrix, initialMatrix()); + },"test flipX() doesn't mutate"); + + test(function() { + var matrix = initialMatrix(); + matrix.flipY(); + checkDOMMatrix(matrix, initialMatrix()); + },"test flipY() doesn't mutate"); + + test(function() { + var matrix = initialMatrix(); + matrix.inverse(); + checkDOMMatrix(matrix, initialMatrix()); + },"test inverse() doesn't mutate"); + +}); \ No newline at end of file diff --git a/integration_tests/specs/dom/geometry/domMatrix003.ts b/integration_tests/specs/dom/geometry/domMatrix003.ts new file mode 100644 index 0000000000..396e776174 --- /dev/null +++ b/integration_tests/specs/dom/geometry/domMatrix003.ts @@ -0,0 +1,304 @@ +var epsilon = 0.0000000005; + +function initialMatrix() { + return new DOMMatrix([ 1, -0.5, 0.5, 0, 0.5, 2, -0.5, 0, 0, 0, 1, 0, 10, 20, 10, 1]) + + // return { + // m11: 1, m12: -0.5, m13: 0.5, m14: 0, + // m21: 0.5, m22: 2, m23: -0.5, m24: 0, + // m31: 0, m32: 0, m33: 1, m34: 0, + // m41: 10, m42: 20, m43: 10, m44: 1, + // is2D: false + // }; +} + +function initialDOMMatrix() { + return DOMMatrixReadOnly.fromMatrix(initialMatrix()) + // return new DOMMatrixReadOnly([1, -0.5, 0.5, 0,0.5, 2, -0.5, 0, 0, 0, 1, 0,10, 20, 10, 1]) +} + +function identity() { + return new DOMMatrix( + [1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1]); +} + +function update(matrix, f) { + f(matrix); + return matrix; +} + +function deg2rad(degrees) { + return degrees * Math.PI / 180; +} + +function getRotationMatrix(x, y, z, alpha_in_degrees) { + // Vector normalizing + var nx = x; + var ny = y; + var nz = z; + var length = Math.sqrt(x * x + y * y + z * z); + if (length) { + nx = x / length; + ny = y / length; + nz = z / length; + } + + // The 3D rotation matrix is described in CSS Transforms with alpha. + // Please see: https://drafts.csswg.org/css-transforms-2/#Rotate3dDefined + var alpha_in_radians = deg2rad(alpha_in_degrees / 2); + var sc = Math.sin(alpha_in_radians) * Math.cos(alpha_in_radians); + var sq = Math.sin(alpha_in_radians) * Math.sin(alpha_in_radians); + + var m11 = 1 - 2 * (ny * ny + nz * nz) * sq; + var m12 = 2 * (nx * ny * sq + nz * sc); + var m13 = 2 * (nx * nz * sq - ny * sc); + var m14 = 0; + var m21 = 2 * (nx * ny * sq - nz * sc); + var m22 = 1 - 2 * (nx * nx + nz * nz) * sq; + var m23 = 2 * (ny * nz * sq + nx * sc); + var m24 = 0; + var m31 = 2 * (nx * nz * sq + ny * sc); + var m32 = 2 * (ny * nz * sq - nx * sc); + var m33 = 1 - 2 * (nx * nx + ny * ny) * sq; + var m34 = 0; + var m41 = 0; + var m42 = 0; + var m43 = 0; + var m44 = 1; + + return new DOMMatrix([ + m11, m12, m13, m14, + m21, m22, m23, m24, + m31, m32, m33, m34, + m41, m42, m43, m44]); +} + +function getMatrixTransform(matrix, point) { + var x = point.x * matrix.m11 + point.y * matrix.m21 + point.z * matrix.m31 + point.w * matrix.m41; + var y = point.x * matrix.m12 + point.y * matrix.m22 + point.z * matrix.m32 + point.w * matrix.m42; + var w = point.x * matrix.m13 + point.y * matrix.m23 + point.z * matrix.m33 + point.w * matrix.m43; + var z = point.x * matrix.m14 + point.y * matrix.m24 + point.z * matrix.m34 + point.w * matrix.m44; + return new DOMPoint(x, y, w, z) +} + +test(function () { + var tx = 1; + var ty = 5; + var tz = 3; + var result = initialDOMMatrix().translate(tx, ty, tz); + var expected = update(initialMatrix(), function (m) { + m.m41 += tx * m.m11 + ty * m.m21 + tz * m.m31; + m.m42 += tx * m.m12 + ty * m.m22 + tz * m.m32; + m.m43 += tx * m.m13 + ty * m.m23 + tz * m.m33; + m.m44 += tx * m.m14 + ty * m.m24 + tz * m.m34; + }); + checkDOMMatrix(result, expected); +}, "test translate()"); + +test(function () { + var sx = 2; + var sy = 5; + var sz = 3; + var result = initialDOMMatrix().scale(sx, sy, sz); + var expected = update(initialMatrix(), function (m) { + m.m11 *= sx; + m.m12 *= sx; + m.m13 *= sx; + m.m14 *= sx; + m.m21 *= sy; + m.m22 *= sy; + m.m23 *= sy; + m.m24 *= sy; + m.m31 *= sz; + m.m32 *= sz; + m.m33 *= sz; + m.m34 *= sz; + }); + checkDOMMatrix(result, expected); +}, "test scale() without offsets"); + +test(function () { + var result = initialDOMMatrix().scale(2, 5, 3, 11, 7, 13); + var expected = initialDOMMatrix() + .translate(11, 7, 13) + .scale(2, 5, 3) + .translate(-11, -7, -13); + checkDOMMatrix(result, expected); +}, "test scale() with offsets"); + +test(function () { + var result = new DOMMatrixReadOnly([1, 2, 3, 4, 5, 6]) + .scale(1, 1, 1, 1, 1, 1); + var expected = new DOMMatrixReadOnly([1, 2, 0, 0, 3, 4, 0, 0, 0, 0, 1, 0, 5, 6, 0, 1]); + checkDOMMatrix(result, expected); +}, "test scale() with identity scale and nonzero originZ"); + +test(function () { + var result = initialDOMMatrix().scaleNonUniform(); + var expected = initialDOMMatrix() + .scale(1, 1, 1, 0, 0, 0); + checkDOMMatrix(result, expected); +}, "test scaleNonUniform()"); + +test(function () { + var result = initialDOMMatrix().scaleNonUniform(6); + var expected = initialDOMMatrix() + .scale(6, 1, 1, 0, 0, 0); + checkDOMMatrix(result, expected); +}, "test scaleNonUniform() with sx"); + +test(function () { + var result = initialDOMMatrix().scaleNonUniform(5, 7); + var expected = initialDOMMatrix() + .scale(5, 7, 1, 0, 0, 0); + checkDOMMatrix(result, expected); +}, "test scaleNonUniform() with sx, sy"); + +test(function () { + var result = initialDOMMatrix().scale3d(7, 5, 2, 3); + var expected = initialDOMMatrix() + .translate(5, 2, 3) + .scale(7, 7, 7) + .translate(-5, -2, -3); + checkDOMMatrix(result, expected); +}, "test scale3d()"); + +test(function () { + var result = initialDOMMatrix().rotate(-90); + var expected = initialDOMMatrix().multiply(getRotationMatrix(0, 0, 1, -90)); + checkDOMMatrix(result, expected); +}, "test rotate() 2d"); + +test(function () { + var result = initialDOMMatrix().rotate(180, 180, 90); + var expected = initialDOMMatrix().rotate(0, 0, -90); + checkDOMMatrix(result, expected); +}, "test rotate()"); + +test(function () { + var result = initialDOMMatrix().rotate(90, 90, 90); + var expected = initialDOMMatrix() + .rotate(0, 0, 90) + .rotate(0, 90, 0) + .rotate(90, 0, 0); + checkDOMMatrix(result, expected); +}, "test rotate() order"); + +test(function () { + var result = initialDOMMatrix().rotateFromVector(1, 1); + var expected = initialDOMMatrix().rotate(45); + checkDOMMatrix(result, expected); +}, "test rotateFromVector()"); // TODO Expected value for m11 is -1, actual value is 6.123233995736767e-17 + +test(function () { + var result = initialDOMMatrix().rotateFromVector(0, 1); + var expected = initialDOMMatrix().rotate(90); + checkDOMMatrix(result, expected); +}, "test rotateFromVector() with x being zero"); // TODO Expected value for m11 is 1.0606601717798214, actual value is 0.9507737510847889 + +test(function () { + var result = initialDOMMatrix().rotateFromVector(1, 0); + var expected = initialDOMMatrix() + checkDOMMatrix(result, expected); +}, "test rotateFromVector() with y being zero"); + +test(function () { + var result = initialDOMMatrix().rotateFromVector(0, 0); + var expected = initialDOMMatrix() + checkDOMMatrix(result, expected); +}, "test rotateFromVector() with two zeros"); + +test(function () { + var result = initialDOMMatrix().rotateAxisAngle(3, 3, 3, 120); + var expected = initialDOMMatrix().multiply(getRotationMatrix(3, 3, 3, 120)); + checkDOMMatrix(result, expected); +}, "test rotateAxisAngle() "); // TODO Expected value for m11 is 0.5000000000000002 + +test(function () { + var angleDeg = 75; + var result = initialDOMMatrix().skewX(angleDeg); + var tangent = Math.tan(angleDeg * Math.PI / 180); + var skew = new DOMMatrix([ + 1, 0, 0, 0, + tangent, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1]) + var expected = initialDOMMatrix().multiply(skew); + checkDOMMatrix(result, expected); +}, "test skewX()"); + +test(function () { + var angleDeg = 18; + var result = initialDOMMatrix().skewY(angleDeg); + var tangent = Math.tan(angleDeg * Math.PI / 180); + var skew = new DOMMatrix([ + 1, tangent, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1]) + var expected = initialDOMMatrix().multiply(skew); + checkDOMMatrix(result, expected); +}, "test skewY()"); // TODO 'Expected value for m11 is 1.1624598481164532 + +test(function () { + var result = initialDOMMatrix().multiply(initialDOMMatrix().inverse()); + checkDOMMatrix(result, identity()); +}, "test multiply with inverse is identity"); + +test(function () { + var result = initialDOMMatrix().flipX(); + var expected = initialDOMMatrix().multiply(new DOMMatrix([-1, 0, 0, 1, 0, 0])); + checkDOMMatrix(result, expected); +}, "test flipX()"); //Expected false to be true, 'Expected value for is2D is true' + +test(function () { + var result = initialDOMMatrix().flipY(); + var expected = initialDOMMatrix().multiply(new DOMMatrix([1, 0, 0, -1, 0, 0])); + checkDOMMatrix(result, expected); +}, "test flipY()"); // Expected false to be true, 'Expected value for is2D is true'. + +test(function () { + var point = new DOMPointReadOnly(1, 2, 3, 4); + var matrix = new DOMMatrix([1, 2, 3, 4, 5, 6]); + var result = matrix.transformPoint(point); + var expected = getMatrixTransform(matrix, point); + checkDOMPoint(result, expected); +}, "test transformPoint() - 2d matrix"); + +test(function () { + var point = new DOMPointReadOnly(1, 2, 3, 4); + var matrix = new DOMMatrix([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + var result = matrix.transformPoint(point); + var expected = getMatrixTransform(matrix, point); + checkDOMPoint(result, expected); +}, "test transformPoint() - 3d matrix"); + +function checkDOMMatrix(m, exp) { + assert_approx_equals(m.m11, exp.m11, epsilon, "Expected value for m11 is " + exp.m11); + assert_approx_equals(m.m12, exp.m12, epsilon, "Expected value for m12 is " + exp.m12); + assert_approx_equals(m.m13, exp.m13, epsilon, "Expected value for m13 is " + exp.m13); + assert_approx_equals(m.m14, exp.m14, epsilon, "Expected value for m14 is " + exp.m14); + assert_approx_equals(m.m21, exp.m21, epsilon, "Expected value for m21 is " + exp.m21); + assert_approx_equals(m.m22, exp.m22, epsilon, "Expected value for m22 is " + exp.m22); + assert_approx_equals(m.m23, exp.m23, epsilon, "Expected value for m23 is " + exp.m23); + assert_approx_equals(m.m24, exp.m24, epsilon, "Expected value for m24 is " + exp.m24); + assert_approx_equals(m.m31, exp.m31, epsilon, "Expected value for m31 is " + exp.m31); + assert_approx_equals(m.m32, exp.m32, epsilon, "Expected value for m32 is " + exp.m32); + assert_approx_equals(m.m33, exp.m33, epsilon, "Expected value for m33 is " + exp.m33); + assert_approx_equals(m.m34, exp.m34, epsilon, "Expected value for m34 is " + exp.m34); + assert_approx_equals(m.m41, exp.m41, epsilon, "Expected value for m41 is " + exp.m41); + assert_approx_equals(m.m42, exp.m42, epsilon, "Expected value for m42 is " + exp.m42); + assert_approx_equals(m.m43, exp.m43, epsilon, "Expected value for m43 is " + exp.m43); + assert_approx_equals(m.m44, exp.m44, epsilon, "Expected value for m44 is " + exp.m44); + // assert_equals(m.is2D, exp.is2D, "Expected value for is2D is " + exp.is2D); +} + +function checkDOMPoint(p, exp) { + assert_equals(p.x, exp.x, "x is not matched"); + assert_equals(p.y, exp.y, "y is not matched"); + assert_equals(p.z, exp.z, "z is not matched"); + assert_equals(p.w, exp.w, "w is not matched"); +} \ No newline at end of file diff --git a/integration_tests/specs/dom/geometry/domPoint001.ts b/integration_tests/specs/dom/geometry/domPoint001.ts new file mode 100644 index 0000000000..0da99448db --- /dev/null +++ b/integration_tests/specs/dom/geometry/domPoint001.ts @@ -0,0 +1,65 @@ +function checkDOMPoint(p, exp) { + assert_equals(p.x, exp.x, "Expected value for x is " + exp.x); + assert_equals(p.y, exp.y, "Expected value for y is " + exp.y); + assert_equals(p.z, exp.z, "Expected value for z is " + exp.z); + assert_equals(p.w, exp.w, "Expected value for w is " + exp.w); +} + +test(function() { + checkDOMPoint(new DOMPoint(), {x:0, y:0, z:0, w:1}); +},'testConstructor0'); +test(function() { + checkDOMPoint(new DOMPoint(1), {x:1, y:0, z:0, w:1}); +},'testConstructor1'); +test(function() { + checkDOMPoint(new DOMPoint(1, 2), {x:1, y:2, z:0, w:1}); +},'testConstructor2'); +test(function() { + checkDOMPoint(new DOMPoint(1, 2, 3), {x:1, y:2, z:3, w:1}); +},'testConstructor3'); +test(function() { + checkDOMPoint(new DOMPoint(1, 2, 3, 4), {x:1, y:2, z:3, w:4}); +},'testConstructor4'); +test(function() { + checkDOMPoint(new DOMPoint(1, 2, 3, 4, 5), {x:1, y:2, z:3, w:4}); +},'testConstructor5'); +// test(function() { +// checkDOMPoint(new DOMPoint({}), {x:NaN, y:0, z:0, w:1}); +// },'testConstructorDictionary0'); //TODO +// test(function() { +// checkDOMPoint(new DOMPoint({x:1}), {x:NaN, y:0, z:0, w:1}); +// },'testConstructorDictionary1'); //TODO +// test(function() { +// checkDOMPoint(new DOMPoint({x:1, y:2}), {x:NaN, y:0, z:0, w:1}); +// },'testConstructorDictionary2'); //TODO +test(function() { + checkDOMPoint(new DOMPoint(1, undefined), {x:1, y:0, z:0, w:1}); +},'testConstructor2undefined'); +// test(function() { +// checkDOMPoint(new DOMPoint("a", "b"), {x:NaN, y:NaN, z:0, w:1}); +// },'testConstructorUndefined1'); //TODO +// test(function() { +// checkDOMPoint(new DOMPoint({x:"a", y:"b"}), {x:NaN, y:0, z:0, w:1}); +// },'testConstructorUndefined2'); //TODO +test(function() { + checkDOMPoint(new DOMPointReadOnly(), {x:0, y:0, z:0, w:1}); +},'DOMPointReadOnly constructor with no values'); +test(function() { + checkDOMPoint(new DOMPointReadOnly(1, 2, 3, 4), {x:1, y:2, z:3, w:4}); +},'DOMPointReadOnly constructor with 4 values'); +test(function() { + var p = new DOMPoint(0, 0, 0, 1); + p.x = undefined; + p.y = undefined; + p.z = undefined; + p.w = undefined; + checkDOMPoint(p, {x:NaN, y:NaN, z:NaN, w:NaN}); +},'testAttributesUndefined'); //TODO +test(function() { + var p = new DOMPoint(0, 0, 0, 1); + p.x = NaN; + p.y = Number.POSITIVE_INFINITY; + p.z = Number.NEGATIVE_INFINITY; + p.w = Infinity; + checkDOMPoint(p, {x:NaN, y:Infinity, z:-Infinity, w:Infinity}); +},'testAttributesNaNInfinity'); //TODO \ No newline at end of file diff --git a/integration_tests/specs/dom/geometry/domPoint002.ts b/integration_tests/specs/dom/geometry/domPoint002.ts new file mode 100644 index 0000000000..3e2edc32aa --- /dev/null +++ b/integration_tests/specs/dom/geometry/domPoint002.ts @@ -0,0 +1,155 @@ +function getMatrixTransform(matrix, point) { + var x = point.x * matrix.m11 + point.y * matrix.m21 + point.z * matrix.m31 + point.w * matrix.m41; + var y = point.x * matrix.m12 + point.y * matrix.m22 + point.z * matrix.m32 + point.w * matrix.m42; + var w = point.x * matrix.m13 + point.y * matrix.m23 + point.z * matrix.m33 + point.w * matrix.m43; + var z = point.x * matrix.m14 + point.y * matrix.m24 + point.z * matrix.m34 + point.w * matrix.m44; + return new DOMPoint(x, y, w, z) +} + +test(function() { + checkDOMPoint(new DOMPoint(), {x:0, y:0, z:0, w:1}); +},'test DOMPoint Constructor no args'); +test(function() { + checkDOMPoint(new DOMPoint(1), {x:1, y:0, z:0, w:1}); +},'test DOMPoint Constructor one args'); +test(function() { + checkDOMPoint(new DOMPoint(1, 2), {x:1, y:2, z:0, w:1}); +},'test DOMPoint Constructor two args'); +test(function() { + checkDOMPoint(new DOMPoint(1, 2, 3), {x:1, y:2, z:3, w:1}); +},'test DOMPoint Constructor three args'); +test(function() { + checkDOMPoint(new DOMPoint(1, 2, 3, 4), {x:1, y:2, z:3, w:4}); +},'test DOMPoint Constructor four args'); +test(function() { + checkDOMPoint(new DOMPoint(1, 2, 3, 4, 5), {x:1, y:2, z:3, w:4}); +},'test DOMPoint Constructor more then four args'); +test(function() { + checkDOMPoint(new DOMPoint(1, undefined), {x:1, y:0, z:0, w:1}); +},'test DOMPoint Constructor with undefined'); +// test(function() { +// checkDOMPoint(new DOMPoint("a", "b"), {x:NaN, y:NaN, z:0, w:1}); +// },'test DOMPoint Constructor with string'); //TODO +// test(function() { +// checkDOMPoint(new DOMPoint({}), {x:NaN, y:0, z:0, w:1}); +// },'test DOMPoint Constructor with empty object'); //TODO +test(function() { + checkDOMPoint(DOMPoint.fromPoint(new DOMPoint({})), {x:0, y:0, z:0, w:1}); +},'test DOMPoint fromPoint with empty object'); +test(function() { + checkDOMPoint(DOMPoint.fromPoint(new DOMPoint({x:1})), {x:1, y:0, z:0, w:1}); +},'test DOMPoint fromPoint with x'); +test(function() { + checkDOMPoint(DOMPoint.fromPoint(new DOMPoint({x:1, y:2})), {x:1, y:2, z:0, w:1}); +},'test DOMPoint fromPoint with x, y'); +test(function() { + checkDOMPoint(DOMPoint.fromPoint(new DOMPoint({x:1, y:2, z:3})), {x:1, y:2, z:3, w:1}); +},'test DOMPoint fromPoint with x, y, z'); +test(function() { + checkDOMPoint(DOMPoint.fromPoint(new DOMPoint({x:1, y:2, z:3, w:4})), {x:1, y:2, z:3, w:4}); +},'test DOMPoint fromPoint with x, y, z, w'); +test(function() { + checkDOMPoint(DOMPoint.fromPoint(new DOMPoint({x:1, y:2, z:3, w:4, v:5})), {x:1, y:2, z:3, w:4}); +},'test DOMPoint fromPoint with x, y, z, w, v'); +test(function() { + checkDOMPoint(DOMPoint.fromPoint(new DOMPoint({x:1, z:3})), {x:1, y:0, z:3, w:1}); +},'test DOMPoint fromPoint with x, z'); +test(function() { + checkDOMPoint(DOMPoint.fromPoint(new DOMPoint({x:1, y: undefined, z:3})), {x:1, y:0, z:3, w:1}); +},'test DOMPoint fromPoint with undefined value'); +test(function() { + var point = new DOMPoint(5, 4); + var matrix = new DOMMatrix([2, 0, 0, 2, 10, 10]); + var result = point.matrixTransform(matrix); + var expected = getMatrixTransform(matrix, point); + checkDOMPoint(result, expected); +},'test DOMPoint matrixTransform'); +test(function() { + var p = new DOMPoint(0, 0, 0, 1); + p.x = undefined; + p.y = undefined; + p.z = undefined; + p.w = undefined; + checkDOMPoint(p, {x:NaN, y:NaN, z:NaN, w:NaN}); +},'test DOMPoint Attributes undefined'); +test(function() { + var p = new DOMPoint(0, 0, 0, 1); + p.x = NaN; + p.y = Number.POSITIVE_INFINITY; + p.z = Number.NEGATIVE_INFINITY; + p.w = Infinity; + checkDOMPoint(p, {x:NaN, y:Infinity, z:-Infinity, w:Infinity}); +},'test DOMPoint Attributes NaN Infinity'); +test(function() { + checkDOMPoint(new DOMPointReadOnly(), {x:0, y:0, z:0, w:1}); +},'test DOMPointReadOnly Constructor no args'); +test(function() { + checkDOMPoint(new DOMPointReadOnly(1), {x:1, y:0, z:0, w:1}); +},'test DOMPointReadOnly Constructor one args'); +test(function() { + checkDOMPoint(new DOMPointReadOnly(1, 2), {x:1, y:2, z:0, w:1}); +},'test DOMPointReadOnly Constructor two args'); +test(function() { + checkDOMPoint(new DOMPointReadOnly(1, 2, 3), {x:1, y:2, z:3, w:1}); +},'test DOMPointReadOnly Constructor three args'); +test(function() { + checkDOMPoint(new DOMPointReadOnly(1, 2, 3, 4), {x:1, y:2, z:3, w:4}); +},'test DOMPointReadOnly Constructor four args'); +test(function() { + checkDOMPoint(new DOMPointReadOnly(1, 2, 3, 4, 5), {x:1, y:2, z:3, w:4}); +},'test DOMPointReadOnly Constructor more then four args'); +test(function() { + checkDOMPoint(new DOMPointReadOnly(1, undefined), {x:1, y:0, z:0, w:1}); +},'test DOMPointReadOnly Constructor with undefined'); +// test(function() { +// checkDOMPoint(new DOMPointReadOnly("a", "b"), {x:NaN, y:NaN, z:0, w:1}); +// },'test DOMPointReadOnly Constructor with string'); // TODO +// test(function() { +// checkDOMPoint(new DOMPointReadOnly({}), {x:NaN, y:0, z:0, w:1}); +// },'test DOMPointReadOnly Constructor with object'); // TODO +test(function() { + checkDOMPoint(DOMPointReadOnly.fromPoint(new DOMPoint({})), {x:0, y:0, z:0, w:1}); +},'test DOMPointReadOnly fromPoint with empty object'); +test(function() { + checkDOMPoint(DOMPointReadOnly.fromPoint(new DOMPoint({x:1})), {x:1, y:0, z:0, w:1}); +},'test DOMPointReadOnly fromPoint with x'); +test(function() { + checkDOMPoint(DOMPointReadOnly.fromPoint(new DOMPoint({x:1, y:2})), {x:1, y:2, z:0, w:1}); +},'test DOMPointReadOnly fromPoint with x, y'); +test(function() { + checkDOMPoint(DOMPointReadOnly.fromPoint(new DOMPoint({x:1, y:2, z:3})), {x:1, y:2, z:3, w:1}); +},'test DOMPointReadOnly fromPoint with x, y, z'); +test(function() { + checkDOMPoint(DOMPointReadOnly.fromPoint(new DOMPoint({x:1, y:2, z:3, w:4})), {x:1, y:2, z:3, w:4}); +},'test DOMPointReadOnly fromPoint with x, y, z, w'); +test(function() { + checkDOMPoint(DOMPointReadOnly.fromPoint(new DOMPoint({x:1, y:2, z:3, w:4, v:5})), {x:1, y:2, z:3, w:4}); +},'test DOMPointReadOnly fromPoint with x, y, z, w, v'); +test(function() { + checkDOMPoint(DOMPointReadOnly.fromPoint(new DOMPoint({x:1, z:3})), {x:1, y:0, z:3, w:1}); +},'test DOMPointReadOnly fromPoint with x, z'); +test(function() { + checkDOMPoint(DOMPointReadOnly.fromPoint(new DOMPoint({x:1, y: undefined, z:3})), {x:1, y:0, z:3, w:1}); +},'test DOMPointReadOnly fromPoint with undefined value'); +test(function() { + var point = new DOMPointReadOnly(5, 4); + var matrix = new DOMMatrix([1, 2, 3, 4, 5, 6]); + var result = point.matrixTransform(matrix); + var expected = getMatrixTransform(matrix, point); + checkDOMPoint(result, expected); +},'test DOMPointReadOnly matrixTransform'); +test(function() { + var p = new DOMPointReadOnly(0, 0, 0, 1); + p.x = undefined; + p.y = undefined; + p.z = undefined; + p.w = undefined; + checkDOMPoint(p, {x:0, y:0, z:0, w:1}); +},'test DOMPointReadOnly Attributes undefined'); + +function checkDOMPoint(p, exp) { + assert_equals(p.x, exp.x, "x is not matched"); + assert_equals(p.y, exp.y, "y is not matched"); + assert_equals(p.z, exp.z, "z is not matched"); + assert_equals(p.w, exp.w, "w is not matched"); +} \ No newline at end of file diff --git a/webf/lib/module.dart b/webf/lib/module.dart index 733e2ec069..eb40cc7f32 100644 --- a/webf/lib/module.dart +++ b/webf/lib/module.dart @@ -15,3 +15,4 @@ export 'src/module/history.dart'; export 'src/module/hybrid_history.dart'; export 'src/module/navigation.dart'; export 'src/module/navigator.dart'; +export 'src/module/dom_matrix.dart'; diff --git a/webf/lib/src/bridge/binding.dart b/webf/lib/src/bridge/binding.dart index bb047a1527..deb63f072a 100644 --- a/webf/lib/src/bridge/binding.dart +++ b/webf/lib/src/bridge/binding.dart @@ -14,6 +14,8 @@ import 'package:webf/dom.dart'; import 'package:webf/geometry.dart'; import 'package:webf/foundation.dart'; import 'package:webf/launcher.dart'; +import 'package:webf/src/geometry/dom_point.dart'; +import 'package:webf/src/html/canvas/canvas_path_2d.dart'; // We have some integrated built-in behavior starting with string prefix reuse the callNativeMethod implements. enum BindingMethodCallOperations { @@ -157,7 +159,9 @@ Future _dispatchEventToNative(Event event, bool isCapture) async { } enum CreateBindingObjectType { - createDOMMatrix + createDOMMatrix, + createPath2D, + createDOMPoint, } abstract class BindingBridge { @@ -178,6 +182,16 @@ abstract class BindingBridge { controller.view.setBindingObject(pointer, domMatrix); return; } + case CreateBindingObjectType.createPath2D: { + Path2D path2D = Path2D(context: BindingContext(controller.view, contextId, pointer), path2DInit: arguments); + controller.view.setBindingObject(pointer, path2D); + return; + } + case CreateBindingObjectType.createDOMPoint: { + DOMPoint domPoint = DOMPoint(BindingContext(controller.view, contextId, pointer), arguments); + controller.view.setBindingObject(pointer, domPoint); + return; + } } } diff --git a/webf/lib/src/bridge/from_native.dart b/webf/lib/src/bridge/from_native.dart index d560dc30f8..47714fa756 100644 --- a/webf/lib/src/bridge/from_native.dart +++ b/webf/lib/src/bridge/from_native.dart @@ -156,7 +156,7 @@ dynamic invokeModule(Pointer callbackContext, WebFController controller, S // To make sure Promise then() and catch() executed before Promise callback called at JavaScript side. // We should make callback always async. Future.microtask(() { - if (controller.view != currentView || currentView.disposed) return; + if (controller.view != currentView || currentView.disposed || callback == nullptr) return; Pointer> handleResult = Pointer.fromFunction(_handleInvokeModuleResult); @@ -192,6 +192,7 @@ dynamic invokeModule(Pointer callbackContext, WebFController controller, S print('Invoke module failed: $e\n$stack'); } String error = '$e\n$stack'; + if (callback == nullptr) return; callback(callbackContext, currentView.contextId, error.toNativeUtf8(), nullptr, {}, nullptr); } diff --git a/webf/lib/src/css/matrix.dart b/webf/lib/src/css/matrix.dart index c17e91f4c0..71dedd4ae4 100644 --- a/webf/lib/src/css/matrix.dart +++ b/webf/lib/src/css/matrix.dart @@ -75,8 +75,11 @@ double _length(v) { return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); } -List _normalize(List v) { +List normalize(List v) { var len = _length(v); + if (len == 0) { + return v; + } return [v[0] / len, v[1] / len, v[2] / len]; } @@ -88,7 +91,7 @@ List _cross(v1, v2) { return [v1[1] * v2[2] - v1[2] * v2[1], v1[2] * v2[0] - v1[0] * v2[2], v1[0] * v2[1] - v1[1] * v2[0]]; } -double _dot(v1, v2) { +double dot(v1, v2) { double result = 0; for (var i = 0; i < v1.length; i++) { result += v1[i] * v2[i]; @@ -99,7 +102,7 @@ double _dot(v1, v2) { final double _1deg = 180 / pi; final double _1rad = pi / 180; -double? _rad2deg(rad) { +double? rad2deg(rad) { // angleInDegree = angleInRadians * (180 / Math.PI) return rad * _1deg; } @@ -227,7 +230,7 @@ class CSSMatrix { // Perform a spherical linear interpolation between the two // passed quaternions with 0 <= t <= 1. static List lerpQuaternion(quaternionA, quaternionB, t) { - var product = _dot(quaternionA, quaternionB); + var product = dot(quaternionA, quaternionB); // Clamp product to -1.0 <= product <= 1.0 product = max(min(product, 1.0), -1.0); @@ -311,33 +314,33 @@ class CSSMatrix { List> row = []; row.add(matrix[0].sublist(0, 3)); - // Compute X scale factor and _normalize first row. + // Compute X scale factor and normalize first row. List scale = List.filled(3, 0); scale[0] = _length(row[0]); - row[0] = _normalize(row[0]); + row[0] = normalize(row[0]); // Compute XY shear factor and make 2nd row orthogonal to 1st. // skew factors XY,XZ,YZ represented as a 3 component vector List skew = List.filled(3, 0); row.add(matrix[1].sublist(0, 3)); - skew[0] = _dot(row[0], row[1]); + skew[0] = dot(row[0], row[1]); row[1] = _combine(row[1], row[0], 1.0, -skew[0]); - // Now, compute Y scale and _normalize 2nd row. + // Now, compute Y scale and normalize 2nd row. scale[1] = _length(row[1]); - row[1] = _normalize(row[1]); + row[1] = normalize(row[1]); skew[0] /= scale[1]; // Compute XZ and YZ shears, orthogonalize 3rd row row.add(matrix[2].sublist(0, 3)); - skew[1] = _dot(row[0], row[2]); + skew[1] = dot(row[0], row[2]); row[2] = _combine(row[2], row[0], 1.0, -skew[1]); - skew[2] = _dot(row[1], row[2]); + skew[2] = dot(row[1], row[2]); row[2] = _combine(row[2], row[1], 1.0, -skew[2]); - // Next, get Z scale and _normalize 3rd row. + // Next, get Z scale and normalize 3rd row. scale[2] = _length(row[2]); - row[2] = _normalize(row[2]); + row[2] = normalize(row[2]); skew[1] /= scale[2]; skew[2] /= scale[2]; @@ -345,7 +348,7 @@ class CSSMatrix { // Check for a coordinate system flip. If the _determinant // is -1, then negate the matrix and the scaling factors. var pdum3 = _cross(row[1], row[2]); - if (_dot(row[0], pdum3) < 0) { + if (dot(row[0], pdum3) < 0) { for (var i = 0; i < 3; i++) { scale[i] *= -1; row[i][0] *= -1; @@ -579,7 +582,7 @@ class CSSMatrix { double m22 = row1y; // Convert into degrees because our rotation functions expect it. - angle = _rad2deg(angle)!; + angle = rad2deg(angle)!; return [translate, scale, angle, m11, m12, m21, m22]; } diff --git a/webf/lib/src/geometry/dom_matrix.dart b/webf/lib/src/geometry/dom_matrix.dart index c28a668127..10175d7bd2 100644 --- a/webf/lib/src/geometry/dom_matrix.dart +++ b/webf/lib/src/geometry/dom_matrix.dart @@ -2,11 +2,16 @@ * Copyright (C) 2022-present The WebF authors. All rights reserved. */ +import 'dart:typed_data'; + +import 'package:vector_math/vector_math_64.dart'; import 'package:webf/foundation.dart'; import 'dom_matrix_readonly.dart'; -class DOMMatrix extends DOMMatrixReadonly { - DOMMatrix(BindingContext context, List domMatrixInit): super(context, domMatrixInit) { - print('domMatrix init: $domMatrixInit'); +class DOMMatrix extends DOMMatrixReadOnly { + DOMMatrix(BindingContext context, List domMatrixInit) : super(context, domMatrixInit) { + } + + DOMMatrix.fromMatrix4(BindingContext context, Matrix4? matrix4, bool flag2D) : super.fromMatrix4(context, matrix4, flag2D) { } } diff --git a/webf/lib/src/geometry/dom_matrix_readonly.dart b/webf/lib/src/geometry/dom_matrix_readonly.dart index b66271956a..67e856653c 100644 --- a/webf/lib/src/geometry/dom_matrix_readonly.dart +++ b/webf/lib/src/geometry/dom_matrix_readonly.dart @@ -1,17 +1,510 @@ /* * Copyright (C) 2022-present The WebF authors. All rights reserved. */ +import 'dart:math'; +import 'dart:typed_data'; +import 'package:flutter/cupertino.dart'; +import 'package:vector_math/vector_math_64.dart'; +import 'package:webf/bridge.dart'; import 'package:webf/foundation.dart'; +import 'package:webf/geometry.dart'; +import 'package:webf/src/css/matrix.dart'; +import 'package:webf/src/geometry/dom_point.dart'; -class DOMMatrixReadonly extends DynamicBindingObject { - DOMMatrixReadonly(BindingContext context, List domMatrixInit): super(context); +class DOMMatrixReadOnly extends DynamicBindingObject { + // Matrix4 Values are stored in column major order. + Matrix4 _matrix4 = Matrix4.identity(); + + Matrix4 get matrix => _matrix4; + bool _is2D = true; + + bool get is2D => _is2D; + + DOMMatrixReadOnly.fromMatrix4(BindingContext context, Matrix4? matrix4, bool flag2D) : super(context) { + if (matrix4 != null) { + _matrix4 = matrix4; + _is2D = flag2D; + } else { + _matrix4 = Matrix4.zero(); + _is2D = false; + } + } + + DOMMatrixReadOnly(BindingContext context, List domMatrixInit) : super(context) { + if (!domMatrixInit.isNotEmpty) { + return; + } + if (domMatrixInit.length == 1) { + // List + if (domMatrixInit[0].runtimeType == List) { + List list = domMatrixInit[0]; + if (list.isNotEmpty && list[0].runtimeType == double) { + List doubleList = List.from(list); + if (doubleList.length == 6) { + _matrix4[0] = doubleList[0]; // m11 = a + _matrix4[1] = doubleList[1]; // m12 = b + _matrix4[4] = doubleList[2]; // m21 = c + _matrix4[5] = doubleList[3]; // m22 = d + _matrix4[12] = doubleList[4]; // m41 = e + _matrix4[13] = doubleList[5]; // m42 = f + } else if (doubleList.length == 16) { + _is2D = false; + _matrix4 = Matrix4.fromList(doubleList); + } else { + throw TypeError(); + } + } + } + } else if (domMatrixInit.length == 3) { + // List, is2D, isIdentity + if (domMatrixInit[0].runtimeType == List) { + List list = domMatrixInit[0]; + if (list.isNotEmpty && list[0].runtimeType == double) { + List doubleList = List.from(list); + if (doubleList.length == 16) { + _matrix4 = Matrix4.fromList(doubleList); + } else { + throw TypeError(); + } + } + _is2D = castToType(domMatrixInit[1]); + //TODO isIdentity + // _isIdentity = castToType(domMatrixInit[2]); + } + } + } @override void initializeMethods(Map methods) { + methods['flipX'] = BindingObjectMethodSync(call: (_) => flipX()); + methods['flipY'] = BindingObjectMethodSync(call: (_) => flipY()); + methods['inverse'] = BindingObjectMethodSync(call: (_) => inverse()); + methods['multiply'] = BindingObjectMethodSync(call: (args) { + BindingObject domMatrix = args[0]; + if (domMatrix is DOMMatrix) { + return multiply((domMatrix as DOMMatrix)); + } + }); + methods['rotateAxisAngle'] = BindingObjectMethodSync( + call: (args) => rotateAxisAngle( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble(), + castToType(args[2]).toDouble(), + castToType(args[3]).toDouble() + ) + ); + methods['rotate'] = BindingObjectMethodSync(call: (args) { + if (args.length == 1) { + // rotate(x) + return rotateZ(castToType(args[0]).toDouble()); + } else if (args.length == 3) { + // rotate(x,y,z) + return rotate( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble(), + castToType(args[2]).toDouble()); + } + }); + + methods['rotateFromVector'] = BindingObjectMethodSync( + call: (args) => rotateFromVector( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble() + ) + ); + methods['scale'] = BindingObjectMethodSync( + call: (args) => scale( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble(), + castToType(args[2]).toDouble(), + castToType(args[3]).toDouble(), + castToType(args[4]).toDouble(), + castToType(args[5]).toDouble() + ) + ); + methods['scale3d'] = BindingObjectMethodSync( + call: (args) => scale3d( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble(), + castToType(args[2]).toDouble(), + castToType(args[3]).toDouble(), + ) + ); + methods['scaleNonUniform'] = BindingObjectMethodSync( + call: (args) => scaleNonUniform( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble() + ) + ); + methods['scale3d'] = BindingObjectMethodSync( + call: (args) => scale3d( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble(), + castToType(args[2]).toDouble(), + castToType(args[3]).toDouble(), + ) + ); + methods['skewX'] = BindingObjectMethodSync(call: (args) => skewX(castToType(args[0]).toDouble())); + methods['skewY'] = BindingObjectMethodSync(call: (args) => skewY(castToType(args[0]).toDouble())); + // toFloat32Array(): number[]; + // toFloat64Array(): number[]; + // toJSON(): DartImpl; + methods['toString'] = BindingObjectMethodSync(call: (args) => toString()); + methods['transformPoint'] = BindingObjectMethodSync(call: (args) { + BindingObject domPoint = args[0]; + if (domPoint is DOMPoint) { + return transformPoint(domPoint); + } + }); + methods['translate'] = BindingObjectMethodSync( + call: (args) => translate( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble(), + castToType(args[2]).toDouble() + ) + ); } @override void initializeProperties(Map properties) { + properties['is2D'] = BindingObjectProperty(getter: () => _is2D); + properties['isIdentity'] = BindingObjectProperty(getter: () => _matrix4.isIdentity()); + // m11 = a + properties['m11'] = BindingObjectProperty(getter: () { + return _matrix4[0]; + }, setter: (value) { + if (value is double) { + _matrix4[0] = value; + } + }); + properties['a'] = BindingObjectProperty( + getter: () => _matrix4[0], + setter: (value) { + if (value is double) { + _matrix4[0] = value; + } + }); + // m12 = b + properties['m12'] = BindingObjectProperty( + getter: () => _matrix4[1], + setter: (value) { + if (value is double) { + _matrix4[1] = value; + } + }); + properties['b'] = BindingObjectProperty( + getter: () => _matrix4[1], + setter: (value) { + if (value is double) { + _matrix4[1] = value; + } + }); + properties['m13'] = BindingObjectProperty( + getter: () => _matrix4[2], + setter: (value) { + if (value is double) { + _matrix4[2] = value; + } + }); + properties['m14'] = BindingObjectProperty( + getter: () => _matrix4[3], + setter: (value) { + if (value is double) { + _matrix4[3] = value; + } + }); + + // m22 = c + properties['m21'] = BindingObjectProperty( + getter: () => _matrix4[4], + setter: (value) { + if (value is double) { + _matrix4[4] = value; + } + }); + properties['c'] = BindingObjectProperty( + getter: () => _matrix4[4], + setter: (value) { + if (value is double) { + _matrix4[4] = value; + } + }); + // m22 = d + properties['m22'] = BindingObjectProperty( + getter: () => _matrix4[5], + setter: (value) { + if (value is double) { + _matrix4[5] = value; + } + }); + properties['d'] = BindingObjectProperty( + getter: () => _matrix4[5], + setter: (value) { + if (value is double) { + _matrix4[5] = value; + } + }); + properties['m23'] = BindingObjectProperty( + getter: () => _matrix4[6], + setter: (value) { + if (value is double) { + _matrix4[6] = value; + } + }); + properties['m24'] = BindingObjectProperty( + getter: () => _matrix4[7], + setter: (value) { + if (value is double) { + _matrix4[7] = value; + } + }); + + properties['m31'] = BindingObjectProperty( + getter: () => _matrix4[8], + setter: (value) { + if (value is double) { + _matrix4[8] = value; + } + }); + properties['m32'] = BindingObjectProperty( + getter: () => _matrix4[9], + setter: (value) { + if (value is double) { + _matrix4[9] = value; + } + }); + properties['m33'] = BindingObjectProperty( + getter: () => _matrix4[10], + setter: (value) { + if (value is double) { + _matrix4[10] = value; + } + }); + properties['m34'] = BindingObjectProperty( + getter: () => _matrix4[11], + setter: (value) { + if (value is double) { + _matrix4[11] = value; + } + }); + + // m41 = e + properties['m41'] = BindingObjectProperty( + getter: () => _matrix4[12], + setter: (value) { + if (value is double) { + _matrix4[12] = value; + } + }); + properties['e'] = BindingObjectProperty( + getter: () => _matrix4[12], + setter: (value) { + if (value is double) { + _matrix4[12] = value; + } + }); + // m42 = f + properties['m42'] = BindingObjectProperty( + getter: () => _matrix4[13], + setter: (value) { + if (value is double) { + _matrix4[13] = value; + } + }); + properties['f'] = BindingObjectProperty( + getter: () => _matrix4[13], + setter: (value) { + if (value is double) { + _matrix4[13] = value; + } + }); + properties['m43'] = BindingObjectProperty( + getter: () => _matrix4[14], + setter: (value) { + if (value is double) { + _matrix4[14] = value; + } + }); + properties['m44'] = BindingObjectProperty( + getter: () => _matrix4[15], + setter: (value) { + if (value is double) { + _matrix4[15] = value; + } + }); + } + + DOMMatrix flipX() { + Matrix4 m = Matrix4.identity()..setEntry(0, 0, -1); + return DOMMatrix.fromMatrix4( + BindingContext(ownerView, ownerView.contextId, allocateNewBindingObject()), _matrix4 * m, _is2D); + } + + DOMMatrix flipY() { + Matrix4 m = Matrix4.identity()..setEntry(1, 1, -1); + return DOMMatrix.fromMatrix4( + BindingContext(ownerView, ownerView.contextId, allocateNewBindingObject()), _matrix4 * m, _is2D); + } + + DOMMatrix inverse() { + Matrix4 m = Matrix4.inverted(_matrix4); + return DOMMatrix.fromMatrix4(BindingContext(ownerView, ownerView.contextId, allocateNewBindingObject()), m, _is2D); + } + + DOMMatrix multiply(DOMMatrix domMatrix) { + Matrix4 m = _matrix4.multiplied(domMatrix.matrix); + return DOMMatrix.fromMatrix4( + BindingContext(ownerView, ownerView.contextId, allocateNewBindingObject()), m, _is2D); + } + + DOMMatrix rotateAxisAngle(double x, double y, double z, double angle) { + Matrix4 m = DOMMatrixReadOnly.rotate3d(x, y, z, angle); + bool flag2D = _is2D; + if (x != 0 || y != 0) { + flag2D = false; + } + return DOMMatrix.fromMatrix4( + BindingContext(ownerView, ownerView.contextId, allocateNewBindingObject()), _matrix4 * m, flag2D); + } + + DOMMatrix rotateZ(double x) { + // rotate(-90) => rotateZ(-90) + double xRad = x * degrees2Radians; + Matrix4 m = _matrix4.clone()..rotateZ(xRad); + return DOMMatrix.fromMatrix4(BindingContext(ownerView, ownerView.contextId, allocateNewBindingObject()), m, _is2D); + } + + DOMMatrix rotate(double x, double y, double z) { + Matrix4 m = DOMMatrixReadOnly.rotate3d(x, y, z, 0); + bool flag2D = _is2D; + if (x != 0 || y == 0) { + flag2D = false; + } + return DOMMatrix.fromMatrix4(BindingContext(ownerView, ownerView.contextId, allocateNewBindingObject()), _matrix4 * m, flag2D); + } + + DOMMatrix rotateFromVector(double x, double y) { + Matrix4 m = _matrix4.clone(); + double rad = atan2(y, x); + double angle = rad * radians2Degrees; + if (angle % 360 != 0) { + m.rotateZ(rad); + } + return DOMMatrix.fromMatrix4(BindingContext(ownerView, ownerView.contextId, allocateNewBindingObject()), m, _is2D); + } + + DOMMatrix scale(double sX, double sY, double sZ, double oriX, double oriY, double oriZ) { + Matrix4 m = _matrix4.clone() + ..translate(oriX, oriY, oriZ) + ..scale(sX, sY, sZ) + ..translate(-oriX, -oriY, -oriZ); + bool flag2D = _is2D; + if (sZ != 1 || oriZ != 0) { + flag2D = false; + } + return DOMMatrix.fromMatrix4(BindingContext(ownerView, ownerView.contextId, allocateNewBindingObject()), m, flag2D); + } + + DOMMatrix scale3d(double scale, double oriX, double oriY, double oriZ) { + return this.scale(scale, scale, scale, oriX, oriY, oriZ); + } + + DOMMatrix scaleNonUniform(double sX, double sY) { + Matrix4 m = _matrix4.clone()..scale(sX, sY, 1); + return DOMMatrix.fromMatrix4(BindingContext(ownerView, ownerView.contextId, allocateNewBindingObject()), m, _is2D); + } + + DOMMatrix skewX(double sx) { + Matrix4 m = Matrix4.skewX(sx * degrees2Radians); + return DOMMatrix.fromMatrix4( + BindingContext(ownerView, ownerView.contextId, allocateNewBindingObject()), _matrix4 * m, _is2D); + } + + DOMMatrix skewY(double sy) { + Matrix4 m = Matrix4.skewY(sy * degrees2Radians); + return DOMMatrix.fromMatrix4( + BindingContext(ownerView, ownerView.contextId, allocateNewBindingObject()), _matrix4 * m, _is2D); + } + + @override + String toString() { + if (_is2D) { + // a,b,c,d,e,f + return 'matrix(${_matrix4[0]},${_matrix4[1]},${_matrix4[4]},${_matrix4[5]},${_matrix4[12]},${_matrix4[13]})'; + } else { + return 'matrix3d(${_matrix4.storage.join(',')})'; + } + } + + DOMPoint transformPoint(DOMPoint domPoint) { + double x = domPoint.x, y = domPoint.y, z = domPoint.z, w = domPoint.w; + if (isIdentityOrTranslation(_matrix4)) { + x += _matrix4[12]; + y += _matrix4[13]; + z += _matrix4[14]; + } else { + // Multiply a homogeneous point by a matrix and return the transformed point + // like method v4MulPointByMatrix(v,m) in WebKit TransformationMatrix + List input = [x, y, z, w]; + x = dot(input, _matrix4.row0); + y = dot(input, _matrix4.row1); + z = dot(input, _matrix4.row2); + w = dot(input, _matrix4.row3); + } + + List list = [x, y, z, w]; + return DOMPoint(BindingContext(ownerView, ownerView.contextId, allocateNewBindingObject()), list); + } + + DOMMatrix translate(double tx, double ty, double tz) { + Matrix4 m = _matrix4.clone()..translate(tx, ty, tz); + bool flag2D = _is2D; + if (tz != 0) { + flag2D = false; + } + return DOMMatrix.fromMatrix4( + BindingContext(ownerView, ownerView.contextId, allocateNewBindingObject()), m, flag2D); + } + + static bool isIdentityOrTranslation(Matrix4 matrix) { + return matrix[0] == 1 && matrix[1] == 0 && matrix[2] == 0 && matrix[3] == 0 && + matrix[4] == 0 && matrix[5] == 1 && matrix[6] == 0 && matrix[7] == 0 && + matrix[8] == 0 && matrix[9] == 0 && matrix[10] == 1 && matrix[11] == 0 && + matrix[15] == 1; + } + + static Matrix4 rotate3d(double x, double y, double z, double angle) { + // normalizing x,y,z + List vec = [x,y,z]; + List norVec = normalize(vec); + double nx = norVec[0]; + double ny = norVec[1]; + double nz = norVec[2]; + + // The 3D rotation matrix is described in CSS Transforms with alpha. + // Please see: https://drafts.csswg.org/css-transforms-2/#Rotate3dDefined + double alpha_in_radians = degrees2Radians * (angle / 2); + double sc = sin (alpha_in_radians) * cos(alpha_in_radians); + double sq = sin(alpha_in_radians) * sin(alpha_in_radians); + + double m11 = 1 - 2 * (ny * ny + nz * nz) * sq; + double m12 = 2 * (nx * ny * sq + nz * sc); + double m13 = 2 * (nx * nz * sq - ny * sc); + double m14 = 0; + double m21 = 2 * (nx * ny * sq - nz * sc); + double m22 = 1 - 2 * (nx * nx + nz * nz) * sq; + double m23 = 2 * (ny * nz * sq + nx * sc); + double m24 = 0; + double m31 = 2 * (nx * nz * sq + ny * sc); + double m32 = 2 * (ny * nz * sq - nx * sc); + double m33 = 1 - 2 * (nx * nx + ny * ny) * sq; + double m34 = 0; + double m41 = 0; + double m42 = 0; + double m43 = 0; + double m44 = 1; + + return new Matrix4(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44); } } diff --git a/webf/lib/src/geometry/dom_point.dart b/webf/lib/src/geometry/dom_point.dart new file mode 100644 index 0000000000..7289f744cd --- /dev/null +++ b/webf/lib/src/geometry/dom_point.dart @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +import 'dart:typed_data'; + +import 'package:vector_math/vector_math_64.dart'; +import 'package:webf/foundation.dart'; +import 'dom_point_readonly.dart'; + +class DOMPoint extends DOMPointReadOnly { + DOMPoint(BindingContext context, List domPointInit) : super(context, domPointInit) {} + + DOMPoint.fromPoint(BindingContext context, DOMPoint? point) : super.fromPoint(context, point) {} +} diff --git a/webf/lib/src/geometry/dom_point_readonly.dart b/webf/lib/src/geometry/dom_point_readonly.dart new file mode 100644 index 0000000000..86b264b2d7 --- /dev/null +++ b/webf/lib/src/geometry/dom_point_readonly.dart @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ +import 'dart:math'; +import 'dart:typed_data'; + +import 'package:flutter/cupertino.dart'; +import 'package:vector_math/vector_math_64.dart'; +import 'package:webf/bridge.dart'; +import 'package:webf/foundation.dart'; +import 'package:webf/geometry.dart'; +import 'package:webf/src/css/matrix.dart'; +import 'package:webf/src/geometry/dom_point.dart'; + + +class DOMPointReadOnly extends DynamicBindingObject { + final List _data = [0,0,0,1]; + DOMPointReadOnly(BindingContext context, List domPointInit) : super(context) { + for(int i = 0; i < domPointInit.length; i ++) { + if(domPointInit[i].runtimeType == double) { + _data[i] = domPointInit[i]; + } + } + } + + DOMPointReadOnly.fromPoint(BindingContext context, DOMPoint? point) : super(context) { + if (point != null) { + _data[0] = point.x; + _data[1] = point.y; + _data[2] = point.z; + _data[3] = point.w; + } + } + + double get x => _data[0]; + double get y => _data[1]; + double get z => _data[2]; + double get w => _data[3]; + + @override + void initializeMethods(Map methods) { + methods['matrixTransform'] = BindingObjectMethodSync(call: (args) { + BindingObject domMatrix = args[0]; + if (domMatrix is DOMMatrix) { + return matrixTransform(domMatrix); + } + }); + } + + @override + void initializeProperties(Map properties) { + properties['x'] = BindingObjectProperty( + getter: () => _data[0], + setter: (value) => _data[0] = castToType(value).toDouble() + ); + properties['y'] = BindingObjectProperty( + getter: () => _data[1], + setter: (value) => _data[1] = castToType(value).toDouble() + ); + properties['z'] = BindingObjectProperty( + getter: () => _data[2], + setter: (value) => _data[2] = castToType(value).toDouble() + ); + properties['w'] = BindingObjectProperty( + getter: () => _data[3], + setter: (value) => _data[3] = castToType(value).toDouble() + ); + } + + DOMPoint matrixTransform(DOMMatrix domMatrix) { + Matrix4 matrix = domMatrix.matrix; + double x = _data[0], y = _data[1], z = _data[2], w = _data[3]; + if ( DOMMatrixReadOnly.isIdentityOrTranslation(matrix)) { + x += matrix[12]; + y += matrix[13]; + z += matrix[14]; + } else { + // Multiply a homogeneous point by a matrix and return the transformed point + // like method v4MulPointByMatrix(v,m) in WebKit TransformationMatrix + List input = [x, y, z, w]; + x = dot(input, matrix.row0); + y = dot(input, matrix.row1); + z = dot(input, matrix.row2); + w = dot(input, matrix.row3); + } + + List list = [x, y, z, w]; + return DOMPoint(BindingContext(ownerView, ownerView.contextId, allocateNewBindingObject()), list); + } +} diff --git a/webf/lib/src/html/canvas/canvas_context_2d.dart b/webf/lib/src/html/canvas/canvas_context_2d.dart index 56d9c78893..1a6cf908ae 100644 --- a/webf/lib/src/html/canvas/canvas_context_2d.dart +++ b/webf/lib/src/html/canvas/canvas_context_2d.dart @@ -114,6 +114,16 @@ class CanvasRenderingContext2D extends DynamicBindingObject { castToType(args[2]).toDouble()); } }); + methods['ellipse'] = BindingObjectMethodSync( + call: (args) => ellipse( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble(), + castToType(args[2]).toDouble(), + castToType(args[3]).toDouble(), + castToType(args[4]).toDouble(), + castToType(args[5]).toDouble(), + castToType(args[6]).toDouble(), + anticlockwise: (args.length > 7 && args[7] == 1) ? true : false)); methods['strokeText'] = BindingObjectMethodSync(call: (args) { if (args.length > 3) { double maxWidth = castToType(args[3]).toDouble(); @@ -147,11 +157,17 @@ class CanvasRenderingContext2D extends DynamicBindingObject { castToType(args[4]).toDouble(), castToType(args[5]).toDouble())); methods['clip'] = BindingObjectMethodSync(call: (args) { - PathFillType fillType = - (args.isNotEmpty && castToType(args[0]) == EVENODD) - ? PathFillType.evenOdd - : PathFillType.nonZero; - return clip(fillType); + if (args.isNotEmpty && args[0] is Path2D) { + PathFillType fillType = (args.length == 2 && args[1] == EVENODD) + ? PathFillType.evenOdd + : PathFillType.nonZero; + return clip(fillType, path: args[0]); + } else { + PathFillType fillType = (args.length == 1 && args[0] == EVENODD) + ? PathFillType.evenOdd + : PathFillType.nonZero; + return clip(fillType); + } }); methods['closePath'] = BindingObjectMethodSync(call: (_) => closePath()); methods['drawImage'] = BindingObjectMethodSync(call: (args) { @@ -190,10 +206,24 @@ class CanvasRenderingContext2D extends DynamicBindingObject { } }); methods['fill'] = BindingObjectMethodSync(call: (args) { - PathFillType fillType = (args.isNotEmpty && args[0] == EVENODD) - ? PathFillType.evenOdd - : PathFillType.nonZero; - return fill(fillType); + if (args.isEmpty) { + return fill(PathFillType.nonZero); + } else if (args.length == 1) { + if (args[0] is Path2D) { + return fill(PathFillType.nonZero, path: args[0]); + } else { + PathFillType fillType = args[0] == EVENODD + ? PathFillType.evenOdd + : PathFillType.nonZero; + return fill(fillType); + } + } else if (args.length == 2) { + assert(args[0] is Path2D); + PathFillType fillType = (args[1] == EVENODD) + ? PathFillType.evenOdd + : PathFillType.nonZero; + return fill(fillType, path: args[0]); + } }); methods['lineTo'] = BindingObjectMethodSync( call: (args) => lineTo(castToType(args[0]).toDouble(), @@ -215,12 +245,25 @@ class CanvasRenderingContext2D extends DynamicBindingObject { castToType(args[3]).toDouble())); methods['rotate'] = BindingObjectMethodSync( call: (args) => rotate(castToType(args[0]).toDouble())); + methods['roundRect'] = BindingObjectMethodSync( + call: (args) => roundRect( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble(), + castToType(args[2]).toDouble(), + castToType(args[3]).toDouble(), + List.from(args[4]))); methods['resetTransform'] = BindingObjectMethodSync(call: (_) => resetTransform()); methods['scale'] = BindingObjectMethodSync( call: (args) => scale(castToType(args[0]).toDouble(), castToType(args[1]).toDouble())); - methods['stroke'] = BindingObjectMethodSync(call: (args) => stroke()); + methods['stroke'] = BindingObjectMethodSync(call: (args) { + Path2D? path; + if (args.length == 1 && args[0] is Path2D) { + path = args[0]; + } + stroke(path: path); + }); methods['setTransform'] = BindingObjectMethodSync( call: (args) => setTransform( castToType(args[0]).toDouble(), @@ -560,27 +603,34 @@ class CanvasRenderingContext2D extends DynamicBindingObject { }); } - void clip(PathFillType fillType) { + void clip(PathFillType fillType, {Path2D? path}) { addAction((Canvas canvas, Size size) { + path?.path.fillType = fillType; path2d.path.fillType = fillType; - canvas.clipPath(path2d.path); + canvas.clipPath(path?.path ?? path2d.path); }); } - void fill(PathFillType fillType) { + void fill(PathFillType fillType, {Path2D? path}) { addAction((Canvas canvas, Size size) { if (fillStyle is! Color) { return; } - path2d.path.fillType = fillType; + Paint paint = Paint() ..color = fillStyle as Color ..style = PaintingStyle.fill; - canvas.drawPath(path2d.path, paint); + if (path != null) { + path.path.fillType = fillType; + canvas.drawPath(path.path, paint); + } else { + path2d.path.fillType = fillType; + canvas.drawPath(path2d.path, paint); + } }); } - void stroke() { + void stroke({Path2D? path}) { addAction((Canvas canvas, Size size) { if (strokeStyle is! Color) { return; @@ -592,7 +642,7 @@ class CanvasRenderingContext2D extends DynamicBindingObject { ..strokeWidth = lineWidth ..strokeMiterLimit = miterLimit ..style = PaintingStyle.stroke; - canvas.drawPath(path2d.path, paint); + canvas.drawPath(path?.path ?? path2d.path, paint); }); } @@ -802,6 +852,12 @@ class CanvasRenderingContext2D extends DynamicBindingObject { }); } + void roundRect(double x, double y, double w, double h, List radii) { + addAction((Canvas canvas, Size size) { + path2d.roundRect(x, y, w, h, radii); + }); + } + // transformations (default transform is the identity matrix) void scale(double x, double y) { _matrix.scale(x, y); diff --git a/webf/lib/src/html/canvas/canvas_path_2d.dart b/webf/lib/src/html/canvas/canvas_path_2d.dart index 1f2f8f17a5..617d0bf48b 100644 --- a/webf/lib/src/html/canvas/canvas_path_2d.dart +++ b/webf/lib/src/html/canvas/canvas_path_2d.dart @@ -8,13 +8,16 @@ import 'dart:typed_data'; import 'dart:ui'; import 'package:vector_math/vector_math_64.dart'; +import 'package:webf/foundation.dart'; +import 'package:webf/geometry.dart'; +import 'package:webf/src/css/values/path.dart'; // ignore: non_constant_identifier_names final double _2pi = 2 * math.pi; final double _pi = math.pi; final double _piOver2 = math.pi / 2; -class Path2D { +class Path2D extends DynamicBindingObject { Path _path = Path(); get path { @@ -23,6 +26,89 @@ class Path2D { final List _points = []; + Path2D({BindingContext? context, List? path2DInit}) : super(context) { + if (path2DInit != null && path2DInit.isNotEmpty) { + switch (path2DInit[0].runtimeType) { + case Path2D: + addPath(path2DInit[0] as Path2D); + break; + case String: + addSVGPath(path2DInit[0] as String); + break; + } + } + } + + @override + void initializeMethods(Map methods) { + methods['moveTo'] = BindingObjectMethodSync( + call: (args) => moveTo( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble())); + methods['closePath'] = BindingObjectMethodSync(call: (_) => closePath()); + methods['lineTo'] = BindingObjectMethodSync( + call: (args) => lineTo(castToType(args[0]).toDouble(), + castToType(args[1]).toDouble())); + methods['bezierCurveTo'] = BindingObjectMethodSync( + call: (args) => bezierCurveTo( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble(), + castToType(args[2]).toDouble(), + castToType(args[3]).toDouble(), + castToType(args[4]).toDouble(), + castToType(args[5]).toDouble())); + methods['arc'] = BindingObjectMethodSync( + call: (args) => arc( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble(), + castToType(args[2]).toDouble(), + castToType(args[3]).toDouble(), + castToType(args[4]).toDouble(), + anticlockwise: (args.length > 5 && args[5] == 1) ? true : false)); + methods['arcTo'] = BindingObjectMethodSync( + call: (args) => arcTo( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble(), + castToType(args[2]).toDouble(), + castToType(args[3]).toDouble(), + castToType(args[4]).toDouble())); + methods['ellipse'] = BindingObjectMethodSync( + call: (args) => ellipse( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble(), + castToType(args[2]).toDouble(), + castToType(args[3]).toDouble(), + castToType(args[4]).toDouble(), + castToType(args[5]).toDouble(), + castToType(args[6]).toDouble(), + anticlockwise: (args.length > 7 && args[7] == 1) ? true : false)); + methods['rect'] = BindingObjectMethodSync( + call: (args) => rect( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble(), + castToType(args[2]).toDouble(), + castToType(args[3]).toDouble())); + methods['roundRect'] = BindingObjectMethodSync( + call: (args) => roundRect( + castToType(args[0]).toDouble(), + castToType(args[1]).toDouble(), + castToType(args[2]).toDouble(), + castToType(args[3]).toDouble(), + List.from(args[4]))); + methods['addPath'] = BindingObjectMethodSync(call: (args) { + if (args.length > 1 && args[1] is DOMMatrix) { + addPath(args[0], matrix4: (args[1] as DOMMatrix).matrix.storage); + } else if (args.isNotEmpty && args[0] is Path2D) { + addPath(args[0]); + } + }); + } + + @override + void initializeProperties(Map properties) { + // TODO: implement initializeProperties + } + void _setPoint(x, y) { _points.add(x); _points.add(y); @@ -117,6 +203,16 @@ class Path2D { _syncCurrentPoint(); } + void addSVGPath(String pathStr, {Float64List? matrix4}) { + CSSPath cssPath = CSSPath.parseValue(pathStr); + if (cssPath != CSSPath.None) { + final path = Path(); + cssPath.applyTo(path); + _path.addPath(path, Offset.zero, matrix4: matrix4); + _syncCurrentPoint(); + } + } + /// Adds a cubic bezier segment that curves from the current point /// to the given point (x3,y3), using the control _points (x1,y1) and /// (x2,y2). @@ -146,6 +242,75 @@ class Path2D { _setPoint(rect.left, rect.bottom); } + // radii values is same with css border-radius + void roundRect(double x, double y, double width, double height, List radii) { + if(radii.isEmpty ) { + return; + } + + RRect rRect = RRect.zero; + Rect rect = Rect.fromLTWH(x, y, width, height); + + if (radii.length == 1) { + rRect = RRect.fromRectAndRadius(rect, Radius.circular(radii[0])); + _path.addRRect(rRect); + } else { + double topLeft = 0; + double topRight = 0; + double bottomRight = 0; + double bottomLeft = 0; + + if (radii.length == 2) { + topLeft = radii[0]; + topRight = radii[1]; + bottomRight = radii[0]; + bottomLeft = radii[1]; + } else if (radii.length == 3) { + topLeft = radii[0]; + topRight = radii[1]; + bottomRight = radii[2]; + bottomLeft = radii[1]; + } else if (radii.length >= 4) { + topLeft = radii[0]; + topRight = radii[1]; + bottomRight = radii[2]; + bottomLeft = radii[3]; + } + + // swap when width or height is negative + // swap left <-> right + if (width < 0) { + double tmp = topLeft; + topLeft = topRight; + topRight = tmp; + + tmp = bottomLeft; + bottomLeft = bottomRight; + bottomRight = tmp; + } + + // swap top <-> bottom + if (height < 0) { + double tmp = topLeft; + topLeft = bottomLeft; + bottomLeft = tmp; + + tmp = topRight; + topRight = bottomRight; + bottomRight = tmp; + } + + RRect rRect = RRect.fromRectAndCorners( + rect, + topLeft:Radius.circular(topLeft), + topRight:Radius.circular(topRight), + bottomRight:Radius.circular(bottomRight), + bottomLeft:Radius.circular(bottomLeft) + ); + _path.addRRect(rRect); + } + } + /// degenerateEllipse() handles a degenerated ellipse using several lines. /// /// Let's see a following example: line to ellipse to line. diff --git a/webf/lib/src/module/dom_matrix.dart b/webf/lib/src/module/dom_matrix.dart new file mode 100644 index 0000000000..957240295b --- /dev/null +++ b/webf/lib/src/module/dom_matrix.dart @@ -0,0 +1,30 @@ +import 'package:flutter/cupertino.dart'; +import 'package:webf/bridge.dart'; +import 'package:webf/foundation.dart'; +import 'package:webf/geometry.dart'; +import 'package:webf/module.dart'; + +class DOMMatrixModule extends BaseModule { + DOMMatrixModule(super.moduleManager); + + @override + void dispose() { + } + + @override + invoke(String method, params, InvokeModuleCallback callback) { + if (method == 'fromMatrix') { + if (params.runtimeType == DOMMatrix) { + DOMMatrix domMatrix = (params as DOMMatrix); + return DOMMatrix.fromMatrix4( + BindingContext(domMatrix.ownerView, domMatrix.ownerView.contextId, allocateNewBindingObject()), + domMatrix.matrix.clone(), + domMatrix.is2D); + } + } + } + + @override + String get name => 'DOMMatrix'; + +} diff --git a/webf/lib/src/module/dom_point.dart b/webf/lib/src/module/dom_point.dart new file mode 100644 index 0000000000..013a43011e --- /dev/null +++ b/webf/lib/src/module/dom_point.dart @@ -0,0 +1,30 @@ +import 'package:flutter/cupertino.dart'; +import 'package:webf/bridge.dart'; +import 'package:webf/foundation.dart'; +import 'package:webf/geometry.dart'; +import 'package:webf/module.dart'; +import 'package:webf/src/geometry/dom_point.dart'; + +class DOMPointModule extends BaseModule { + DOMPointModule(super.moduleManager); + + @override + void dispose() { + } + + @override + invoke(String method, params, InvokeModuleCallback callback) { + if (method == 'fromPoint') { + if (params.runtimeType == DOMPoint) { + DOMPoint domPoint = (params as DOMPoint); + + return DOMPoint.fromPoint( + BindingContext(domPoint.ownerView, domPoint.ownerView.contextId, allocateNewBindingObject()), domPoint); + } + } + } + + @override + String get name => 'DOMPoint'; + +} diff --git a/webf/lib/src/module/module_manager.dart b/webf/lib/src/module/module_manager.dart index 2f1b54cf82..1bf449cdba 100644 --- a/webf/lib/src/module/module_manager.dart +++ b/webf/lib/src/module/module_manager.dart @@ -3,6 +3,7 @@ * Copyright (C) 2022-present The WebF authors. All rights reserved. */ import 'package:webf/bridge.dart' as bridge; +import 'package:webf/src/module/dom_point.dart'; import 'package:webf/webf.dart'; import 'local_storage.dart'; import 'session_storage.dart'; @@ -44,6 +45,8 @@ void _defineModuleCreator() { _defineModule((ModuleManager? moduleManager) => LocalStorageModule(moduleManager)); _defineModule((ModuleManager? moduleManager) => SessionStorageModule(moduleManager)); _defineModule((ModuleManager? moduleManager) => WebSocketModule(moduleManager)); + _defineModule((ModuleManager? moduleManager) => DOMMatrixModule(moduleManager)); + _defineModule((ModuleManager? moduleManager) => DOMPointModule(moduleManager)); } final Map _creatorMap = {};