Example #1
0
def color_formatter(src="", language="", class_name=None, md=""):
    """Formatter wrapper."""

    from coloraide import Color

    try:
        result = src.strip()
        try:
            console, colors = execute(result)
            if len(colors) != 1 or len(colors[0]) != 1:
                raise ValueError('Need one color only')
            color = colors[0][0].color
            result = colors[0][0].string
        except Exception:
            color = Color(result.strip())
        el = Etree.Element('span')
        stops = []
        if not color.in_gamut(WEBSPACE):
            color.fit(WEBSPACE, in_place=True)
            attributes = {'class': "swatch out-of-gamut", "title": result}
            sub_el = Etree.SubElement(el, 'span', attributes)
            stops.append(color.convert(WEBSPACE).to_string(hex=True, alpha=False))
            if color.alpha < 1.0:
                stops[-1] += ' 50%'
                stops.append(color.convert(WEBSPACE).to_string(hex=True) + ' 50%')
        else:
            attributes = {'class': "swatch", "title": result}
            sub_el = Etree.SubElement(el, 'span', attributes)
            stops.append(color.convert(WEBSPACE).to_string(hex=True, alpha=False))
            if color.alpha < 1.0:
                stops[-1] += ' 50%'
                stops.append(color.convert(WEBSPACE).to_string(hex=True) + ' 50%')

        if not stops:
            stops.extend(['transparent'] * 2)
        if len(stops) == 1:
            stops.append(stops[0])

        Etree.SubElement(
            sub_el,
            'span',
            {
                "class": "swatch-color",
                "style": "--swatch-stops: {};".format(','.join(stops))
            }
        )

        el.append(md.inlinePatterns['backtick'].handle_code('css-color', result))
    except Exception:
        import traceback
        print(traceback.format_exc())
        el = md.inlinePatterns['backtick'].handle_code('text', src)
    return el
Example #2
0
    def test_from_luv(self):
        """Test null from Luv conversion."""

        c1 = Color('color(--luv 90% 0 0)')
        c2 = c1.convert('lchuv')
        self.assertColorEqual(c2, Color('color(--lchuv 90% 0 0)'))
        self.assertTrue(c2.is_nan('hue'))
Example #3
0
    def setup_image_border(self):
        """Setup_image_border."""

        ch_settings = sublime.load_settings('color_helper.sublime-settings')
        border_color = ch_settings.get('image_border_color')
        if border_color is not None:
            try:
                border_color = Color(border_color, filters=util.SRGB_SPACES)
                border_color.fit("srgb", in_place=True)
            except Exception:
                border_color = None

        if border_color is None:
            # Calculate border color for images
            border_color = Color(self.view.style()['background'],
                                 filters=util.SRGB_SPACES).convert("hsl")
            border_color.lightness = border_color.lightness + (
                30 if border_color.luminance() < 0.5 else -30)

        self.default_border = border_color.convert("srgb").to_string(**HEX)
        self.out_of_gamut = Color("transparent",
                                  filters=util.SRGB_SPACES).to_string(**HEX)
        self.out_of_gamut_border = Color(
            self.view.style().get('redish', "red"),
            filters=util.SRGB_SPACES).to_string(**HEX)
Example #4
0
    def assert_round_trip(self, color, space):
        """Cycle through all the other colors and convert to them and back and check the results."""

        c1 = Color(color).convert(space)
        for space in SPACES:
            # Print the color space to easily identify which color space broke.
            c2 = c1.convert(space)
            c2.convert(c1.space(), in_place=True)
            # Catch cases where we are really close to 360 which should wrap to 0
            for c in (c1, c2):
                if isinstance(c._space, Cylindrical):
                    if util.round_half_up(util.no_nan(c.hue),
                                          c.PRECISION) == 360:
                        c.hue = 0
            # Run rounded string back through parsing in case we hit something like a hue that needs normalization.
            str1 = Color(c1.to_string(color=True,
                                      fit=False)).to_string(color=True,
                                                            fit=False)
            str2 = Color(c2.to_string(color=True,
                                      fit=False)).to_string(color=True,
                                                            fit=False)
            # Print failing results for debug purposes
            if str1 != str2:
                print('----- Convert: {} <=> {} -----'.format(
                    c1.space(), space))
                print('Original: ', color.to_string(color=True, fit=False))
                print(c1.space() + ': ', str1, c1.coords())
                print(space + ': ', str2, c2.coords())
                assert str1 == str2
Example #5
0
    def test_from_din99o(self):
        """Test null from DIN99o conversion."""

        c1 = Color('color(--din99o 90% 0 0)')
        c2 = c1.convert('lch99o')
        self.assertColorEqual(c2, Color('color(--lch99o 90% 0 0)'))
        self.assertTrue(c2.is_nan('hue'))
Example #6
0
    def test_from_lab(self):
        """Test null from Lab conversion."""

        c1 = Color('lab(90% 0 0)')
        c2 = c1.convert('lch')
        self.assertColorEqual(c2, Color('lch(90% 0 0)'))
        self.assertTrue(c2.is_nan('hue'))
Example #7
0
    def test_from_hsl(self):
        """Test null from Lab conversion."""

        c1 = Color('hsl(0 0% 50%)')
        c2 = c1.convert('hsv')
        self.assertColorEqual(c2, Color('color(--hsv 0 0% 50%)'))
        self.assertTrue(c2.is_nan('hue'))
Example #8
0
    def test_from_jzazbz(self):
        """Test null from Lab conversion."""

        c1 = Color('color(--jzazbz 90% 0 0)')
        c2 = c1.convert('jzczhz')
        self.assertColorEqual(c2, Color('color(--jzczhz 90% 0 0)'))
        self.assertTrue(c2.is_nan('hue'))
Example #9
0
    def test_achromatic_hue(self):
        """Test that all RGB-ish colors convert to LCHuv with a null hue."""

        for space in ('srgb', 'display-p3', 'rec2020', 'a98-rgb',
                      'prophoto-rgb'):
            for x in range(0, 256):
                color = Color('color({space} {num:f} {num:f} {num:f})'.format(
                    space=space, num=x / 255))
                color2 = color.convert('lchuv')
                self.assertTrue(color2.is_nan('hue'))
Example #10
0
def run(target, rgb, res):
    """Run."""

    max_x = float('-inf')
    max_y = float('-inf')
    max_z = float('-inf')
    min_x = float('inf')
    min_y = float('inf')
    min_z = float('inf')

    print('-> Current:', end="")
    x = y = z = 0
    color = Color(rgb, [0, 0, 0])
    while True:
        color.update(rgb, [x / res, y / res, z / res])
        print('\rCurrent: {}'.format(color.to_string(color=True)), end="")
        cx, cy, cz = color.convert(target).coords()
        if cx < min_x:
            min_x = cx
        if cy < min_y:
            min_y = cy
        if cz < min_z:
            min_z = cz
        if cx > max_x:
            max_x = cx
        if cy > max_y:
            max_y = cy
        if cz > max_z:
            max_z = cz

        if x == y == z == res:
            break
        elif y == z == res:
            x += 1
            y = z = 0
        elif z == res:
            y += 1
            z = 0
        else:
            z += 1

    print('')
    chan_x, chan_y, chan_z = Color('white').convert(
        target)._space.CHANNEL_NAMES
    print('---- {} range in {} ----'.format(target, rgb))
    print('{}: [{}, {}]'.format(chan_x, util.round_half_up(min_x, 3),
                                util.round_half_up(max_x, 3)))
    print('{}: [{}, {}]'.format(chan_y, util.round_half_up(min_y, 3),
                                util.round_half_up(max_y, 3)))
    print('{}: [{}, {}]'.format(chan_z, util.round_half_up(min_z, 3),
                                util.round_half_up(max_z, 3)))

    return 0
Example #11
0
    def setup(self, color, mode, on_done, on_cancel):
        """Setup properties for rendering."""

        self.on_done = on_done
        self.on_cancel = on_cancel
        self.template_vars = {}
        color = Color(color)
        self.setup_gamut_style()
        self.setup_image_border()
        self.setup_sizes()
        self.height_big = int(self.height + self.height / 4)
        self.setup_mode(color, mode)
        self.color = color.convert(self.mode, fit=True)
Example #12
0
    def initial_text(self):
        """Initial text."""

        if self.color is not None:
            return self.color
        elif len(self.view.sel()) == 1:
            self.setup_color_class()
            text = self.view.substr(self.view.sel()[0])
            if text:
                color = None
                try:
                    color = self.custom_color_class(text, filters=self.filters)
                except Exception:
                    pass
                if color is not None:
                    color = Color(color)
                    if color.space() not in util.SRGB_SPACES:
                        color = color.convert("srgb", fit=True)
                    return color.to_string(**util.DEFAULT)
        return ''
Example #13
0
def plot_slice(space,
               channel0,
               channel1,
               channel2,
               gamut='srgb',
               resolution=500,
               dark=False,
               title=""):
    """Plot a slice."""

    res = resolution
    if not dark:
        plt.style.use('seaborn-darkgrid')
        default_color = 'black'
    else:
        plt.style.use('dark_background')
        default_color = 'white'

    figure = plt.figure()

    # Create a color object based on the specified space.
    c = Color(space, [])

    # Parse the channel strings into actual values
    name0, value = [
        c if i == 0 else float(c)
        for i, c in enumerate(channel0.split(":"), 0)
    ]
    name1, factor1, offset1 = [
        c if i == 0 else float(c)
        for i, c in enumerate(channel1.split(':'), 0)
    ]
    name2, factor2, offset2 = [
        c if i == 0 else float(c)
        for i, c in enumerate(channel2.split(':'), 0)
    ]

    # Get the actual indexes of the specified channels
    name0 = c._space.CHANNEL_ALIASES.get(name0, name0)
    index0 = c._space.CHANNEL_NAMES.index(name0)
    name1 = c._space.CHANNEL_ALIASES.get(name1, name1)
    index1 = c._space.CHANNEL_NAMES.index(name1)
    name2 = c._space.CHANNEL_ALIASES.get(name2, name2)
    index2 = c._space.CHANNEL_NAMES.index(name2)

    # Arrays for data points to plot
    c_map = []
    xaxis = []
    yaxis = []

    # Track minimum and maximum values
    c1_mn = float('inf')
    c1_mx = float('-inf')
    c2_mn = float('inf')
    c2_mx = float('-inf')

    # Track the edge of the graphed shape.
    edge_map = {}

    # Iterate through the two specified channels
    for c1, c2 in itertools.product(
        ((x / res * factor1) + offset1 for x in range(0, res + 1)),
        ((x / res * factor2) + offset2 for x in range(0, res + 1))):
        # Set the appropriate channels and update the color object
        coords = [NaN] * 3
        coords[index0] = value
        coords[index1] = c1
        coords[index2] = c2
        c.update(space, coords)

        # Only process colors within the gamut of sRGB.
        if c.in_gamut(gamut, tolerance=0) and not needs_lchuv_workaround(c):
            # Get the absolute min and max value plotted
            if c1 < c1_mn:
                c1_mn = c1
            if c1 > c1_mx:
                c1_mx = c1

            if c2 < c2_mn:
                c2_mn = c2
            if c2 > c2_mx:
                c2_mx = c2

            # Create an edge map so we can draw an outline
            if c1 not in edge_map:
                mn = mx = c2
            else:
                mn, mx = edge_map[c1]
                if c2 < mn:
                    mn = c2
                elif c2 > mx:
                    mx = c2
            edge_map[c1] = [mn, mx]

            # Save the points
            xaxis.append(c1)
            yaxis.append(c2)
            c_map.append(c.convert('srgb').to_string(hex=True))

    # Create a border around the data
    xe = []
    ye = []
    xtemp = []
    ytemp = []
    for p1, edges in edge_map.items():
        xe.append(p1)
        ye.append(edges[0])
        xtemp.append(p1)
        ytemp.append(edges[1])
    xe.extend(reversed(xtemp))
    ye.extend(reversed(ytemp))
    xe.append(xe[0])
    ye.append(ye[0])

    ax = plt.axes(xlabel='{}: {} - {}'.format(name1, c1_mn, c1_mx),
                  ylabel='{}: {} - {}'.format(name2, c2_mn, c2_mx))
    ax.set_aspect('auto')
    figure.add_axes(ax)

    if not title:
        title = "A Slice of '{}' and '{}' in the {} Color Space".format(
            name1, name2, space)
    title += '\n{}: {}'.format(name0, value)
    plt.title(title)

    plt.plot(xe,
             ye,
             color=default_color,
             marker="",
             linewidth=2,
             markersize=0,
             antialiased=True)

    plt.scatter(xaxis, yaxis, marker="o", color=c_map, s=2)
Example #14
0
    def get_color_map_square(self):
        """Get a square variant of the color map."""

        global color_map
        global color_map_size
        global line_height
        global default_border
        global color_scale
        global last_saturation

        s = self.color.convert("hsl").saturation

        # Only update if the last time we rendered we changed
        # something that would require a new render.
        if (color_map is None or s != last_saturation
                or self.graphic_size != color_map_size
                or self.graphic_scale != color_scale
                or self.line_height != line_height
                or self.default_border != default_border):
            color_map_size = self.graphic_size
            color_scale = self.graphic_scale

            line_height = self.line_height
            default_border = self.default_border

            html_colors = []

            # Generate the colors with each row being dark than the last.
            # Each column will progress through hues.
            color = Color("hsl(0 {}% 90%)".format(s), filters=util.SRGB_SPACES)
            hfac = 24.0
            lfac = 8.0
            check_size = self.check_size(self.height)
            for y in range(0, 11):
                html_colors.append([self.get_spacer(width=5)])
                for x in range(0, 15):
                    value = color.convert("srgb").to_string(**HEX)
                    kwargs = {
                        "border_size": BORDER_SIZE,
                        "height": self.height,
                        "width": self.width,
                        "check_size": check_size
                    }

                    if y == 0 and x == 0:
                        border_map = colorbox.TOP | colorbox.LEFT
                    elif y == 0 and x == 14:
                        border_map = colorbox.TOP | colorbox.RIGHT
                    elif y == 0:
                        border_map = colorbox.TOP
                    elif y == 10 and x == 0:
                        border_map = colorbox.BOTTOM | colorbox.LEFT
                    elif y == 10 and x == 14:
                        border_map = colorbox.BOTTOM | colorbox.RIGHT
                    elif y == 10:
                        border_map = colorbox.BOTTOM
                    elif x == 0:
                        border_map = colorbox.LEFT
                    elif x == 14:
                        border_map = colorbox.RIGHT
                    else:
                        border_map = 0
                    kwargs["border_map"] = border_map

                    html_colors[-1].append('<a href="{}">{}</a>'.format(
                        color.to_string(**COLOR_FULL_PREC),
                        mdpopups.color_box([value], self.default_border,
                                           **kwargs)))
                    color.hue = color.hue + hfac
                color.hue = 0.0
                color.lightness = color.lightness - lfac

            # Generate a grayscale bar.
            lfac = 10.0
            color = Color('hsl(0 0% 100%)', filters=util.SRGB_SPACES)
            check_size = self.check_size(self.height)
            for y in range(0, 11):
                value = color.convert("srgb").to_string(**HEX)
                kwargs = {
                    "border_size": BORDER_SIZE,
                    "height": self.height,
                    "width": self.width,
                    "check_size": check_size
                }

                if y == 0:
                    border_map = 0xb
                elif y == 10:
                    border_map = 0xe
                else:
                    border_map = 0xa
                kwargs["border_map"] = border_map

                html_colors[y].append('<a href="{}">{}</a>'.format(
                    color.to_string(**COLOR_FULL_PREC),
                    mdpopups.color_box([value], self.default_border,
                                       **kwargs)))
                color.lightness = color.lightness - lfac

            color_map = (''.join([
                '<span>{}</span><br>'.format(''.join([y1 for y1 in x1]))
                for x1 in html_colors
            ]) + '\n\n')
        self.template_vars['color_picker'] = color_map
Example #15
0
    def do_search(self, force=False):
        """
        Perform the search for the highlighted word.

        TODO: This function is a big boy. We should look into breaking it up.
              With that said, this is low priority.
        """

        # Since the plugin has been reloaded, force update.
        global reload_flag
        settings = self.view.settings()
        colors = []

        # Allow per view scan override
        option = settings.get("color_helper.scan_override", None)
        if option in ("Force enable", "Force disable"):
            override = option == "Force enable"
        else:
            override = None

        # Get the rules and use them to get the needed scopes.
        # The scopes will be used to get the searchable regions.
        rules = util.get_rules(self.view)
        # Bail if this if this view has no valid rule or scanning is disabled.
        if (rules is None or not rules.get("enabled", False)
                or (not rules.get("allow_scanning", True) and not override)
                or override is False):
            self.erase_phantoms()
            return

        if reload_flag:
            reload_flag = False
            force = True

        # Calculate size of preview boxes
        box_height = self.calculate_box_size()
        check_size = int((box_height - 2) / 4)
        if check_size < 2:
            check_size = 2

        # If desired preview boxes are different than current,
        # we need to reload the boxes.
        old_box_height = int(settings.get('color_helper.box_height', 0))
        current_color_scheme = settings.get('color_scheme')
        if (force or old_box_height != box_height
                or current_color_scheme != settings.get(
                    'color_helper.color_scheme', '')
                or settings.get('color_helper.refresh')):
            self.erase_phantoms()
            settings.set('color_helper.color_scheme', current_color_scheme)
            settings.set('color_helper.box_height', box_height)
            force = True

        # If we don't need to force previews,
        # quit if visible region is the same as last time
        visible_region = self.view.visible_region()
        if not force and self.previous_region == visible_region:
            return
        self.previous_region = visible_region

        # Setup "preview on select"
        preview_on_select = ch_settings.get("preview_on_select", False)
        show_preview = True
        sel = None
        if preview_on_select and len(self.view.sel()) != 1:
            show_preview = False
        elif preview_on_select:
            sel = self.view.sel()[0]

        # Get the scan scopes
        scanning = rules.get("scanning")
        classes = rules.get("color_class", "css-level-4")
        if show_preview and visible_region.size() and scanning and classes:
            # Get out of gamut related options
            self.setup_gamut_options()

            # Get triggers that identify where colors are likely
            color_trigger = re.compile(
                rules.get("color_trigger", util.RE_COLOR_START))

            # Find source content in the visible region.
            # We will return consecutive content, but if the lines are too wide
            # horizontally, they will be clipped and returned as separate chunks.
            for src_region in self.source_iter(visible_region):
                source = self.view.substr(src_region)
                start = 0

                # Find colors in this source chunk.
                for m in color_trigger.finditer(source):
                    # Test if we have found a valid color
                    start = m.start()
                    src_start = src_region.begin() + start

                    # Check if the first point within the color matches our scope rules
                    # and load up the appropriate color class
                    color_class, filters = self.get_color_class(
                        src_start, classes)
                    if color_class is None:
                        continue

                    # Check if scope matches for scanning
                    try:
                        value = self.view.score_selector(src_start, scanning)
                        if not value:
                            continue
                    except Exception:
                        continue

                    obj = color_class.match(source,
                                            start=start,
                                            filters=filters)
                    if obj is not None:
                        # Calculate true start and end of the color source
                        src_end = src_region.begin() + obj.end
                        region = sublime.Region(src_start, src_end)

                        # If "preview on select" is enabled, only show preview if within a selection
                        # or if the selection as no width and the color comes right after.
                        if (preview_on_select
                                and not (sel.empty()
                                         and sel.begin() == region.begin())
                                and not region.intersects(sel)):
                            continue
                    else:
                        continue

                    # Calculate point at which we which to insert preview
                    position_on_left = preview_is_on_left()
                    pt = src_start if position_on_left else src_end
                    if str(region.begin()) in self.previews:
                        # Already exists
                        continue

                    # Calculate a reasonable border color for our image at this location and get color strings
                    hsl = Color(mdpopups.scope2style(
                        self.view, self.view.scope_name(pt))['background'],
                                filters=util.SRGB_SPACES).convert("hsl")
                    hsl.lightness = hsl.lightness + (
                        30 if hsl.luminance() < 0.5 else -30)
                    preview_border = hsl.convert(
                        "srgb", fit=True).to_string(**util.HEX)

                    color = Color(obj.color)
                    title = ''
                    if not color.in_gamut("srgb"):
                        title = ' title="Out of gamut"'
                        if self.show_out_of_gamut_preview:
                            srgb = color.convert("srgb", fit=True)
                            preview1 = srgb.to_string(**util.HEX_NA)
                            preview2 = srgb.to_string(**util.HEX)
                        else:
                            preview1 = self.out_of_gamut
                            preview2 = self.out_of_gamut
                            preview_border = self.out_of_gamut_border
                    else:
                        srgb = color.convert("srgb")
                        preview1 = srgb.to_string(**util.HEX_NA)
                        preview2 = srgb.to_string(**util.HEX)

                    # Create preview
                    unique_id = str(time()) + str(region)
                    html = PREVIEW_IMG.format(
                        unique_id, title,
                        mdpopups.color_box([preview1, preview2],
                                           preview_border,
                                           height=box_height,
                                           width=box_height,
                                           border_size=PREVIEW_BORDER_SIZE,
                                           check_size=check_size))
                    colors.append(
                        (html, pt, region.begin(), region.end(), unique_id))

            # Add all previews
            self.add_phantoms(colors)

            # The phantoms may have altered the viewable region,
            # so set previous region to the current viewable region
            self.previous_region = sublime.Region(
                self.previous_region.begin(),
                self.view.visible_region().end())