diff --git a/.gitignore b/.gitignore index f9d34141..6f1942f4 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,5 @@ debug-mod/ local-mod/ local-gdb/ out/ +3rdparty/blend2d/ +3rdparty/blend2d-apps/ diff --git a/examples/vue.fluid b/examples/vue.fluid index 3c003bb5..e4dbfb5e 100644 --- a/examples/vue.fluid +++ b/examples/vue.fluid @@ -1,14 +1,5 @@ --[[ A file viewer for recognised documents like SVG and RIPL - -Example: parasol vue.fluid file=[Path] - -Optional parameters are: - - width: Width of the application window - height: Height of the application window - path: Path to use in the file selector. - file: A source file to load and display in the application. --]] require 'gui' @@ -44,6 +35,7 @@ Optional parameters are:

height

Height of the application window.

path

Path to use in the file selector.

file

A source file to load and display in the application.
+

no-browser

Hide the file browser.

@@ -191,50 +183,58 @@ end glViewport = glWindow:clientViewport({ aspectRatio = ARF_MEET }) - -- File view on the left - - glView = gui.listView({ - target = glViewport, - x = glWindow.margins.left, - y = glWindow.margins.top, - width = 200, - yOffset = glWindow.margins.top, - dragDrop = true, - sensitive = true, - multiSelect = false, - borderless = true - }) + if arg('no-browser') then + glViewArea = glViewport.new('VectorViewport', { + x=glWindow.margins.left, y=glWindow.margins.top, + xOffset=glWindow.margins.right, yOffset=glWindow.margins.top + }) + else + -- File view on the left + + glView = gui.listView({ + target = glViewport, + x = glWindow.margins.left, + y = glWindow.margins.top, + width = 200, + yOffset = glWindow.margins.top, + dragDrop = true, + sensitive = true, + multiSelect = false, + borderless = true + }) - glFileview = gui.fileview(glView, { - navigationBar = true, - path = arg('path', ':'), - filterList = { - { name='SVG Images', pattern='.svg' }, - { name='RIPL Documents', pattern={ '.ripl', '.rpl' } }, - { name='JPEG Images', pattern={ '.jpeg', '.jpg' } }, - { name='PNG Images', pattern='.png' } - }, - fileSelected = function(FileView) - displayFile(FileView.selectionPath()) - title = FileView.selectionFile() - if glStats.width and glStats.height then - title = title .. ' [ ' .. glStats.width .. ' x ' .. glStats.height .. ' ]' + glFileview = gui.fileview(glView, { + navigationBar = true, + displayPath = true, + path = arg('path', ':'), + filterList = { + { name='SVG Images', pattern='.svg' }, + { name='RIPL Documents', pattern={ '.ripl', '.rpl' } }, + { name='JPEG Images', pattern={ '.jpeg', '.jpg' } }, + { name='PNG Images', pattern='.png' } + }, + fileSelected = function(FileView) + displayFile(FileView.selectionPath()) + title = FileView.selectionFile() + if glStats.width and glStats.height then + title = title .. ' [ ' .. glStats.width .. ' x ' .. glStats.height .. ' ]' + end + glWindow:setTitle(title) end - glWindow:setTitle(title) - end - }) + }) - -- Document viewer on the right + -- Document viewer on the right - glViewArea = glViewport.new('VectorViewport', { - x=glView.viewport.x + glView.viewport.width + 6, y=glWindow.margins.top, - xOffset=glWindow.margins.right, yOffset=glWindow.margins.top - }) + glViewArea = glViewport.new('VectorViewport', { + x=glView.viewport.x + glView.viewport.width + 6, y=glWindow.margins.top, + xOffset=glWindow.margins.right, yOffset=glWindow.margins.top + }) - gui.divider({ - left = glView.viewport, right = glViewArea, - minA = 100, maxA = glWindow.surface.insideWidth * 0.4, minB = 100 - }) + gui.divider({ + left = glView.viewport, right = glViewArea, + minA = 100, maxA = glWindow.surface.insideWidth * 0.4, minB = 100 + }) + end -- Display a file from the commandline, or show the application documentation. diff --git a/scripts/gui.fluid b/scripts/gui.fluid index 00f12899..ca1f652c 100644 --- a/scripts/gui.fluid +++ b/scripts/gui.fluid @@ -371,25 +371,6 @@ gui.createIcon = function(Scene, Path, Size, Theme, Name) end end) - -- Adjust gradient size to match the viewport height. - - local viewport = icon.pattern.viewport.child - if viewport != nil then - while child != nil do - if child.class.id == ID_VECTORVIEWPORT then - viewport = child - break - end - end - - if viewport.viewHeight < viewport.viewWidth then - icon.gradient.y1 = viewport.viewY - ((viewport.viewWidth - viewport.viewHeight) * 0.5) - icon.gradient.y2 = icon.gradient.y1 + viewport.viewWidth - else - icon.gradient.y1 = viewport.viewY - icon.gradient.y2 = viewport.viewY + viewport.viewHeight - end - end check(Scene.mtAddDef(Name, icon.pattern)) diff --git a/src/svg/class_svg.cpp b/src/svg/class_svg.cpp index 6ea4a40c..80a84ece 100644 --- a/src/svg/class_svg.cpp +++ b/src/svg/class_svg.cpp @@ -88,6 +88,7 @@ static ERR SVG_Free(extSVG *Self) { if (Self->AnimationTimer) { UpdateTimer(Self->AnimationTimer, 0); + if (Self->Scene) UnsubscribeAction(Self->Scene, AC::Free); Self->AnimationTimer = 0; } diff --git a/src/svg/tests/gradients/w3-pservers-grad-04-b.svg b/src/svg/tests/gradients/w3-pservers-grad-04-b.svg new file mode 100644 index 00000000..66b3fc2c --- /dev/null +++ b/src/svg/tests/gradients/w3-pservers-grad-04-b.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + Multi-color linear gradient. + + + + + + + + + + + + + + Multi-color radial gradient. + + + diff --git a/src/svg/tests/gradients/w3-pservers-grad-05-b.svg b/src/svg/tests/gradients/w3-pservers-grad-05-b.svg new file mode 100644 index 00000000..5f5b92b3 --- /dev/null +++ b/src/svg/tests/gradients/w3-pservers-grad-05-b.svg @@ -0,0 +1,29 @@ + + + + Background + + + + + + + + + + + + Background + + + + + + + + + + + + diff --git a/src/svg/tests/gradients/w3-pservers-grad-10-b.svg b/src/svg/tests/gradients/w3-pservers-grad-10-b.svg new file mode 100644 index 00000000..82ea60b1 --- /dev/null +++ b/src/svg/tests/gradients/w3-pservers-grad-10-b.svg @@ -0,0 +1,35 @@ + + + Testing spreadMethod attribute + + + + + + + + + spreadMethod=pad + + + + + + + + + spreadMethod=reflect + + + + + + + + + spreadMethod=repeat + + + + diff --git a/src/svg/tests/gradients/w3-pservers-grad-14-b.svg b/src/svg/tests/gradients/w3-pservers-grad-14-b.svg new file mode 100644 index 00000000..a7528671 --- /dev/null +++ b/src/svg/tests/gradients/w3-pservers-grad-14-b.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Gradient 'spreadMethod' values + + + spreadMethod="pad" + + + spreadMethod="reflect" + + + spreadMethod="repeat" + + + spreadMethod="default" + + + + spreadMethod="pad" + + + spreadMethod="reflect" + + + spreadMethod="repeat" + + + spreadMethod="default" + + + + diff --git a/src/svg/tests/test-svg.fluid b/src/svg/tests/test-svg.fluid index 4009de8c..58737a34 100644 --- a/src/svg/tests/test-svg.fluid +++ b/src/svg/tests/test-svg.fluid @@ -68,11 +68,15 @@ function testVStripes() hashTestSVG('patterns/vstripes.svg', 0x2f85dc2 function testW3Gradients01() hashTestSVG('gradients/w3-pservers-grad-01-b.svg', 0xe637a6fa) end function testW3Gradients02() hashTestSVG('gradients/w3-pservers-grad-02-b.svg', 0x69157174) end +function testW3Gradients04() hashTestSVG('gradients/w3-pservers-grad-04-b.svg', 0x30e19fc3) end +function testW3Gradients05() hashTestSVG('gradients/w3-pservers-grad-05-b.svg', 0x80b6a37d) end function testW3Gradients07() hashTestSVG('gradients/w3-pservers-grad-07-b.svg', 0xfb7be5fe) end function testW3Gradients08() hashTestSVG('gradients/w3-pservers-grad-08-b.svg', 0x97f7b810) end function testW3Gradients09() hashTestSVG('gradients/w3-pservers-grad-09-b.svg', 0xfd34d4f1) end +function testW3Gradients10() hashTestSVG('gradients/w3-pservers-grad-10-b.svg', 0x8a32cb66) end function testW3Gradients11() hashTestSVG('gradients/w3-pservers-grad-11-b.svg', 0x4f4a58f3) end function testW3Gradients12() hashTestSVG('gradients/w3-pservers-grad-12-b.svg', 0xd59b60ce) end +function testW3Gradients14() hashTestSVG('gradients/w3-pservers-grad-14-b.svg', 0xb26061f7) end function testW3Gradients15() hashTestSVG('gradients/w3-pservers-grad-15-b.svg', 0x531aca96) end function testW3Gradients22() hashTestSVG('gradients/w3-pservers-grad-22-b.svg', 0x5477019b) end @@ -252,11 +256,15 @@ function testW3TextB25() hashTestSVG('text/w3-text-ws-03-t.svg', -1) end 'testInheritClip', 'testW3Gradients01', 'testW3Gradients02', + 'testW3Gradients04', + 'testW3Gradients05', 'testW3Gradients07', 'testW3Gradients08', 'testW3Gradients09', + 'testW3Gradients10', 'testW3Gradients11', 'testW3Gradients12', + 'testW3Gradients14', 'testW3Gradients15', 'testW3Gradients22', -- Patterns diff --git a/src/vector/defs/gradient.cpp b/src/vector/defs/gradient.cpp index 07c240eb..90a2e947 100644 --- a/src/vector/defs/gradient.cpp +++ b/src/vector/defs/gradient.cpp @@ -618,6 +618,7 @@ static ERR VECTORGRADIENT_SET_X1(extVectorGradient *Self, Unit &Value) if (Value.scaled()) Self->Flags = (Self->Flags | VGF::SCALED_X1) & (~VGF::FIXED_X1); else Self->Flags = (Self->Flags | VGF::FIXED_X1) & (~VGF::SCALED_X1); Self->X1 = Value; + Self->CalcAngle = true; return ERR::Okay; } @@ -643,6 +644,7 @@ static ERR VECTORGRADIENT_SET_X2(extVectorGradient *Self, Unit &Value) if (Value.scaled()) Self->Flags = (Self->Flags | VGF::SCALED_X2) & (~VGF::FIXED_X2); else Self->Flags = (Self->Flags | VGF::FIXED_X2) & (~VGF::SCALED_X2); Self->X2 = Value; + Self->CalcAngle = true; return ERR::Okay; } @@ -668,6 +670,7 @@ static ERR VECTORGRADIENT_SET_Y1(extVectorGradient *Self, Unit &Value) if (Value.scaled()) Self->Flags = (Self->Flags | VGF::SCALED_Y1) & (~VGF::FIXED_Y1); else Self->Flags = (Self->Flags | VGF::FIXED_Y1) & (~VGF::SCALED_Y1); Self->Y1 = Value; + Self->CalcAngle = true; return ERR::Okay; } @@ -693,6 +696,7 @@ static ERR VECTORGRADIENT_SET_Y2(extVectorGradient *Self, Unit &Value) if (Value.scaled()) Self->Flags = (Self->Flags | VGF::SCALED_Y2) & (~VGF::FIXED_Y2); else Self->Flags = (Self->Flags | VGF::FIXED_Y2) & (~VGF::SCALED_Y2); Self->Y2 = Value; + Self->CalcAngle = true; return ERR::Okay; } diff --git a/src/vector/scene/scene.cpp b/src/vector/scene/scene.cpp index 58ad2345..7b259fe2 100644 --- a/src/vector/scene/scene.cpp +++ b/src/vector/scene/scene.cpp @@ -46,7 +46,7 @@ static void fill_image(VectorState &, const TClipRectangle &, agg::path_ agg::rasterizer_scanline_aa<> &, DOUBLE Alpha = 1.0); static void fill_gradient(VectorState &, const TClipRectangle &, agg::path_storage *, - const agg::trans_affine &, DOUBLE, DOUBLE, const extVectorGradient &, GRADIENT_TABLE *, + const agg::trans_affine &, DOUBLE, DOUBLE, extVectorGradient &, GRADIENT_TABLE *, agg::renderer_base &, agg::rasterizer_scanline_aa<> &); static void fill_pattern(VectorState &, const TClipRectangle &, agg::path_storage *, diff --git a/src/vector/scene/scene_fill.cpp b/src/vector/scene/scene_fill.cpp index 684ba518..f3ce31af 100644 --- a/src/vector/scene/scene_fill.cpp +++ b/src/vector/scene/scene_fill.cpp @@ -61,19 +61,19 @@ void SceneRenderer::render_fill(VectorState &State, extVector &Vector, agg::rast // Path: The original vector path without transforms. // Transform: Transforms to be applied to the path and to align the image. -static void fill_image(VectorState &State, const TClipRectangle &Bounds, agg::path_storage &Path, VSM SampleMethod, - const agg::trans_affine &Transform, DOUBLE ViewWidth, DOUBLE ViewHeight, +static void fill_image(VectorState &State, const TClipRectangle &Bounds, agg::path_storage &Path, VSM SampleMethod, + const agg::trans_affine &Transform, double ViewWidth, double ViewHeight, objVectorImage &Image, agg::renderer_base &RenderBase, - agg::rasterizer_scanline_aa<> &Raster, DOUBLE Alpha) + agg::rasterizer_scanline_aa<> &Raster, double Alpha) { - const DOUBLE c_width = (Image.Units IS VUNIT::USERSPACE) ? ViewWidth : Bounds.width(); - const DOUBLE c_height = (Image.Units IS VUNIT::USERSPACE) ? ViewHeight : Bounds.height(); - const DOUBLE dx = Bounds.left + (dmf::hasScaledX(Image.Dimensions) ? (c_width * Image.X) : Image.X); - const DOUBLE dy = Bounds.top + (dmf::hasScaledY(Image.Dimensions) ? (c_height * Image.Y) : Image.Y); + const double c_width = (Image.Units IS VUNIT::USERSPACE) ? ViewWidth : Bounds.width(); + const double c_height = (Image.Units IS VUNIT::USERSPACE) ? ViewHeight : Bounds.height(); + const double dx = Bounds.left + (dmf::hasScaledX(Image.Dimensions) ? (c_width * Image.X) : Image.X); + const double dy = Bounds.top + (dmf::hasScaledY(Image.Dimensions) ? (c_height * Image.Y) : Image.Y); Path.approximation_scale(Transform.scale()); - DOUBLE x_scale, y_scale, x_offset, y_offset; + double x_scale, y_scale, x_offset, y_offset; calc_aspectratio("fill_image", Image.AspectRatio, c_width, c_height, Image.Bitmap->Width, Image.Bitmap->Height, &x_offset, &y_offset, &x_scale, &y_scale); @@ -100,28 +100,29 @@ static void fill_image(VectorState &State, const TClipRectangle &Bounds, // The Raster must contain the shape's path. // TODO: Support gradient_xy (rounded corner), gradient_sqrt_xy -static void fill_gradient(VectorState &State, const TClipRectangle &Bounds, agg::path_storage *Path, - const agg::trans_affine &Transform, DOUBLE ViewWidth, DOUBLE ViewHeight, const extVectorGradient &Gradient, +static void fill_gradient(VectorState &State, const TClipRectangle &Bounds, agg::path_storage *Path, + const agg::trans_affine &Transform, double ViewWidth, double ViewHeight, extVectorGradient &Gradient, GRADIENT_TABLE *Table, agg::renderer_base &RenderBase, agg::rasterizer_scanline_aa<> &Raster) { + constexpr LONG MAX_SPAN = 256; typedef agg::span_interpolator_linear<> interpolator_type; typedef agg::span_allocator span_allocator_type; - typedef agg::pod_auto_array color_array_type; + typedef agg::pod_auto_array color_array_type; typedef agg::renderer_base RENDERER_BASE_TYPE; agg::trans_affine transform; interpolator_type span_interpolator(transform); span_allocator_type span_allocator; - const DOUBLE c_width = Gradient.Units IS VUNIT::USERSPACE ? ViewWidth : Bounds.width(); - const DOUBLE c_height = Gradient.Units IS VUNIT::USERSPACE ? ViewHeight : Bounds.height(); - const DOUBLE x_offset = Gradient.Units IS VUNIT::USERSPACE ? 0 : Bounds.left; - const DOUBLE y_offset = Gradient.Units IS VUNIT::USERSPACE ? 0 : Bounds.top; + const double c_width = Gradient.Units IS VUNIT::USERSPACE ? ViewWidth : Bounds.width(); + const double c_height = Gradient.Units IS VUNIT::USERSPACE ? ViewHeight : Bounds.height(); + const double x_offset = Gradient.Units IS VUNIT::USERSPACE ? 0 : Bounds.left; + const double y_offset = Gradient.Units IS VUNIT::USERSPACE ? 0 : Bounds.top; Path->approximation_scale(Transform.scale()); - auto render_gradient = [&](S SpreadMethod, DOUBLE Span) { + auto render_gradient = [&](S SpreadMethod, double Span) { typedef agg::span_gradient span_gradient_type; typedef agg::renderer_scanline_aa renderer_gradient_type; @@ -140,47 +141,70 @@ static void fill_gradient(VectorState &State, const TClipRectangle &Boun }; if (Gradient.Type IS VGT::LINEAR) { - TClipRectangle area; - - if ((Gradient.Flags & VGF::SCALED_X1) != VGF::NIL) area.left = x_offset + (c_width * Gradient.X1); - else area.left = x_offset + Gradient.X1; - - if ((Gradient.Flags & VGF::SCALED_X2) != VGF::NIL) area.right = x_offset + (c_width * Gradient.X2); - else area.right = x_offset + Gradient.X2; - - if ((Gradient.Flags & VGF::SCALED_Y1) != VGF::NIL) area.top = y_offset + (c_height * Gradient.Y1); - else area.top = y_offset + Gradient.Y1; + auto span = MAX_SPAN; + if (Gradient.Units IS VUNIT::BOUNDING_BOX) { + // NOTE: In this mode we are mapping a 1x1 gradient square into the target path, which means + // the gradient is stretched into position as a square. We don't map the (X,Y) points to the + // bounding box and draw point-to-point. + + const double x = x_offset + (c_width * Gradient.X1); + const double y = y_offset + (c_height * Gradient.Y1); + + if (Gradient.CalcAngle) { + const double dx = Gradient.X2 - Gradient.X1; + const double dy = Gradient.Y2 - Gradient.Y1; + Gradient.Angle = atan2(dy, dx); + Gradient.Length = sqrt((dx * dx) + (dy * dy)); + Gradient.CalcAngle = false; + } - if ((Gradient.Flags & VGF::SCALED_Y2) != VGF::NIL) area.bottom = y_offset + (c_height * Gradient.Y2); - else area.bottom = y_offset + Gradient.Y2; + transform.scale(Gradient.Length); + transform.rotate(Gradient.Angle); + transform.scale(c_width / span, c_height / span); + transform.translate(x, y); + } + else { + TClipRectangle area; + if ((Gradient.Flags & VGF::SCALED_X1) != VGF::NIL) area.left = x_offset + (c_width * Gradient.X1); + else area.left = x_offset + Gradient.X1; + + if ((Gradient.Flags & VGF::SCALED_X2) != VGF::NIL) area.right = x_offset + (c_width * Gradient.X2); + else area.right = x_offset + Gradient.X2; + + if ((Gradient.Flags & VGF::SCALED_Y1) != VGF::NIL) area.top = y_offset + (c_height * Gradient.Y1); + else area.top = y_offset + Gradient.Y1; + + if ((Gradient.Flags & VGF::SCALED_Y2) != VGF::NIL) area.bottom = y_offset + (c_height * Gradient.Y2); + else area.bottom = y_offset + Gradient.Y2; + + if (Gradient.CalcAngle) { + const double dx = area.width(); + const double dy = area.height(); + Gradient.Angle = atan2(dy, dx); + Gradient.Length = sqrt((dx * dx) + (dy * dy)); + Gradient.CalcAngle = false; + } - // Calculate the gradient's transition from the point at (x1,y1) to (x2,y2) + transform.scale(Gradient.Length / span); + transform.rotate(Gradient.Angle); + transform.translate(area.left, area.top); + } - const DOUBLE dx = area.width(); - const DOUBLE dy = area.height(); - transform.scale(sqrt((dx * dx) + (dy * dy)) / 256.0); - transform.rotate(atan2(dy, dx)); - transform.translate(area.left, area.top); apply_transforms(Gradient, transform); transform *= Transform; transform.invert(); - agg::gradient_x gradient_func; // gradient_x is a horizontal gradient with infinite height - typedef agg::span_gradient span_gradient_type; - typedef agg::renderer_scanline_aa renderer_gradient_type; - span_gradient_type span_gradient(span_interpolator, gradient_func, *Table, 0, 256); - renderer_gradient_type solidgrad(RenderBase, span_allocator, span_gradient); + agg::gradient_x gradient_func; - if (State.mClipStack->empty()) { - agg::scanline_u8 scanline; - agg::render_scanlines(Raster, scanline, solidgrad); + if (Gradient.SpreadMethod IS VSPREAD::REFLECT) { + agg::gradient_reflect_adaptor spread_method(gradient_func); + render_gradient(spread_method, span); } - else { // Masked gradient - agg::alpha_mask_gray8 alpha_mask(State.mClipStack->top().m_renderer); - agg::scanline_u8_am masked_scanline(alpha_mask); - - agg::render_scanlines(Raster, masked_scanline, solidgrad); + else if (Gradient.SpreadMethod IS VSPREAD::REPEAT) { + agg::gradient_repeat_adaptor spread_method(gradient_func); + render_gradient(spread_method, span); } + else render_gradient(gradient_func, span); } else if (Gradient.Type IS VGT::RADIAL) { agg::point_d c, f; @@ -202,26 +226,21 @@ static void fill_gradient(VectorState &State, const TClipRectangle &Boun if ((c.x IS f.x) and (c.y IS f.y)) { // Standard radial gradient, where the focal point is the same as the gradient center - DOUBLE radial_col_span = Gradient.Radius; + double radial_col_span = Gradient.Radius; if (Gradient.Units IS VUNIT::USERSPACE) { // Coordinates are relative to the viewport if ((Gradient.Flags & VGF::SCALED_RADIUS) != VGF::NIL) { // Gradient is a ratio of the viewport's dimensions radial_col_span = (ViewWidth + ViewHeight) * Gradient.Radius * 0.5; } } else { // Coordinates are scaled to the bounding box - if ((Gradient.Flags & VGF::SCALED_RADIUS) != VGF::NIL) { - // Set radial_col_span to the wider of the width/height - if (c_height > c_width) { - radial_col_span = c_height * Gradient.Radius; - transform.scaleX(c_width / c_height); - } - else { - radial_col_span = c_width * Gradient.Radius; - transform.scaleY(c_height / c_width); - } + // Set radial_col_span to the wider of the width/height + if (c_height > c_width) { + radial_col_span = c_height * Gradient.Radius; + transform.scaleX(c_width / c_height); } else { - //transform *= agg::trans_affine_scaling(Gradient.Radius * 0.01 / radial_col_span); + radial_col_span = c_width * Gradient.Radius; + transform.scaleY(c_height / c_width); } } @@ -230,6 +249,10 @@ static void fill_gradient(VectorState &State, const TClipRectangle &Boun transform.scale(radial_col_span * (1.0 / MIN_SPAN)); radial_col_span = MIN_SPAN; } + else if (radial_col_span > MAX_SPAN) { + transform.scale(radial_col_span * (1.0 / MAX_SPAN)); + radial_col_span = MAX_SPAN; + } transform.translate(c); apply_transforms(Gradient, transform); @@ -253,8 +276,8 @@ static void fill_gradient(VectorState &State, const TClipRectangle &Boun // the SVG standard, the focal point had to be within the base radius. Later specifications allowed it to // be placed outside of that radius. - DOUBLE radial_col_span = Gradient.Radius; - DOUBLE focal_radius = Gradient.FocalRadius; + double radial_col_span = Gradient.Radius; + double focal_radius = Gradient.FocalRadius; if (focal_radius <= 0) focal_radius = Gradient.Radius; if (Gradient.Units IS VUNIT::USERSPACE) { // Coordinates are relative to the viewport @@ -264,21 +287,16 @@ static void fill_gradient(VectorState &State, const TClipRectangle &Boun } } else { // Coordinates are scaled to the bounding box - if ((Gradient.Flags & VGF::SCALED_RADIUS) != VGF::NIL) { - // Set radial_col_span to the wider of the width/height - if (c_height > c_width) { - radial_col_span = c_height * radial_col_span; - focal_radius = c_height * focal_radius; - transform.scaleX(c_width / c_height); - } - else { - radial_col_span = c_width * radial_col_span; - focal_radius = c_width * focal_radius; - transform.scaleY(c_height / c_width); - } + // Set radial_col_span to the wider of the width/height + if (c_height > c_width) { + radial_col_span = c_height * radial_col_span; + focal_radius = c_height * focal_radius; + transform.scaleX(c_width / c_height); } else { - //transform *= agg::trans_affine_scaling(Gradient.Radius * 0.01 / radial_col_span); + radial_col_span = c_width * radial_col_span; + focal_radius = c_width * focal_radius; + transform.scaleY(c_height / c_width); } } @@ -315,7 +333,7 @@ static void fill_gradient(VectorState &State, const TClipRectangle &Boun // Standard diamond gradient, where the focal point is the same as the gradient center - DOUBLE radial_col_span = Gradient.Radius; + double radial_col_span = Gradient.Radius; if (Gradient.Units IS VUNIT::USERSPACE) { if ((Gradient.Flags & VGF::SCALED_RADIUS) != VGF::NIL) { radial_col_span = (ViewWidth + ViewHeight) * Gradient.Radius * 0.5; @@ -323,18 +341,15 @@ static void fill_gradient(VectorState &State, const TClipRectangle &Boun else transform *= agg::trans_affine_scaling(Gradient.Radius * 0.01); } else { // Align to vector's bounding box - if ((Gradient.Flags & VGF::SCALED_RADIUS) != VGF::NIL) { - // Set radial_col_span to the wider of the width/height - if (c_height > c_width) { - radial_col_span = c_height * Gradient.Radius; - transform.scaleX(c_width / c_height); - } - else { - radial_col_span = c_width * Gradient.Radius; - transform.scaleY(c_height / c_width); - } + // Set radial_col_span to the wider of the width/height + if (c_height > c_width) { + radial_col_span = c_height * Gradient.Radius; + transform.scaleX(c_width / c_height); + } + else { + radial_col_span = c_width * Gradient.Radius; + transform.scaleY(c_height / c_width); } - else transform *= agg::trans_affine_scaling(Gradient.Radius * 0.01); } agg::gradient_diamond gradient_func; @@ -365,7 +380,7 @@ static void fill_gradient(VectorState &State, const TClipRectangle &Boun // Standard conic gradient, where the focal point is the same as the gradient center - DOUBLE radial_col_span = Gradient.Radius; + double radial_col_span = Gradient.Radius; if (Gradient.Units IS VUNIT::USERSPACE) { if ((Gradient.Flags & VGF::SCALED_RADIUS) != VGF::NIL) { radial_col_span = (ViewWidth + ViewHeight) * Gradient.Radius * 0.5; @@ -373,18 +388,15 @@ static void fill_gradient(VectorState &State, const TClipRectangle &Boun else transform *= agg::trans_affine_scaling(Gradient.Radius * 0.01); } else { // Bounding box - if ((Gradient.Flags & VGF::SCALED_RADIUS) != VGF::NIL) { - // Set radial_col_span to the wider of the width/height - if (c_height > c_width) { - radial_col_span = c_height * Gradient.Radius; - transform.scaleX(c_width / c_height); - } - else { - radial_col_span = c_width * Gradient.Radius; - transform.scaleY(c_height / c_width); - } + // Set radial_col_span to the wider of the width/height + if (c_height > c_width) { + radial_col_span = c_height * Gradient.Radius; + transform.scaleX(c_width / c_height); + } + else { + radial_col_span = c_width * Gradient.Radius; + transform.scaleY(c_height / c_width); } - else transform *= agg::trans_affine_scaling(Gradient.Radius * 0.01); } agg::gradient_conic gradient_func; @@ -438,20 +450,20 @@ static void fill_gradient(VectorState &State, const TClipRectangle &Boun // Patterns rendered with BOUNDING_BOX require real-time calculation as they have a dependency on the target // vector's dimensions. -static void fill_pattern(VectorState &State, const TClipRectangle &Bounds, agg::path_storage *Path, - VSM SampleMethod, const agg::trans_affine &Transform, DOUBLE ViewWidth, DOUBLE ViewHeight, +static void fill_pattern(VectorState &State, const TClipRectangle &Bounds, agg::path_storage *Path, + VSM SampleMethod, const agg::trans_affine &Transform, double ViewWidth, double ViewHeight, extVectorPattern &Pattern, agg::renderer_base &RenderBase, agg::rasterizer_scanline_aa<> &Raster) { - const DOUBLE c_width = (Pattern.Units IS VUNIT::USERSPACE) ? ViewWidth : Bounds.width(); - const DOUBLE c_height = (Pattern.Units IS VUNIT::USERSPACE) ? ViewHeight : Bounds.height(); - const DOUBLE x_offset = (Pattern.Units IS VUNIT::USERSPACE) ? 0 : Bounds.left; - const DOUBLE y_offset = (Pattern.Units IS VUNIT::USERSPACE) ? 0 : Bounds.top; + const double c_width = (Pattern.Units IS VUNIT::USERSPACE) ? ViewWidth : Bounds.width(); + const double c_height = (Pattern.Units IS VUNIT::USERSPACE) ? ViewHeight : Bounds.height(); + const double x_offset = (Pattern.Units IS VUNIT::USERSPACE) ? 0 : Bounds.left; + const double y_offset = (Pattern.Units IS VUNIT::USERSPACE) ? 0 : Bounds.top; Path->approximation_scale(Transform.scale()); if (Pattern.Units IS VUNIT::USERSPACE) { // Use fixed coordinates specified in the pattern. - DOUBLE dwidth, dheight; + double dwidth, dheight; if (dmf::hasScaledWidth(Pattern.Dimensions)) dwidth = c_width * Pattern.Width; else if (dmf::hasWidth(Pattern.Dimensions)) dwidth = Pattern.Width; else dwidth = 1; @@ -470,7 +482,7 @@ static void fill_pattern(VectorState &State, const TClipRectangle &Bound // BOUNDING_BOX. The pattern (x,y) is an optional offset applied to the base position of the vector's // path. The area is relative to the vector's bounds. - DOUBLE dwidth, dheight; + double dwidth, dheight; if (dmf::hasScaledWidth(Pattern.Dimensions)) dwidth = Pattern.Width * c_width; else if (dmf::hasWidth(Pattern.Dimensions)) { @@ -508,7 +520,7 @@ static void fill_pattern(VectorState &State, const TClipRectangle &Bound agg::trans_affine transform; - DOUBLE dx, dy; + double dx, dy; if (dmf::hasScaledX(Pattern.Dimensions)) dx = x_offset + (c_width * Pattern.X); else if (dmf::hasX(Pattern.Dimensions)) dx = x_offset + Pattern.X; else dx = x_offset; diff --git a/src/vector/vector.h b/src/vector/vector.h index 7a69215d..59b5c80c 100644 --- a/src/vector/vector.h +++ b/src/vector/vector.h @@ -323,6 +323,9 @@ class extVectorGradient : public objVectorGradient { STRING ID; LONG NumericID; WORD ChangeCounter; + DOUBLE Angle; + DOUBLE Length; + bool CalcAngle; // True if the Angle/Length values require recalculation. }; class extVectorPattern : public objVectorPattern {