// SPDX-License-Identifier: GPL-2.0-or-later /** @file * TODO: insert short description here *//* * Authors: see git history * * Copyright (C) 2018 Authors * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include #include "attributes.h" #include "display/cairo-utils.h" #include "sp-mesh-gradient.h" /* * Mesh Gradient */ //#define MESH_DEBUG //#define OBJECT_TRACE SPMeshGradient::SPMeshGradient() : SPGradient(), type( SP_MESH_TYPE_COONS ), type_set(false) { #ifdef OBJECT_TRACE objectTrace( "SPMeshGradient::SPMeshGradient" ); #endif // Start coordinate of mesh this->x.unset(SVGLength::NONE, 0.0, 0.0); this->y.unset(SVGLength::NONE, 0.0, 0.0); #ifdef OBJECT_TRACE objectTrace( "SPMeshGradient::SPMeshGradient", false ); #endif } SPMeshGradient::~SPMeshGradient() { #ifdef OBJECT_TRACE objectTrace( "SPMeshGradient::~SPMeshGradient (empty function)" ); objectTrace( "SPMeshGradient::~SPMeshGradient", false ); #endif } void SPMeshGradient::build(SPDocument *document, Inkscape::XML::Node *repr) { #ifdef OBJECT_TRACE objectTrace( "SPMeshGradient::build" ); #endif SPGradient::build(document, repr); // Start coordinate of meshgradient this->readAttr(SPAttr::X); this->readAttr(SPAttr::Y); this->readAttr(SPAttr::TYPE); #ifdef OBJECT_TRACE objectTrace( "SPMeshGradient::build", false ); #endif } void SPMeshGradient::set(SPAttr key, gchar const *value) { #ifdef OBJECT_TRACE objectTrace( "SPMeshGradient::set" ); #endif switch (key) { case SPAttr::X: if (!this->x.read(value)) { this->x.unset(SVGLength::NONE, 0.0, 0.0); } this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SPAttr::Y: if (!this->y.read(value)) { this->y.unset(SVGLength::NONE, 0.0, 0.0); } this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SPAttr::TYPE: if (value) { if (!strcmp(value, "coons")) { this->type = SP_MESH_TYPE_COONS; } else if (!strcmp(value, "bicubic")) { this->type = SP_MESH_TYPE_BICUBIC; } else { std::cerr << "SPMeshGradient::set(): invalid value " << value << std::endl; } this->type_set = TRUE; } else { // std::cout << "SPMeshGradient::set() No value " << std::endl; this->type = SP_MESH_TYPE_COONS; this->type_set = FALSE; } this->requestModified(SP_OBJECT_MODIFIED_FLAG); break; default: SPGradient::set(key, value); break; } #ifdef OBJECT_TRACE objectTrace( "SPMeshGradient::set", false ); #endif } /** * Write mesh gradient attributes to associated repr. */ Inkscape::XML::Node* SPMeshGradient::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { #ifdef OBJECT_TRACE objectTrace( "SPMeshGradient::write", false ); #endif if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { repr = xml_doc->createElement("svg:meshgradient"); } if ((flags & SP_OBJECT_WRITE_ALL) || this->x._set) { repr->setAttributeSvgDouble("x", this->x.computed); } if ((flags & SP_OBJECT_WRITE_ALL) || this->y._set) { repr->setAttributeSvgDouble("y", this->y.computed); } if ((flags & SP_OBJECT_WRITE_ALL) || this->type_set) { switch (this->type) { case SP_MESH_TYPE_COONS: repr->setAttribute("type", "coons"); break; case SP_MESH_TYPE_BICUBIC: repr->setAttribute("type", "bicubic"); break; default: // Do nothing break; } } SPGradient::write(xml_doc, repr, flags); #ifdef OBJECT_TRACE objectTrace( "SPMeshGradient::write", false ); #endif return repr; } cairo_pattern_t* SPMeshGradient::pattern_new(cairo_t * /*ct*/, Geom::OptRect const &bbox, double opacity) { using Geom::X; using Geom::Y; #ifdef MESH_DEBUG std::cout << "sp_meshgradient_create_pattern: " << (*bbox) << " " << opacity << std::endl; #endif this->ensureArray(); cairo_pattern_t *cp = nullptr; SPMeshNodeArray* my_array = &array; if( type_set ) { switch (type) { case SP_MESH_TYPE_COONS: // std::cout << "SPMeshGradient::pattern_new: Coons" << std::endl; break; case SP_MESH_TYPE_BICUBIC: array.bicubic( &array_smoothed, type ); my_array = &array_smoothed; break; } } cp = cairo_pattern_create_mesh(); for( unsigned int i = 0; i < my_array->patch_rows(); ++i ) { for( unsigned int j = 0; j < my_array->patch_columns(); ++j ) { SPMeshPatchI patch( &(my_array->nodes), i, j ); cairo_mesh_pattern_begin_patch( cp ); cairo_mesh_pattern_move_to( cp, patch.getPoint( 0, 0 )[X], patch.getPoint( 0, 0 )[Y] ); for( unsigned int k = 0; k < 4; ++k ) { #ifdef DEBUG_MESH std::cout << i << " " << j << " " << patch.getPathType( k ) << " ("; for( int p = 0; p < 4; ++p ) { std::cout << patch.getPoint( k, p ); } std::cout << ") " << patch.getColor( k ).toString() << std::endl; #endif switch ( patch.getPathType( k ) ) { case 'l': case 'L': case 'z': case 'Z': cairo_mesh_pattern_line_to( cp, patch.getPoint( k, 3 )[X], patch.getPoint( k, 3 )[Y] ); break; case 'c': case 'C': { std::vector< Geom::Point > pts = patch.getPointsForSide( k ); cairo_mesh_pattern_curve_to( cp, pts[1][X], pts[1][Y], pts[2][X], pts[2][Y], pts[3][X], pts[3][Y] ); break; } default: // Shouldn't happen std::cout << "sp_mesh_create_pattern: path error" << std::endl; } if( patch.tensorIsSet(k) ) { // Tensor point defined relative to corner. Geom::Point t = patch.getTensorPoint(k); cairo_mesh_pattern_set_control_point( cp, k, t[X], t[Y] ); //std::cout << " sp_mesh_create_pattern: tensor " << k // << " set to " << t << "." << std::endl; } else { // Geom::Point t = patch.coonsTensorPoint(k); //std::cout << " sp_mesh_create_pattern: tensor " << k // << " calculated as " << t << "." <gradientTransform; if (this->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) { Geom::Affine bbox2user(bbox->width(), 0, 0, bbox->height(), bbox->left(), bbox->top()); gs2user *= bbox2user; } ink_cairo_pattern_set_matrix(cp, gs2user.inverse()); /* cairo_pattern_t *cp = cairo_pattern_create_radial( rg->fx.computed, rg->fy.computed, 0, rg->cx.computed, rg->cy.computed, rg->r.computed); sp_gradient_pattern_common_setup(cp, gr, bbox, opacity); */ return cp; }