Пример #1
0
def test_rgba(calc):
    # four args (css-style)
    assert calc('rgba(128, 192, 224, 0.5)') == ColorValue.from_rgb(128/255, 192/255, 224/255, 0.5)
    assert calc('rgba(20%, 40%, 60%, 0.7)') == ColorValue.from_rgb(0.2, 0.4, 0.6, 0.7)

    # two args (modify alpha of existing color)
    assert calc('rgba(red, 0.4)') == ColorValue.from_rgb(1., 0., 0., 0.4)
Пример #2
0
def _color_type(color, a, type):
    color = ColorValue(color).value
    a = NumberValue(a).value if a is not None else color[3]
    col = list(color[:3])
    col += [0.0 if a < 0 else 1.0 if a > 1 else a]
    col += [type]
    return ColorValue(col)
Пример #3
0
def __hsl_op(op, color, h, s, l):
    color = ColorValue(color)
    c = color.value
    h = None if h is None else NumberValue(h)
    s = None if s is None else NumberValue(s)
    l = None if l is None else NumberValue(l)
    a = [
        None if h is None else h.value / 360.0,
        None if s is None else _apply_percentage(s),
        None if l is None else _apply_percentage(l),
    ]
    # Convert to HSL:
    h, l, s = list(
        colorsys.rgb_to_hls(c[0] / 255.0, c[1] / 255.0, c[2] / 255.0))
    c = h, s, l
    # Do the additions:
    c = [
        0.0
        if c[i] < 0 else 1.0 if c[i] > 1 else op(c[i], a[i]) if op is not None
        and a[i] is not None else a[i] if a[i] is not None else c[i]
        for i in range(3)
    ]
    # Validations:
    c[0] = (c[0] * 360.0) % 360
    r = 360.0, 1.0, 1.0
    c = [0.0 if c[i] < 0 else r[i] if c[i] > r[i] else c[i] for i in range(3)]
    # Convert back to RGB:
    c = colorsys.hls_to_rgb(c[0] / 360.0, 0.99999999 if c[2] == 1 else c[2],
                            0.99999999 if c[1] == 1 else c[1])
    color.value = (c[0] * 255.0, c[1] * 255.0, c[2] * 255.0, color.value[3])
    return color
Пример #4
0
def test_hsl(calc):
    # Examples from the CSS 3 color spec, which Sass uses
    # (http://www.w3.org/TR/css3-color/#hsl-color)
    assert calc('hsl(0, 100%, 50%)') == ColorValue.from_rgb(1., 0., 0.)
    assert calc('hsl(120, 100%, 50%)') == ColorValue.from_rgb(0., 1., 0.)
    assert calc('hsl(120, 100%, 25%)') == ColorValue.from_rgb(0., 0.5, 0.)
    assert calc('hsl(120, 100%, 75%)') == ColorValue.from_rgb(0.5, 1., 0.5)
    assert calc('hsl(120, 75%, 75%)') == ColorValue.from_rgb(0.5625, 0.9375, 0.5625)
Пример #5
0
def test_rgba(calc):
    # four args (css-style)
    assert calc('rgba(128, 192, 224, 0.5)') == ColorValue.from_rgb(
        128 / 255, 192 / 255, 224 / 255, 0.5)
    assert calc('rgba(20%, 40%, 60%, 0.7)') == ColorValue.from_rgb(
        0.2, 0.4, 0.6, 0.7)

    # two args (modify alpha of existing color)
    assert calc('rgba(red, 0.4)') == ColorValue.from_rgb(1., 0., 0., 0.4)
Пример #6
0
def test_hsl(calc):
    # Examples from the CSS 3 color spec, which Sass uses
    # (http://www.w3.org/TR/css3-color/#hsl-color)
    assert calc('hsl(0, 100%, 50%)') == ColorValue.from_rgb(1., 0., 0.)
    assert calc('hsl(120, 100%, 50%)') == ColorValue.from_rgb(0., 1., 0.)
    assert calc('hsl(120, 100%, 25%)') == ColorValue.from_rgb(0., 0.5, 0.)
    assert calc('hsl(120, 100%, 75%)') == ColorValue.from_rgb(0.5, 1., 0.5)
    assert calc('hsl(120, 75%, 75%)') == ColorValue.from_rgb(
        0.5625, 0.9375, 0.5625)
Пример #7
0
def test_hsla(calc):
    # Examples from the CSS 3 color spec
    assert calc('hsla(120, 100%, 50%, 1)') == ColorValue.from_rgb(
        0.,
        1.,
        0.,
    )
    assert calc('hsla(240, 100%, 50%, 0.5)') == ColorValue.from_rgb(
        0., 0., 1., 0.5)
    assert calc('hsla(30, 100%, 50%, 0.1)') == ColorValue.from_rgb(
        1., 0.5, 0., 0.1)
Пример #8
0
def invert(color):
    """
    Returns the inverse (negative) of a color.
    The red, green, and blue values are inverted, while the opacity is left alone.
    """
    col = ColorValue(color)
    c = list(col.value)
    c[0] = 255.0 - c[0]
    c[1] = 255.0 - c[1]
    c[2] = 255.0 - c[2]
    col.value = tuple(c)
    return col
Пример #9
0
def invert(color):
    """
    Returns the inverse (negative) of a color.
    The red, green, and blue values are inverted, while the opacity is left alone.
    """
    col = ColorValue(color)
    c = list(col.value)
    c[0] = 255.0 - c[0]
    c[1] = 255.0 - c[1]
    c[2] = 255.0 - c[2]
    col.value = tuple(c)
    return col
Пример #10
0
def parse_bareword(word):
    if word in _colors:
        # TODO tidy this up once constructors are more reliable
        ret = ColorValue(ParserValue(_colors[word]))
        ret.tokens = ParserValue(word)
        return ret
    elif word in ("null", "undefined"):
        return Null()
    elif word == "true":
        return BooleanValue(True)
    elif word == "false":
        return BooleanValue(False)
    else:
        return String(word, quotes=None)
Пример #11
0
def background_brushed(density=None, intensity=None, color=None, opacity=None, size=None, monochrome=False, direction=None, spread=None, background=None, inline=False):
    if not Image:
        raise Exception("Images manipulation require PIL")

    density = [NumberValue(v).value for v in List.from_maybe(density)]
    intensity = [NumberValue(v).value for v in List.from_maybe(intensity)]
    color = [ColorValue(v).value for v in List.from_maybe(color) if v]
    opacity = [NumberValue(v).value for v in List.from_maybe(opacity)]

    size = int(NumberValue(size).value) if size else -1
    if size < 0 or size > 512:
        size = 200

    monochrome = bool(monochrome)

    direction = [NumberValue(v).value for v in List.from_maybe(direction)]
    spread = [NumberValue(v).value for v in List.from_maybe(spread)]

    background = ColorValue(background).value if background else None

    new_image = Image.new(
        mode='RGBA',
        size=(size, size)
    )

    pixdata = new_image.load()
    _image_brushed(pixdata, size, density, intensity, color, opacity, monochrome, direction, spread, background)

    if not inline:
        key = (size, density, intensity, color, opacity, monochrome, direction, spread, background)
        asset_file = 'brushed-%s%sx%s' % ('mono-' if monochrome else '', size, size)
        # asset_file += '-[%s][%s][%s]' % ('-'.join(to_str(s).replace('.', '_') for s in density or []), '-'.join(to_str(s).replace('.', '_') for s in opacity or []), '-'.join(to_str(s).replace('.', '_') for s in direction or []))
        asset_file += '-' + base64.urlsafe_b64encode(hashlib.md5(repr(key)).digest()).rstrip('=').replace('-', '_')
        asset_file += '.png'
        asset_path = os.path.join(config.ASSETS_ROOT or os.path.join(config.STATIC_ROOT, 'assets'), asset_file)
        try:
            new_image.save(asset_path)
        except IOError:
            log.exception("Error while saving image")
            inline = True  # Retry inline version
        url = '%s%s' % (config.ASSETS_URL, asset_file)
    if inline:
        output = six.BytesIO()
        new_image.save(output, format='PNG')
        contents = output.getvalue()
        output.close()
        url = 'data:image/png;base64,' + base64.b64encode(contents)

    inline = 'url("%s")' % escape(url)
    return StringValue(inline)
Пример #12
0
def __rgba_op(op, color, r, g, b, a):
    color = ColorValue(color)
    c = color.value
    a = [
        None if r is None else NumberValue(r).value,
        None if g is None else NumberValue(g).value,
        None if b is None else NumberValue(b).value,
        None if a is None else NumberValue(a).value,
    ]
    # Do the additions:
    c = [op(c[i], a[i]) if op is not None and a[i] is not None else a[i] if a[i] is not None else c[i] for i in range(4)]
    # Validations:
    r = 255.0, 255.0, 255.0, 1.0
    c = [0.0 if c[i] < 0 else r[i] if c[i] > r[i] else c[i] for i in range(4)]
    color.value = tuple(c)
    return color
Пример #13
0
def rgba(r, g, b, a, type='rgba'):
    channels = []
    for ch in (r, g, b):
        channels.append(_constrain(_apply_percentage(ch, relto=255), 0, 255))

    channels.append(_constrain(_apply_percentage(a), 0, 1))
    channels.append(type)
    return ColorValue(channels)
Пример #14
0
def test_adjust_hue(calc):
    # Examples from the Ruby docs
    assert calc('adjust-hue(hsl(120, 30%, 90%), 60deg)') == calc(
        'hsl(180, 30%, 90%)')
    assert calc('adjust-hue(hsl(120, 30%, 90%), -60deg)') == calc(
        'hsl(60, 30%, 90%)')
    assert calc('adjust-hue(#811, 45deg)') == ColorValue.from_rgb(
        136 / 255, 106.25 / 255, 17 / 255)
Пример #15
0
def __rgba_op(op, color, r, g, b, a):
    color = ColorValue(color)
    c = color.value
    a = [
        None if r is None else NumberValue(r).value,
        None if g is None else NumberValue(g).value,
        None if b is None else NumberValue(b).value,
        None if a is None else NumberValue(a).value,
    ]
    # Do the additions:
    c = [
        op(c[i], a[i]) if op is not None and a[i] is not None else
        a[i] if a[i] is not None else c[i] for i in range(4)
    ]
    # Validations:
    r = 255.0, 255.0, 255.0, 1.0
    c = [0.0 if c[i] < 0 else r[i] if c[i] > r[i] else c[i] for i in range(4)]
    color.value = tuple(c)
    return color
Пример #16
0
def parse_bareword(word):
    if word in COLOR_NAMES:
        return ColorValue.from_name(word)
    elif word in ('null', 'undefined'):
        return Null()
    elif word == 'true':
        return BooleanValue(True)
    elif word == 'false':
        return BooleanValue(False)
    else:
        return String(word, quotes=None)
Пример #17
0
def parse_bareword(word):
    if word in COLOR_NAMES:
        return ColorValue.from_name(word)
    elif word == 'null':
        return Null()
    elif word == 'undefined':
        return Undefined()
    elif word == 'true':
        return BooleanValue(True)
    elif word == 'false':
        return BooleanValue(False)
    else:
        return String(word, quotes=None)
Пример #18
0
def hsla(h, s, l, a, type='hsla'):
    rgb = colorsys.hls_to_rgb(
        _apply_percentage(h, relto=360) % 360 / 360,
        # Ruby sass treats plain numbers for saturation and lightness as though
        # they were percentages, just without the %
        _constrain(_apply_percentage(l, relto=100), 0, 100) / 100,
        _constrain(_apply_percentage(s, relto=100), 0, 100) / 100,
    )

    channels = list(ch * 255 for ch in rgb)
    channels.append(_constrain(_apply_percentage(a), 0, 1))
    channels.append(type)
    return ColorValue(channels)
Пример #19
0
def __hsl_op(op, color, h, s, l):
    color = ColorValue(color)
    c = color.value
    h = None if h is None else NumberValue(h)
    s = None if s is None else NumberValue(s)
    l = None if l is None else NumberValue(l)
    a = [
        None if h is None else h.value / 360.0,
        None if s is None else _apply_percentage(s),
        None if l is None else _apply_percentage(l),
    ]
    # Convert to HSL:
    h, l, s = list(colorsys.rgb_to_hls(c[0] / 255.0, c[1] / 255.0, c[2] / 255.0))
    c = h, s, l
    # Do the additions:
    c = [0.0 if c[i] < 0 else 1.0 if c[i] > 1 else op(c[i], a[i]) if op is not None and a[i] is not None else a[i] if a[i] is not None else c[i] for i in range(3)]
    # Validations:
    c[0] = (c[0] * 360.0) % 360
    r = 360.0, 1.0, 1.0
    c = [0.0 if c[i] < 0 else r[i] if c[i] > r[i] else c[i] for i in range(3)]
    # Convert back to RGB:
    c = colorsys.hls_to_rgb(c[0] / 360.0, 0.99999999 if c[2] == 1 else c[2], 0.99999999 if c[1] == 1 else c[1])
    color.value = (c[0] * 255.0, c[1] * 255.0, c[2] * 255.0, color.value[3])
    return color
Пример #20
0
def image_color(color, width=1, height=1):
    if not Image:
        raise Exception("Images manipulation require PIL")
    c = ColorValue(color).value
    w = int(NumberValue(width).value)
    h = int(NumberValue(height).value)
    if w <= 0 or h <= 0:
        raise ValueError
    new_image = Image.new(
        mode='RGB' if c[3] == 1 else 'RGBA',
        size=(w, h),
        color=(c[0], c[1], c[2], int(c[3] * 255.0))
    )
    output = six.BytesIO()
    new_image.save(output, format='PNG')
    contents = output.getvalue()
    output.close()
    mime_type = 'image/png'
    url = 'data:' + mime_type + ';base64,' + base64.b64encode(contents)
    inline = 'url("%s")' % escape(url)
    return StringValue(inline)
Пример #21
0
 def atom(self):
     _token_ = self._peek(self.u_expr_chks)
     if _token_ == 'LPAR':
         LPAR = self._scan('LPAR')
         expr_lst = self.expr_lst()
         RPAR = self._scan('RPAR')
         return Parentheses(expr_lst)
     elif _token_ == 'ID':
         ID = self._scan('ID')
         return Literal(parse_bareword(ID))
     elif _token_ == 'BANG_IMPORTANT':
         BANG_IMPORTANT = self._scan('BANG_IMPORTANT')
         return Literal(String(BANG_IMPORTANT, quotes=None))
     elif _token_ == 'FNCT':
         FNCT = self._scan('FNCT')
         v = ArgspecLiteral([])
         LPAR = self._scan('LPAR')
         if self._peek(self.atom_rsts) != 'RPAR':
             argspec = self.argspec()
             v = argspec
         RPAR = self._scan('RPAR')
         return CallOp(FNCT, v)
     elif _token_ == 'NUM':
         NUM = self._scan('NUM')
         if self._peek(self.atom_rsts_) == 'UNITS':
             UNITS = self._scan('UNITS')
             return Literal(NumberValue(float(NUM), unit=UNITS.lower()))
         return Literal(NumberValue(float(NUM)))
     elif _token_ == 'STR':
         STR = self._scan('STR')
         return Literal(String(STR[1:-1], quotes="'"))
     elif _token_ == 'QSTR':
         QSTR = self._scan('QSTR')
         return Literal(String(QSTR[1:-1], quotes='"'))
     elif _token_ == 'COLOR':
         COLOR = self._scan('COLOR')
         return Literal(ColorValue(ParserValue(COLOR)))
     else:  # == 'VAR'
         VAR = self._scan('VAR')
         return Variable(VAR)
Пример #22
0
def test_complement(calc):
    assert calc('complement(black)') == ColorValue.from_rgb(0., 0., 0.)
    assert calc('complement(white)') == ColorValue.from_rgb(1., 1., 1.)
    assert calc('complement(yellow)') == ColorValue.from_rgb(0., 0., 1.)
Пример #23
0
def test_grayscale(calc):
    assert calc('grayscale(black)') == ColorValue.from_rgb(0., 0., 0.)
    assert calc('grayscale(white)') == ColorValue.from_rgb(1., 1., 1.)
    assert calc('grayscale(yellow)') == ColorValue.from_rgb(0.5, 0.5, 0.5)
Пример #24
0
def test_desaturate(calc):
    # Examples from the Ruby docs
    assert calc('desaturate(hsl(120, 30%, 90%), 20%)') == calc('hsl(120, 10%, 90%)')
    assert calc('desaturate(#855, 20%)') == ColorValue.from_rgb(113.9/255, 107.1/255, 107.1/255)
Пример #25
0
def test_saturate(calc):
    # Examples from the Ruby docs
    assert calc('saturate(hsl(120, 30%, 90%), 20%)') == calc('hsl(120, 50%, 90%)')
    assert calc('saturate(#855, 20%)') == ColorValue.from_rgb(158.1/255, 62.9/255, 62.9/255)
Пример #26
0
def test_hsla(calc):
    # Examples from the CSS 3 color spec
    assert calc('hsla(120, 100%, 50%, 1)') == ColorValue.from_rgb(0., 1., 0.,)
    assert calc('hsla(240, 100%, 50%, 0.5)') == ColorValue.from_rgb(0., 0., 1., 0.5)
    assert calc('hsla(30, 100%, 50%, 0.1)') == ColorValue.from_rgb(1., 0.5, 0., 0.1)
Пример #27
0
def hue(color):
    c = ColorValue(color).value
    h, l, s = colorsys.rgb_to_hls(c[0] / 255.0, c[1] / 255.0, c[2] / 255.0)
    return NumberValue(h * 360, unit='deg')
Пример #28
0
def test_invert(calc):
    assert calc('invert(black)') == ColorValue.from_rgb(1., 1., 1.)
    assert calc('invert(white)') == ColorValue.from_rgb(0., 0., 0.)
    assert calc('invert(yellow)') == ColorValue.from_rgb(0., 0., 1.)
Пример #29
0
def test_complement(calc):
    assert calc('complement(black)') == ColorValue.from_rgb(0., 0., 0.)
    assert calc('complement(white)') == ColorValue.from_rgb(1., 1., 1.)
    assert calc('complement(yellow)') == ColorValue.from_rgb(0., 0., 1.)
Пример #30
0
def test_grayscale(calc):
    assert calc('grayscale(black)') == ColorValue.from_rgb(0., 0., 0.)
    assert calc('grayscale(white)') == ColorValue.from_rgb(1., 1., 1.)
    assert calc('grayscale(yellow)') == ColorValue.from_rgb(0.5, 0.5, 0.5)
Пример #31
0
def blue(color):
    c = ColorValue(color).value
    return NumberValue(c[2])
Пример #32
0
def _grid_image(left_gutter, width, right_gutter, height, columns=1, grid_color=None, baseline_color=None, background_color=None, inline=False):
    if not Image:
        raise Exception("Images manipulation require PIL")
    if grid_color is None:
        grid_color = (120, 170, 250, 15)
    else:
        c = ColorValue(grid_color).value
        grid_color = (c[0], c[1], c[2], int(c[3] * 255.0))
    if baseline_color is None:
        baseline_color = (120, 170, 250, 30)
    else:
        c = ColorValue(baseline_color).value
        baseline_color = (c[0], c[1], c[2], int(c[3] * 255.0))
    if background_color is None:
        background_color = (0, 0, 0, 0)
    else:
        c = ColorValue(background_color).value
        background_color = (c[0], c[1], c[2], int(c[3] * 255.0))
    _height = int(height) if height >= 1 else int(height * 1000.0)
    _width = int(width) if width >= 1 else int(width * 1000.0)
    _left_gutter = int(left_gutter) if left_gutter >= 1 else int(left_gutter * 1000.0)
    _right_gutter = int(right_gutter) if right_gutter >= 1 else int(right_gutter * 1000.0)
    if _height <= 0 or _width <= 0 or _left_gutter <= 0 or _right_gutter <= 0:
        raise ValueError
    _full_width = (_left_gutter + _width + _right_gutter)
    new_image = Image.new(
        mode='RGBA',
        size=(_full_width * int(columns), _height),
        color=background_color
    )
    draw = ImageDraw.Draw(new_image)
    for i in range(int(columns)):
        draw.rectangle((i * _full_width + _left_gutter, 0, i * _full_width + _left_gutter + _width - 1, _height - 1),  fill=grid_color)
    if _height > 1:
        draw.rectangle((0, _height - 1, _full_width * int(columns) - 1, _height - 1),  fill=baseline_color)
    if not inline:
        grid_name = 'grid_'
        if left_gutter:
            grid_name += str(int(left_gutter)) + '+'
        grid_name += str(int(width))
        if right_gutter:
            grid_name += '+' + str(int(right_gutter))
        if height and height > 1:
            grid_name += 'x' + str(int(height))
        key = (columns, grid_color, baseline_color, background_color)
        key = grid_name + '-' + base64.urlsafe_b64encode(hashlib.md5(repr(key)).digest()).rstrip('=').replace('-', '_')
        asset_file = key + '.png'
        asset_path = os.path.join(config.ASSETS_ROOT or os.path.join(config.STATIC_ROOT, 'assets'), asset_file)
        try:
            new_image.save(asset_path)
        except IOError:
            log.exception("Error while saving image")
            inline = True  # Retry inline version
        url = '%s%s' % (config.ASSETS_URL, asset_file)
    if inline:
        output = six.BytesIO()
        new_image.save(output, format='PNG')
        contents = output.getvalue()
        output.close()
        url = 'data:image/png;base64,' + base64.b64encode(contents)
    inline = 'url("%s")' % escape(url)
    return StringValue(inline)
Пример #33
0
def test_invert(calc):
    assert calc('invert(black)') == ColorValue.from_rgb(1., 1., 1.)
    assert calc('invert(white)') == ColorValue.from_rgb(0., 0., 0.)
    assert calc('invert(yellow)') == ColorValue.from_rgb(0., 0., 1.)
Пример #34
0
def test_rgb(calc):
    assert calc('rgb(128, 192, 224)') == ColorValue.from_rgb(
        128 / 255, 192 / 255, 224 / 255)
    assert calc('rgb(20%, 40%, 60%)') == ColorValue.from_rgb(0.2, 0.4, 0.6)
Пример #35
0
def test_rgb(calc):
    assert calc('rgb(128, 192, 224)') == ColorValue.from_rgb(128/255, 192/255, 224/255)
    assert calc('rgb(20%, 40%, 60%)') == ColorValue.from_rgb(0.2, 0.4, 0.6)
Пример #36
0
def ie_hex_str(color):
    c = ColorValue(color).value
    return String(u'#%02X%02X%02X%02X' %
                  (round(c[3] * 255), round(c[0]), round(c[1]), round(c[2])))
Пример #37
0
def alpha(color):
    c = ColorValue(color).value
    return NumberValue(c[3])
Пример #38
0
def lightness(color):
    c = ColorValue(color).value
    h, l, s = colorsys.rgb_to_hls(c[0] / 255.0, c[1] / 255.0, c[2] / 255.0)
    return NumberValue(l * 100, unit='%')
Пример #39
0
def test_adjust_hue(calc):
    # Examples from the Ruby docs
    assert calc('adjust-hue(hsl(120, 30%, 90%), 60deg)') == calc('hsl(180, 30%, 90%)')
    assert calc('adjust-hue(hsl(120, 30%, 90%), -60deg)') == calc('hsl(60, 30%, 90%)')
    assert calc('adjust-hue(#811, 45deg)') == ColorValue.from_rgb(136/255, 106.25/255, 17/255)
Пример #40
0
def saturation(color):
    c = ColorValue(color).value
    h, l, s = colorsys.rgb_to_hls(c[0] / 255.0, c[1] / 255.0, c[2] / 255.0)
    return NumberValue(s * 100, unit='%')
Пример #41
0
def mix(color1, color2, weight=None):
    """
    Mixes together two colors. Specifically, takes the average of each of the
    RGB components, optionally weighted by the given percentage.
    The opacity of the colors is also considered when weighting the components.

    Specifically, takes the average of each of the RGB components,
    optionally weighted by the given percentage.
    The opacity of the colors is also considered when weighting the components.

    The weight specifies the amount of the first color that should be included
    in the returned color.
    50%, means that half the first color
        and half the second color should be used.
    25% means that a quarter of the first color
        and three quarters of the second color should be used.

    For example:

        mix(#f00, #00f) => #7f007f
        mix(#f00, #00f, 25%) => #3f00bf
        mix(rgba(255, 0, 0, 0.5), #00f) => rgba(63, 0, 191, 0.75)

    """
    # This algorithm factors in both the user-provided weight
    # and the difference between the alpha values of the two colors
    # to decide how to perform the weighted average of the two RGB values.
    #
    # It works by first normalizing both parameters to be within [-1, 1],
    # where 1 indicates "only use color1", -1 indicates "only use color 0",
    # and all values in between indicated a proportionately weighted average.
    #
    # Once we have the normalized variables w and a,
    # we apply the formula (w + a)/(1 + w*a)
    # to get the combined weight (in [-1, 1]) of color1.
    # This formula has two especially nice properties:
    #
    #   * When either w or a are -1 or 1, the combined weight is also that number
    #     (cases where w * a == -1 are undefined, and handled as a special case).
    #
    #   * When a is 0, the combined weight is w, and vice versa
    #
    # Finally, the weight of color1 is renormalized to be within [0, 1]
    # and the weight of color2 is given by 1 minus the weight of color1.
    #
    # Algorithm from the Sass project: http://sass-lang.com/

    c1 = ColorValue(color1).value
    c2 = ColorValue(color2).value
    p = NumberValue(weight).value if weight is not None else 0.5
    p = 0.0 if p < 0 else 1.0 if p > 1 else p

    w = p * 2 - 1
    a = c1[3] - c2[3]

    w1 = ((w if (w * a == -1) else (w + a) / (1 + w * a)) + 1) / 2.0

    w2 = 1 - w1
    q = [w1, w1, w1, p]
    r = [w2, w2, w2, 1 - p]

    color = ColorValue(None).merge(c1).merge(c2)
    color.value = [c1[i] * q[i] + c2[i] * r[i] for i in range(4)]

    return color
Пример #42
0
def _image_url(path,
               only_path=False,
               cache_buster=True,
               dst_color=None,
               src_color=None,
               inline=False,
               mime_type=None,
               spacing=None,
               collapse_x=None,
               collapse_y=None):
    """
    src_color - a list of or a single color to be replaced by each corresponding dst_color colors
    spacing - spaces to be added to the image
    collapse_x, collapse_y - collapsable (layered) image of the given size (x, y)
    """
    if inline or dst_color or spacing:
        if not Image:
            raise Exception("Images manipulation require PIL")
    filepath = StringValue(path).value
    mime_type = inline and (StringValue(mime_type).value
                            or mimetypes.guess_type(filepath)[0])
    path = None
    if callable(config.STATIC_ROOT):
        try:
            _file, _storage = list(config.STATIC_ROOT(filepath))[0]
            d_obj = _storage.modified_time(_file)
            filetime = int(time.mktime(d_obj.timetuple()))
            if inline or dst_color or spacing:
                path = _storage.open(_file)
        except:
            filetime = 'NA'
    else:
        _path = os.path.join(config.STATIC_ROOT, filepath.strip('/'))
        if os.path.exists(_path):
            filetime = int(os.path.getmtime(_path))
            if inline or dst_color or spacing:
                path = open(_path, 'rb')
        else:
            filetime = 'NA'
    BASE_URL = config.STATIC_URL
    if path:
        dst_colors = [
            list(ColorValue(v).value[:3]) for v in List.from_maybe(dst_color)
            if v
        ]

        src_colors = src_color
        src_colors = [
            tuple(ColorValue(v).value[:3]) if v else (0, 0, 0)
            for v in List.from_maybe(src_color)
        ]

        len_colors = max(len(dst_colors), len(src_colors))
        dst_colors = (dst_colors * len_colors)[:len_colors]
        src_colors = (src_colors * len_colors)[:len_colors]

        spacing = [
            int(NumberValue(v).value) if v else 0
            for v in List.from_maybe(spacing)
        ]
        spacing = (spacing * 4)[:4]

        file_name, file_ext = os.path.splitext(
            os.path.normpath(filepath).replace('\\', '_').replace('/', '_'))
        key = (filetime, src_color, dst_color, spacing)
        key = file_name + '-' + base64.urlsafe_b64encode(
            hashlib.md5(repr(key)).digest()).rstrip('=').replace('-', '_')
        asset_file = key + file_ext
        ASSETS_ROOT = config.ASSETS_ROOT or os.path.join(
            config.STATIC_ROOT, 'assets')
        asset_path = os.path.join(ASSETS_ROOT, asset_file)

        if os.path.exists(asset_path):
            filepath = asset_file
            BASE_URL = config.ASSETS_URL
            if inline:
                path = open(asset_path, 'rb')
                url = 'data:' + mime_type + ';base64,' + base64.b64encode(
                    path.read())
            else:
                url = '%s%s' % (BASE_URL, filepath)
                if cache_buster:
                    filetime = int(os.path.getmtime(asset_path))
                    url = add_cache_buster(url, filetime)
        else:
            image = Image.open(path)
            width, height = collapse_x or image.size[
                0], collapse_y or image.size[1]
            new_image = Image.new(mode='RGBA',
                                  size=(width + spacing[1] + spacing[3],
                                        height + spacing[0] + spacing[2]),
                                  color=(0, 0, 0, 0))
            for i, dst_color in enumerate(dst_colors):
                src_color = src_colors[i]
                pixdata = image.load()
                for _y in xrange(image.size[1]):
                    for _x in xrange(image.size[0]):
                        pixel = pixdata[_x, _y]
                        if pixel[:3] == src_color:
                            pixdata[_x, _y] = tuple(
                                [int(c) for c in dst_color] +
                                [pixel[3] if len(pixel) == 4 else 255])
            iwidth, iheight = image.size
            if iwidth != width or iheight != height:
                cy = 0
                while cy < iheight:
                    cx = 0
                    while cx < iwidth:
                        cropped_image = image.crop(
                            (cx, cy, cx + width, cy + height))
                        new_image.paste(cropped_image,
                                        (int(spacing[3]), int(spacing[0])),
                                        cropped_image)
                        cx += width
                    cy += height
            else:
                new_image.paste(image, (int(spacing[3]), int(spacing[0])))

            if not inline:
                try:
                    new_image.save(asset_path)
                    filepath = asset_file
                    BASE_URL = config.ASSETS_URL
                    if cache_buster:
                        filetime = int(os.path.getmtime(asset_path))
                except IOError:
                    log.exception("Error while saving image")
                    inline = True  # Retry inline version
                url = '%s%s' % (config.ASSETS_URL, asset_file)
                if cache_buster:
                    url = add_cache_buster(url, filetime)
            if inline:
                output = six.BytesIO()
                new_image.save(output, format='PNG')
                contents = output.getvalue()
                output.close()
                url = 'data:' + mime_type + ';base64,' + base64.b64encode(
                    contents)
    else:
        url = '%s%s' % (BASE_URL, filepath)
        if cache_buster:
            url = add_cache_buster(url, filetime)

    if not only_path:
        url = 'url("%s")' % escape(url)
    return StringValue(url)
Пример #43
0
def test_saturate(calc):
    # Examples from the Ruby docs
    assert calc('saturate(hsl(120, 30%, 90%), 20%)') == calc(
        'hsl(120, 50%, 90%)')
    assert calc('saturate(#855, 20%)') == ColorValue.from_rgb(
        158.1 / 255, 62.9 / 255, 62.9 / 255)
Пример #44
0
def test_desaturate(calc):
    # Examples from the Ruby docs
    assert calc('desaturate(hsl(120, 30%, 90%), 20%)') == calc(
        'hsl(120, 10%, 90%)')
    assert calc('desaturate(#855, 20%)') == ColorValue.from_rgb(
        113.9 / 255, 107.1 / 255, 107.1 / 255)
Пример #45
0
def sprite_map(g, **kwargs):
    """
    Generates a sprite map from the files matching the glob pattern.
    Uses the keyword-style arguments passed in to control the placement.

    $direction - Sprite map layout. Can be `vertical` (default), `horizontal`, `diagonal` or `smart`.

    $position - For `horizontal` and `vertical` directions, the position of the sprite. (defaults to `0`)
    $<sprite>-position - Position of a given sprite.

    $padding, $spacing - Adds paddings to sprites (top, right, bottom, left). (defaults to `0, 0, 0, 0`)
    $<sprite>-padding, $<sprite>-spacing - Padding for a given sprite.

    $dst-color - Together with `$src-color`, forms a map of source colors to be converted to destiny colors (same index of `$src-color` changed to `$dst-color`).
    $<sprite>-dst-color - Destiny colors for a given sprite. (defaults to `$dst-color`)

    $src-color - Selects source colors to be converted to the corresponding destiny colors. (defaults to `black`)
    $<sprite>-dst-color - Source colors for a given sprite. (defaults to `$src-color`)

    $collapse - Collapses every image in the sprite map to a fixed size (`x` and `y`).
    $collapse-x  - Collapses a size for `x`.
    $collapse-y  - Collapses a size for `y`.
    """
    if not Image:
        raise Exception("Images manipulation require PIL")

    now_time = time.time()

    g = StringValue(g).value

    if g in sprite_maps:
        sprite_maps[glob]['*'] = now_time
    elif '..' not in g:  # Protect against going to prohibited places...
        if callable(config.STATIC_ROOT):
            glob_path = g
            rfiles = files = sorted(config.STATIC_ROOT(g))
        else:
            glob_path = os.path.join(config.STATIC_ROOT, g)
            files = glob.glob(glob_path)
            files = sorted((f, None) for f in files)
            rfiles = [(rf[len(config.STATIC_ROOT):], s) for rf, s in files]

        if not files:
            log.error("Nothing found at '%s'", glob_path)
            return StringValue(None)

        map_name = os.path.normpath(os.path.dirname(g)).replace('\\', '_').replace('/', '_')
        key = list(zip(*files)[0]) + [repr(kwargs), config.ASSETS_URL]
        key = map_name + '-' + base64.urlsafe_b64encode(hashlib.md5(repr(key)).digest()).rstrip('=').replace('-', '_')
        asset_file = key + '.png'
        ASSETS_ROOT = config.ASSETS_ROOT or os.path.join(config.STATIC_ROOT, 'assets')
        asset_path = os.path.join(ASSETS_ROOT, asset_file)
        cache_path = os.path.join(config.CACHE_ROOT or ASSETS_ROOT, asset_file + '.cache')

        sprite_map = None
        if os.path.exists(asset_path):
            try:
                save_time, asset, sprite_map, sizes = pickle.load(open(cache_path))
                sprite_maps[asset] = sprite_map
            except:
                pass

            if sprite_map:
                for file_, storage in files:
                    if storage is not None:
                        d_obj = storage.modified_time(file_)
                        _time = time.mktime(d_obj.timetuple())
                    else:
                        _time = os.path.getmtime(file_)
                    if save_time < _time:
                        if _time > now_time:
                            log.warning("File '%s' has a date in the future (cache ignored)" % file_)
                        sprite_map = None  # Invalidate cached sprite map
                        break

        if sprite_map is None:
            direction = StringValue(kwargs.get('direction', config.SPRTE_MAP_DIRECTION)).value
            repeat = StringValue(kwargs.get('repeat', 'no-repeat')).value
            collapse = kwargs.get('collapse') or 0
            if isinstance(collapse, List):
                collapse_x = int(NumberValue(collapse[0]).value)
                collapse_y = int(NumberValue(collapse[-1]).value)
            else:
                collapse_x = collapse_y = int(NumberValue(collapse).value)
            if 'collapse_x' in kwargs:
                collapse_x = int(NumberValue(kwargs['collapse_x']).value)
            if 'collapse_y' in kwargs:
                collapse_y = int(NumberValue(kwargs['collapse_y']).value)

            position = kwargs.get('position', 0)
            position = NumberValue(position)
            if position.unit != '%' and position.value > 1:
                position = position.value / 100.0
            else:
                position = position.value
            if position < 0:
                position = 0.0
            elif position > 1:
                position = 1.0

            padding = kwargs.get('padding', kwargs.get('spacing', 0))
            padding = [int(NumberValue(v).value) for v in List.from_maybe(padding)]
            padding = (padding * 4)[:4]

            dst_colors = kwargs.get('dst_color')
            dst_colors = [list(ColorValue(v).value[:3]) for v in List.from_maybe(dst_colors) if v]
            src_colors = kwargs.get('src_color')
            src_colors = [tuple(ColorValue(v).value[:3]) if v else (0, 0, 0) for v in List.from_maybe(src_colors)]
            len_colors = max(len(dst_colors), len(src_colors))
            dst_colors = (dst_colors * len_colors)[:len_colors]
            src_colors = (src_colors * len_colors)[:len_colors]

            def images(f=lambda x: x):
                for file_, storage in f(files):
                    if storage is not None:
                        _file = storage.open(file_)
                    else:
                        _file = file_
                    _image = Image.open(_file)
                    yield _image

            names = tuple(os.path.splitext(os.path.basename(file_))[0] for file_, storage in files)

            has_dst_colors = False
            all_dst_colors = []
            all_src_colors = []
            all_positions = []
            all_paddings = []

            for name in names:
                name = name.replace('-', '_')

                _position = kwargs.get(name + '_position')
                if _position is None:
                    _position = position
                else:
                    _position = NumberValue(_position)
                    if _position.unit != '%' and _position.value > 1:
                        _position = _position.value / 100.0
                    else:
                        _position = _position.value
                    if _position < 0:
                        _position = 0.0
                    elif _position > 1:
                        _position = 1.0
                all_positions.append(_position)

                _padding = kwargs.get(name + '_padding', kwargs.get(name + '_spacing'))
                if _padding is None:
                    _padding = padding
                else:
                    _padding = [int(NumberValue(v).value) for v in List.from_maybe(_padding)]
                    _padding = (_padding * 4)[:4]
                all_paddings.append(_padding)

                _dst_colors = kwargs.get(name + '_dst_color')
                if _dst_colors is None:
                    _dst_colors = dst_colors
                    if dst_colors:
                        has_dst_colors = True
                else:
                    has_dst_colors = True
                    _dst_colors = [list(ColorValue(v).value[:3]) for v in List.from_maybe(_dst_colors) if v]
                _src_colors = kwargs.get(name + '_src_color')
                if _src_colors is None:
                    _src_colors = src_colors
                else:
                    _src_colors = [tuple(ColorValue(v).value[:3]) if v else (0, 0, 0) for v in List.from_maybe(_src_colors)]
                _len_colors = max(len(_dst_colors), len(_src_colors))
                _dst_colors = (_dst_colors * _len_colors)[:_len_colors]
                _src_colors = (_src_colors * _len_colors)[:_len_colors]
                all_dst_colors.append(_dst_colors)
                all_src_colors.append(_src_colors)

            sizes = tuple((collapse_x or i.size[0], collapse_y or i.size[1]) for i in images())

            if direction == 'horizontal':
                layout = HorizontalSpritesLayout(sizes, all_paddings, position=all_positions)
            elif direction == 'vertical':
                layout = VerticalSpritesLayout(sizes, all_paddings, position=all_positions)
            elif direction == 'diagonal':
                layout = DiagonalSpritesLayout(sizes, all_paddings)
            elif direction == 'smart':
                layout = PackedSpritesLayout(sizes, all_paddings)
            else:
                raise Exception("Invalid direction %r" % (direction,))
            layout_positions = list(layout)

            new_image = Image.new(
                mode='RGBA',
                size=(layout.width, layout.height),
                color=(0, 0, 0, 0)
            )

            useless_dst_color = has_dst_colors

            offsets_x = []
            offsets_y = []
            for i, image in enumerate(images()):
                x, y, width, height, cssx, cssy, cssw, cssh = layout_positions[i]
                iwidth, iheight = image.size

                if has_dst_colors:
                    pixdata = image.load()
                    for _y in xrange(iheight):
                        for _x in xrange(iwidth):
                            pixel = pixdata[_x, _y]
                            a = pixel[3] if len(pixel) == 4 else 255
                            if a:
                                rgb = pixel[:3]
                                for j, dst_color in enumerate(all_dst_colors[i]):
                                    if rgb == all_src_colors[i][j]:
                                        new_color = tuple([int(c) for c in dst_color] + [a])
                                        if pixel != new_color:
                                            pixdata[_x, _y] = new_color
                                            useless_dst_color = False
                                        break

                if iwidth != width or iheight != height:
                    cy = 0
                    while cy < iheight:
                        cx = 0
                        while cx < iwidth:
                            new_image = alpha_composite(new_image, image, (x, y), (cx, cy, cx + width, cy + height))
                            cx += width
                        cy += height
                else:
                    new_image.paste(image, (x, y))
                offsets_x.append(cssx)
                offsets_y.append(cssy)

            if useless_dst_color:
                log.warning("Useless use of $dst-color in sprite map for files at '%s' (never used for)" % glob_path)

            try:
                new_image.save(asset_path)
            except IOError:
                log.exception("Error while saving image")

            filetime = int(now_time)
            url = '%s%s?_=%s' % (config.ASSETS_URL, asset_file, filetime)
            asset = 'url("%s") %s' % (escape(url), repeat)

            # Add the new object:
            sprite_map = dict(zip(names, zip(sizes, rfiles, offsets_x, offsets_y)))
            sprite_map['*'] = now_time
            sprite_map['*f*'] = asset_file
            sprite_map['*k*'] = key
            sprite_map['*n*'] = map_name
            sprite_map['*t*'] = filetime

            cache_tmp = tempfile.NamedTemporaryFile(delete=False, dir=ASSETS_ROOT)
            pickle.dump((now_time, asset, sprite_map, zip(files, sizes)), cache_tmp)
            cache_tmp.close()
            os.rename(cache_tmp.name, cache_path)

            # Use the sorted list to remove older elements (keep only 500 objects):
            if len(sprite_maps) > MAX_SPRITE_MAPS:
                for a in sorted(sprite_maps, key=lambda a: sprite_maps[a]['*'], reverse=True)[KEEP_SPRITE_MAPS:]:
                    del sprite_maps[a]
                log.warning("Exceeded maximum number of sprite maps (%s)" % MAX_SPRITE_MAPS)
            sprite_maps[asset] = sprite_map
        for file_, size in sizes:
            _image_size_cache[file_] = size
    ret = StringValue(asset)
    return ret
Пример #46
0
def green(color):
    c = ColorValue(color).value
    return NumberValue(c[1])