summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorРуслан Ижбулатов <lrn1986@gmail.com>2015-05-18 20:55:24 +0000
committerJakub Steiner <jimmac@gmail.com>2015-05-19 16:11:22 +0200
commitfeb88a1e0b0930469c9ffcd97c83d24f6c1356f9 (patch)
treed414065bbef9361f77c23c7c5c2fdc9b960cdb65
parente22e0540ea8d67f0844046dc10e855ea64686a2e (diff)
downloadadwaita-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-xsrc/cursors/anicursorgen.py104
-rw-r--r--src/cursors/pngs/make-w32.sh65
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