diff options
author | Руслан Ижбулатов <lrn1986@gmail.com> | 2015-05-18 20:55:24 +0000 |
---|---|---|
committer | Jakub Steiner <jimmac@gmail.com> | 2015-05-19 16:11:22 +0200 |
commit | feb88a1e0b0930469c9ffcd97c83d24f6c1356f9 (patch) | |
tree | d414065bbef9361f77c23c7c5c2fdc9b960cdb65 | |
parent | e22e0540ea8d67f0844046dc10e855ea64686a2e (diff) | |
download | adwaita-icon-theme-feb88a1e0b0930469c9ffcd97c83d24f6c1356f9.tar.gz |
Add shadow generation and a make-script for W32 cursors
* anicursorgen.py now can generate shadows similar to those that W32 WM
generates for system cursors. This is enabled by the -s option and
disabled (overriding previous -s occurrences) by the -n option.
Options -r, -d, -b and -c are available to customize the shadow generator.
This will generate shadows even for parts of cursors that normally have
no shadows in Adwaita (the sub-icons attached to dnd and left_ptr variants).
However, this is consistent with how W32 WM draws shadows (i.e. IDC_HELP).
* New make-w32.sh, which is just like make.sh, except that it generates
W32 cursors. Any arguments given to make-w32.sh will be passed along
to anicursorgen.py that it runs. It knows enough to pass an extra -n
when generating a 'tcross' cursor.
https://bugzilla.gnome.org/show_bug.cgi?id=749559
-rwxr-xr-x | src/cursors/anicursorgen.py | 104 | ||||
-rw-r--r-- | src/cursors/pngs/make-w32.sh | 65 |
2 files changed, 149 insertions, 20 deletions
diff --git a/src/cursors/anicursorgen.py b/src/cursors/anicursorgen.py index ad13bfffe..7c4c31a26 100755 --- a/src/cursors/anicursorgen.py +++ b/src/cursors/anicursorgen.py @@ -24,7 +24,9 @@ import argparse import shlex import io import struct +import math from PIL import Image +from PIL import ImageFilter p = struct.pack @@ -40,6 +42,21 @@ def main (): help='Display the usage message and exit.') parser.add_argument ('-p', '--prefix', metavar='dir', default=None, help='Find cursor images in the directory specified by dir. If not specified, the current directory is used.') + parser.add_argument ('-s', '--add-shadows', action='store_true', dest='add_shadows', default=False, + help='Generate shadows for cursors (disabled by default).') + parser.add_argument ('-n', '--no-shadows', action='store_false', dest='add_shadows', default=False, + help='Do not generate shadows for cursors (put after --add-shadows to cancel its effect).') + + shadows = parser.add_argument_group (title='Shadow generation', description='Only relevant when --add-shadows is given') + + shadows.add_argument ('-r', '--right-shift', metavar='%', type=float, default=9.375, + help='Shift shadow right by this percentage of the canvas size (default is 9.375).') + shadows.add_argument ('-d', '--down-shift', metavar='%', type=float, default=3.125, + help='Shift shadow down by this percentage of the canvas size (default is 3.125).') + shadows.add_argument ('-b', '--blur', metavar='%', type=float, default=3.125, + help='Blur radius, in percentage of the canvas size (default is 3.125, set to 0 to disable blurring).') + shadows.add_argument ('-c', '--color', metavar='%', default='0x80808080', + help='Shadow color in 0xRRGGBBAA form (default is 0x80808080).') parser.add_argument ('input_config', default='-', metavar='input-config [output-file]', nargs='?', help='Input config file (stdin by default).') @@ -48,6 +65,15 @@ def main (): args = parser.parse_args () + try: + if args.color[0] != '0' or args.color[1] not in ['x', 'X'] or len (args.color) != 10: + raise ValueError + args.color = (int (args.color[2:4], 16), int (args.color[4:6], 16), int (args.color[6:8], 16), int (args.color[8:10], 16)) + except: + print ("Can't parse the color '{}'".format (args.color), file=sys.stderr) + parser.print_help () + return 1 + if args.prefix is None: args.prefix = os.getcwd () @@ -61,22 +87,22 @@ def main (): else: output_file = open (args.output_file, 'wb') - result = make_cursor_from (input_config, output_file, args.prefix) + result = make_cursor_from (input_config, output_file, args) input_config.close () output_file.close () return result -def make_cursor_from (inp, out, prefix): - frames = parse_config_from (inp, prefix) +def make_cursor_from (inp, out, args): + frames = parse_config_from (inp, args.prefix) animated = frames_have_animation (frames) if animated: - result = make_ani (frames, out) + result = make_ani (frames, out, args) else: - buf = make_cur (frames) + buf = make_cur (frames, args) copy_to (out, buf) result = 0 @@ -102,7 +128,7 @@ def frames_have_animation (frames): return False -def make_cur (frames): +def make_cur (frames, args): buf = io.BytesIO () buf.write (p ('<HHH', 0, 2, len (frames))) frame_offsets = [] @@ -120,12 +146,22 @@ def make_cur (frames): frame_offset = buf.seek (0, io.SEEK_CUR) frame_offsets[i].append (frame_offset) + frame_png = Image.open (frame[3]) + + if args.add_shadows: + succeeded, shadowed = create_shadow (frame_png, args) + if succeeded == 0: + frame_png.close () + frame_png = shadowed + compressed = frame[0] > 48 if compressed: - write_png (buf, frame) + write_png (buf, frame, frame_png) else: - write_cur (buf, frame) + write_cur (buf, frame, frame_png) + + frame_png.close () frame_end = buf.seek (0, io.SEEK_CUR) frame_offsets[i].append (frame_end - frame_offset) @@ -172,7 +208,7 @@ def make_framesets (frames): return framesets -def make_ani (frames, out): +def make_ani (frames, out, args): framesets = make_framesets (frames) if framesets is None: return 1 @@ -207,7 +243,7 @@ def make_ani (frames, out): for frameset in framesets: buf.write (b'icon') - cur = make_cur (frameset) + cur = make_cur (frameset, args) cur_size = cur.seek (0, io.SEEK_END) aligned_cur_size = cur_size #if cur_size % 4 != 0: @@ -228,17 +264,11 @@ def make_ani (frames, out): return 0 -def write_png (out, frame): - with open (frame[3], 'rb') as src: - while True: - buf = src.read (1024) - if buf is None or len (buf) == 0: - break - out.write (buf) +def write_png (out, frame, frame_png): + frame_png.save (out, "png", optimize=True) -def write_cur (out, frame): - img = Image.open (frame[3]) - pixels = img.load () +def write_cur (out, frame, frame_png): + pixels = frame_png.load () out.write (p ('<I II HH IIIIII', 40, frame[0], frame[0] * 2, 1, 32, 0, 0, 0, 0, 0, 0)) @@ -293,5 +323,39 @@ def parse_config_from (inp, prefix): return frames +def create_shadow (orig, args): + blur_px = orig.size[0] / 100.0 * args.blur + right_px = int (orig.size[0] / 100.0 * args.right_shift) + down_px = int (orig.size[1] / 100.0 * args.down_shift) + + shadow = Image.new ('RGBA', orig.size, (0, 0, 0, 0)) + shadowize (shadow, orig, args.color) + shadow.load () + + if args.blur > 0: + crop = (int (math.floor (-blur_px)), int (math.floor (-blur_px)), orig.size[0] + int (math.ceil (blur_px)), orig.size[1] + int (math.ceil (blur_px))) + right_px += int (math.floor (-blur_px)) + down_px += int (math.floor (-blur_px)) + shadow = shadow.crop (crop) + flt = ImageFilter.GaussianBlur (blur_px) + shadow = shadow.filter (flt) + shadow.load () + + shadowed = Image.new ('RGBA', orig.size, (0, 0, 0, 0)) + shadowed.paste (shadow, (right_px, down_px)) + shadowed.crop ((0, 0, orig.size[0], orig.size[1])) + shadowed = Image.alpha_composite (shadowed, orig) + + return 0, shadowed + +def shadowize (shadow, orig, color): + o_pxs = orig.load () + s_pxs = shadow.load () + for y in range (orig.size[1]): + for x in range (orig.size[0]): + o_px = o_pxs[x, y] + if o_px[3] > 0: + s_pxs[x, y] = (color[0], color[1], color[2], int (color[3] * (o_px[3] / 255.0))) + if __name__ == '__main__': sys.exit (main ()) diff --git a/src/cursors/pngs/make-w32.sh b/src/cursors/pngs/make-w32.sh new file mode 100644 index 000000000..9268e90b9 --- /dev/null +++ b/src/cursors/pngs/make-w32.sh @@ -0,0 +1,65 @@ +#!/bin/sh +# $@ is for the caller to be able to supply arguments to anicursorgen (-s, in particular) +GEN=../anicursorgen.py\ "$@" +for theme in Adwaita Adwaita-Large Adwaita-ExtraLarge +do + mkdir -p ../../../$theme/cursors + ${GEN} left_ptr_watch.in ../../../$theme/cursors/left_ptr_watch.ani +# ${GEN} hand1.in ../../../$theme/cursors/hand1.cur + ${GEN} hand2.in ../../../$theme/cursors/hand2.cur + ${GEN} left_ptr.in ../../../$theme/cursors/left_ptr.cur +# ${GEN} center_ptr.in ../../../$theme/cursors/center_ptr.cur + ${GEN} xterm.in ../../../$theme/cursors/xterm.cur + ${GEN} crossed_circle.in ../../../$theme/cursors/crossed_circle.cur + ${GEN} right_ptr.in ../../../$theme/cursors/right_ptr.cur + ${GEN} copy.in ../../../$theme/cursors/copy.cur + ${GEN} move.in ../../../$theme/cursors/move.cur + ${GEN} pointer-move.in ../../../$theme/cursors/pointer-move.cur + ${GEN} link.in ../../../$theme/cursors/link.cur + ${GEN} circle.in ../../../$theme/cursors/circle.cur + ${GEN} sb_h_double_arrow.in ../../../$theme/cursors/sb_h_double_arrow.cur + ${GEN} sb_v_double_arrow.in ../../../$theme/cursors/sb_v_double_arrow.cur + ${GEN} top_left_corner.in ../../../$theme/cursors/top_left_corner.cur + ${GEN} top_right_corner.in ../../../$theme/cursors/top_right_corner.cur + ${GEN} bottom_left_corner.in ../../../$theme/cursors/bottom_left_corner.cur + ${GEN} bottom_right_corner.in ../../../$theme/cursors/bottom_right_corner.cur + ${GEN} fd_double_arrow.in ../../../$theme/cursors/fd_double_arrow.cur + ${GEN} bd_double_arrow.in ../../../$theme/cursors/bd_double_arrow.cur + ${GEN} watch.in ../../../$theme/cursors/watch.ani + ${GEN} sb_left_arrow.in ../../../$theme/cursors/sb_left_arrow.cur + ${GEN} sb_right_arrow.in ../../../$theme/cursors/sb_right_arrow.cur + ${GEN} sb_up_arrow.in ../../../$theme/cursors/sb_up_arrow.cur + ${GEN} sb_down_arrow.in ../../../$theme/cursors/sb_down_arrow.cur +# ${GEN} based_arrow_down.in ../../../$theme/cursors/based_arrow_down.cur +# ${GEN} based_arrow_up.in ../../../$theme/cursors/based_arrow_up.cur + ${GEN} bottom_side.in ../../../$theme/cursors/bottom_side.cur + ${GEN} top_side.in ../../../$theme/cursors/top_side.cur + ${GEN} left_side.in ../../../$theme/cursors/left_side.cur + ${GEN} right_side.in ../../../$theme/cursors/right_side.cur + ${GEN} grabbing.in ../../../$theme/cursors/grabbing.cur + ${GEN} question_arrow.in ../../../$theme/cursors/question_arrow.cur + ${GEN} top_tee.in ../../../$theme/cursors/top_tee.cur + ${GEN} bottom_tee.in ../../../$theme/cursors/bottom_tee.cur + ${GEN} left_tee.in ../../../$theme/cursors/left_tee.cur + ${GEN} right_tee.in ../../../$theme/cursors/right_tee.cur + ${GEN} ul_angle.in ../../../$theme/cursors/ul_angle.cur + ${GEN} ll_angle.in ../../../$theme/cursors/ll_angle.cur + ${GEN} ur_angle.in ../../../$theme/cursors/ur_angle.cur + ${GEN} lr_angle.in ../../../$theme/cursors/lr_angle.cur + ${GEN} X_cursor.in ../../../$theme/cursors/X_cursor.cur + ${GEN} crosshair.in ../../../$theme/cursors/crosshair.cur + ${GEN} cross.in ../../../$theme/cursors/cross.cur + ${GEN} --no-shadows tcross.in ../../../$theme/cursors/tcross.cur + ${GEN} dotbox.in ../../../$theme/cursors/dotbox.cur + ${GEN} plus.in ../../../$theme/cursors/plus.cur + ${GEN} pencil.in ../../../$theme/cursors/pencil.cur + ${GEN} dnd-none.in ../../../$theme/cursors/dnd-none.cur + ${GEN} dnd-copy.in ../../../$theme/cursors/dnd-copy.cur + ${GEN} dnd-link.in ../../../$theme/cursors/dnd-link.cur + ${GEN} dnd-move.in ../../../$theme/cursors/dnd-move.cur + ${GEN} dnd-ask.in ../../../$theme/cursors/dnd-ask.cur + ${GEN} zoom-in.in ../../../$theme/cursors/zoom-in.cur + ${GEN} zoom-out.in ../../../$theme/cursors/zoom-out.cur + ${GEN} all-scroll.in ../../../$theme/cursors/all-scroll.cur + ${GEN} vertical-text.in ../../../$theme/cursors/vertical-text.cur +done |