"Fossies" - the Fresh Open Source Software Archive

Member "sk1-2.0rc4/src/uc2/formats/sk2/crenderer.py" (25 May 2019, 13282 Bytes) of package /linux/misc/sk1-2.0rc4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "crenderer.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.0rc3_vs_2.0rc4.

    1 # -*- coding: utf-8 -*-
    2 #
    3 #  Copyright (C) 2011-2015 by Igor E. Novikov
    4 #
    5 #  This program is free software: you can redistribute it and/or modify
    6 #  it under the terms of the GNU General Public License as published by
    7 #  the Free Software Foundation, either version 3 of the License, or
    8 #  (at your option) any later version.
    9 #
   10 #  This program is distributed in the hope that it will be useful,
   11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
   12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13 #  GNU General Public License for more details.
   14 #
   15 #  You should have received a copy of the GNU General Public License
   16 #  along with this program.  If not, see <https://www.gnu.org/licenses/>.
   17 
   18 import cairo
   19 from copy import deepcopy
   20 
   21 from uc2 import libcairo, libgeom, sk2const
   22 from uc2.formats.sk2 import sk2_model
   23 
   24 CAIRO_BLACK = [0.0, 0.0, 0.0]
   25 CAIRO_GRAY = [0.5, 0.5, 0.5]
   26 CAIRO_WHITE = [1.0, 1.0, 1.0]
   27 
   28 CAPS = {
   29     sk2const.CAP_BUTT: cairo.LINE_CAP_BUTT,
   30     sk2const.CAP_ROUND: cairo.LINE_CAP_ROUND,
   31     sk2const.CAP_SQUARE: cairo.LINE_CAP_SQUARE,
   32 }
   33 
   34 JOINS = {
   35     sk2const.JOIN_BEVEL: cairo.LINE_JOIN_BEVEL,
   36     sk2const.JOIN_MITER: cairo.LINE_JOIN_MITER,
   37     sk2const.JOIN_ROUND: cairo.LINE_JOIN_ROUND,
   38 }
   39 
   40 EXTEND = {
   41     sk2const.GRADIENT_EXTEND_NONE: cairo.EXTEND_NONE,
   42     sk2const.GRADIENT_EXTEND_PAD: cairo.EXTEND_PAD,
   43     sk2const.GRADIENT_EXTEND_REPEAT: cairo.EXTEND_REPEAT,
   44     sk2const.GRADIENT_EXTEND_REFLECT: cairo.EXTEND_REFLECT,
   45 }
   46 
   47 
   48 class CairoRenderer:
   49     cms = None
   50     antialias_flag = True
   51     contour_flag = False
   52     stroke_style = []
   53     for_display = False
   54 
   55     def __init__(self, cms):
   56         self.cms = cms
   57 
   58     # -------ROUTINES----------
   59 
   60     def get_color(self, color):
   61         """
   62         Provides Cairo suitable, CMS processed color values. 
   63         """
   64         if self.for_display:
   65             r, g, b = self.cms.get_display_color(color)
   66         else:
   67             r, g, b = self.cms.get_rgb_color(color)[1]
   68         return r, g, b, color[2]
   69 
   70     def get_surface(self, obj):
   71         if self.contour_flag:
   72             return obj.handler.get_surface(self.cms, stroke_mode=True)
   73         else:
   74             return obj.handler.get_surface(self.cms, self.cms.proofing)
   75 
   76     def _create_pattern_image(self, obj):
   77         fill = obj.style[0]
   78         pattern_fill = fill[2]
   79         image_obj = sk2_model.Pixmap(obj.config)
   80         image_obj.handler.load_from_b64str(self.cms, pattern_fill[1])
   81         image_obj.handler.flip_top_to_bottom()
   82         if pattern_fill[0] == sk2const.PATTERN_IMG and len(pattern_fill) > 2:
   83             image_obj.style[3] = deepcopy(pattern_fill[2])
   84         return image_obj
   85 
   86     def get_pattern_surface(self, obj):
   87         image_obj = self._create_pattern_image(obj)
   88         if self.contour_flag:
   89             if not obj.cache_gray_pattern_img:
   90                 s = image_obj.handler.get_surface(self.cms, stroke_mode=True)
   91                 obj.cache_gray_pattern_img = s
   92             return obj.cache_gray_pattern_img
   93         elif self.cms.proofing:
   94             if not obj.cache_ps_pattern_img:
   95                 s = image_obj.handler.get_surface(self.cms, True)
   96                 obj.cache_ps_pattern_img = s
   97             return obj.cache_ps_pattern_img
   98         else:
   99             if not obj.cache_pattern_img:
  100                 s = image_obj.handler.get_surface(self.cms)
  101                 obj.cache_pattern_img = s
  102             return obj.cache_pattern_img
  103 
  104     # -------DOCUMENT RENDERING
  105 
  106     def render(self, ctx, objs=None):
  107         objs = objs or []
  108         if self.antialias_flag:
  109             ctx.set_antialias(cairo.ANTIALIAS_DEFAULT)
  110         else:
  111             ctx.set_antialias(cairo.ANTIALIAS_NONE)
  112 
  113         if objs:
  114             for obj in objs:
  115                 self.render_object(ctx, obj)
  116 
  117     def render_object(self, ctx, obj):
  118         if obj.is_primitive:
  119             self.render_primitives(ctx, obj)
  120         elif obj.is_container:
  121             self.render_container(ctx, obj)
  122         elif obj.is_group:
  123             for obj in obj.childs:
  124                 self.render_object(ctx, obj)
  125         else:
  126             pass
  127 
  128     def render_container(self, ctx, obj):
  129         ctx.save()
  130         container = obj.cache_container
  131 
  132         if container.style[1] and not self.contour_flag \
  133                 and container.style[1][7]:
  134             ctx.new_path()
  135             self.process_stroke(ctx, None, container.style)
  136             ctx.append_path(container.cache_cpath)
  137             ctx.stroke()
  138 
  139         if container.style[0] and not self.contour_flag:
  140             ctx.new_path()
  141             self.process_fill(ctx, container)
  142             ctx.append_path(container.cache_cpath)
  143             ctx.fill()
  144 
  145         ctx.new_path()
  146         ctx.append_path(container.cache_cpath)
  147         ctx.clip()
  148         for obj in obj.childs[1:]:
  149             self.render_object(ctx, obj)
  150         ctx.restore()
  151 
  152         if container.style[1] and not self.contour_flag and \
  153                 not container.style[1][7]:
  154             ctx.new_path()
  155             self.process_stroke(ctx, None, container.style)
  156             ctx.append_path(container.cache_cpath)
  157             ctx.stroke()
  158         elif self.contour_flag:
  159             ctx.new_path()
  160             self.process_stroke(ctx, None, self.stroke_style)
  161             ctx.append_path(container.cache_cpath)
  162             ctx.stroke()
  163 
  164     def render_image(self, ctx, obj):
  165         surface = self.get_surface(obj)
  166         if not surface:
  167             return
  168         canvas_matrix = ctx.get_matrix()
  169         canvas_trafo = libcairo.get_trafo_from_matrix(canvas_matrix)
  170         zoom = canvas_trafo[0]
  171 
  172         h = obj.size[1]
  173         lu_corner = libgeom.apply_trafo_to_point([0.0, float(h)], obj.trafo)
  174         x0, y0 = libgeom.apply_trafo_to_point(lu_corner, canvas_trafo)
  175 
  176         m11, m12, m21, m22 = obj.trafo[:4]
  177         matrix = cairo.Matrix(zoom * m11, -zoom * m12,
  178                               - zoom * m21, zoom * m22, x0, y0)
  179         ctx.set_matrix(matrix)
  180 
  181         ctx.set_source_surface(surface)
  182         if zoom * abs(m11) > .98:
  183             ctx.get_source().set_filter(cairo.FILTER_NEAREST)
  184 
  185         if self.contour_flag:
  186             ctx.paint_with_alpha(0.3)
  187         else:
  188             ctx.paint()
  189 
  190         ctx.set_matrix(canvas_matrix)
  191 
  192     def render_primitives(self, ctx, obj):
  193         if obj.cache_cpath is None:
  194             obj.update()
  195         if obj.is_pixmap:
  196             self.render_image(ctx, obj)
  197             return
  198         if obj.is_text:
  199             if self.contour_flag:
  200                 self.process_stroke(ctx, None, self.stroke_style)
  201                 for item in obj.cache_cpath:
  202                     if item:
  203                         ctx.new_path()
  204                         ctx.append_path(item)
  205                         ctx.stroke()
  206             else:
  207                 if obj.style[1] and obj.style[1][7]:
  208                     self.stroke_text_obj(ctx, obj)
  209                     self.fill_text_obj(ctx, obj)
  210                 else:
  211                     self.fill_text_obj(ctx, obj)
  212                     self.stroke_text_obj(ctx, obj)
  213             return
  214 
  215         if self.contour_flag:
  216             ctx.new_path()
  217             self.process_stroke(ctx, None, self.stroke_style)
  218             ctx.append_path(obj.cache_cpath)
  219             ctx.stroke()
  220             self.draw_arrows(ctx, obj, self.stroke_style)
  221         else:
  222             if obj.style[1] and obj.style[1][7]:
  223                 self.stroke_obj(ctx, obj)
  224                 self.draw_arrows(ctx, obj)
  225                 self.fill_obj(ctx, obj)
  226             else:
  227                 self.fill_obj(ctx, obj)
  228                 self.stroke_obj(ctx, obj)
  229                 self.draw_arrows(ctx, obj)
  230 
  231     def fill_obj(self, ctx, obj):
  232         if obj.style[0]:
  233             ctx.new_path()
  234             self.process_fill(ctx, obj)
  235             ctx.append_path(obj.cache_cpath)
  236             ctx.fill()
  237 
  238     def fill_text_obj(self, ctx, obj):
  239         if obj.style[0]:
  240             self.process_fill(ctx, obj)
  241             for item in obj.cache_cpath:
  242                 if item is not None:
  243                     ctx.new_path()
  244                     ctx.append_path(item)
  245                     ctx.fill()
  246 
  247     def stroke_obj(self, ctx, obj):
  248         if obj.style[1]:
  249             ctx.new_path()
  250             self.process_stroke(ctx, obj)
  251             ctx.append_path(obj.cache_cpath)
  252             ctx.stroke()
  253 
  254     def stroke_text_obj(self, ctx, obj):
  255         if obj.style[1]:
  256             self.process_stroke(ctx, obj)
  257             for item in obj.cache_cpath:
  258                 if item is not None:
  259                     ctx.new_path()
  260                     ctx.append_path(item)
  261                     ctx.stroke()
  262 
  263     def process_fill(self, ctx, obj):
  264         fill = obj.style[0]
  265         fill_rule = fill[0]
  266         if fill_rule & sk2const.FILL_CLOSED_ONLY and not obj.is_closed():
  267             ctx.set_source_rgba(0.0, 0.0, 0.0, 0.0)
  268             return
  269         if fill_rule & sk2const.FILL_EVENODD:
  270             ctx.set_fill_rule(cairo.FILL_RULE_EVEN_ODD)
  271         else:
  272             ctx.set_fill_rule(cairo.FILL_RULE_WINDING)
  273         if fill[1] == sk2const.FILL_SOLID:
  274             if obj.fill_trafo:
  275                 obj.fill_trafo = []
  276             color = fill[2]
  277             ctx.set_source_rgba(*self.get_color(color))
  278         elif fill[1] == sk2const.FILL_GRADIENT:
  279             if not obj.fill_trafo:
  280                 obj.fill_trafo = [] + sk2const.NORMAL_TRAFO
  281             gradient = fill[2]
  282             points = gradient[1]
  283             if not points:
  284                 obj.fill_trafo = [] + sk2const.NORMAL_TRAFO
  285                 points = libgeom.bbox_middle_points(obj.cache_bbox)
  286                 if gradient[0] == sk2const.GRADIENT_LINEAR:
  287                     points = [points[0], points[2]]
  288                 else:
  289                     points = [[points[1][0], points[2][1]], points[2]]
  290                 gradient[1] = points
  291             coords = points[0] + points[1]
  292             if gradient[0] == sk2const.GRADIENT_LINEAR:
  293                 grd = cairo.LinearGradient(*coords)
  294             else:
  295                 x0, y0 = coords[:2]
  296                 radius = libgeom.distance(*points)
  297                 grd = cairo.RadialGradient(x0, y0, 0, x0, y0, radius)
  298             for stop in gradient[2]:
  299                 grd.add_color_stop_rgba(stop[0], *self.get_color(stop[1]))
  300             matrix = cairo.Matrix(*obj.fill_trafo)
  301             matrix.invert()
  302             extend = cairo.EXTEND_PAD
  303             if len(gradient) > 3:
  304                 extend = EXTEND[gradient[3]]
  305             grd.set_extend(extend)
  306             grd.set_matrix(matrix)
  307             ctx.set_source(grd)
  308         elif fill[1] == sk2const.FILL_PATTERN:
  309             if not obj.fill_trafo:
  310                 obj.fill_trafo = [] + sk2const.NORMAL_TRAFO
  311                 obj.fill_trafo = obj.fill_trafo[:4] + [obj.cache_bbox[0],
  312                                                        obj.cache_bbox[3]]
  313             pattern_fill = fill[2]
  314             sp = cairo.SurfacePattern(self.get_pattern_surface(obj))
  315             sp.set_extend(cairo.EXTEND_REPEAT)
  316             flip_matrix = cairo.Matrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
  317             if len(pattern_fill) > 3:
  318                 pattern_matrix = cairo.Matrix(*pattern_fill[3])
  319                 pattern_matrix.invert()
  320                 flip_matrix = flip_matrix * pattern_matrix
  321             trafo_matrix = cairo.Matrix(*obj.fill_trafo)
  322             trafo_matrix.invert()
  323             flip_matrix = flip_matrix * trafo_matrix
  324             sp.set_matrix(flip_matrix)
  325             ctx.set_source(sp)
  326 
  327             canvas_matrix = ctx.get_matrix()
  328             canvas_trafo = libcairo.get_trafo_from_matrix(canvas_matrix)
  329             zoom = canvas_trafo[0]
  330             if zoom * abs(obj.fill_trafo[0]) > .98:
  331                 ctx.get_source().set_filter(cairo.FILTER_NEAREST)
  332 
  333     def process_stroke(self, ctx, obj, style=None):
  334         if style:
  335             stroke = style[1]
  336         else:
  337             stroke = obj.style[1]
  338 
  339         # Line width
  340         if not stroke[8]:
  341             line_width = stroke[1]
  342             if obj and obj.stroke_trafo:
  343                 obj.stroke_trafo = []
  344         else:
  345             if obj and not obj.stroke_trafo:
  346                 obj.stroke_trafo = [] + sk2const.NORMAL_TRAFO
  347             points = [[0.0, 0.0], [1.0, 0.0]]
  348             points = libgeom.apply_trafo_to_points(points, obj.stroke_trafo)
  349             coef = libgeom.distance(*points)
  350             line_width = stroke[1] * coef
  351         ctx.set_line_width(line_width)
  352         # Line color
  353         ctx.set_source_rgba(*self.get_color(stroke[2]))
  354         # Dashes
  355         dash = []
  356         for item in stroke[3]:
  357             dash.append(item * line_width)
  358         ctx.set_dash(dash)
  359 
  360         ctx.set_line_cap(CAPS[stroke[4]])
  361         ctx.set_line_join(JOINS[stroke[5]])
  362         ctx.set_miter_limit(stroke[6])
  363 
  364     def draw_arrows(self, ctx, obj, stroke=None):
  365         if not obj.is_curve:
  366             return
  367         obj_stroke = obj.style[1]
  368         if not obj_stroke or not obj_stroke[-1]:
  369             return
  370         for pair in obj.cache_arrows:
  371             for item in pair:
  372                 if item:
  373                     ctx.new_path()
  374                     ctx.append_path(item)
  375                     if stroke:
  376                         ctx.stroke()
  377                     else:
  378                         ctx.fill()