1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/reftests/border-image/gen-refs.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,347 @@ 1.4 +# This Source Code Form is subject to the terms of the Mozilla Public 1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.7 + 1.8 +# Generates tables of background images which correspond with border images for 1.9 +# creating reftests. Input is the filename containing input defined below (a subset 1.10 +# of the allowed CSS border properties). An html representation of a table is 1.11 +# output to stdout. 1.12 +# 1.13 +# Usage: python gen-refs.py input_filename 1.14 +# 1.15 +# Input must take the form (order is not important, nothing is optional, distance in order top, right, bottom, left): 1.16 +# width: p; 1.17 +# height: p; 1.18 +# border-width: p; 1.19 +# border-image-source: ...; 1.20 +# border-image-slice: p p p p; 1.21 +# note that actually border-image-slice takes numbers without px, which represent pixels anyway (or at least coords) 1.22 +# border-image-width: np np np np; 1.23 +# border-image-repeat: stretch | repeat | round; 1.24 +# border-image-outset: np np np np; 1.25 +# 1.26 +# where: 1.27 +# p ::= n'px' 1.28 +# np ::= n | p 1.29 +# 1.30 +# Assumes there is no intrinsic size for the border-image-source, so uses 1.31 +# the size of the border image area. 1.32 + 1.33 +import sys 1.34 + 1.35 +class Point: 1.36 + def __init__(self, w=0, h=0): 1.37 + self.x = w 1.38 + self.y = h 1.39 +class Size: 1.40 + def __init__(self, w=0, h=0): 1.41 + self.width = w 1.42 + self.height = h 1.43 +class Rect: 1.44 + def __init__(self, x=0, y=0, x2=0, y2=0): 1.45 + self.x = x 1.46 + self.y = y 1.47 + self.x2 = x2 1.48 + self.y2 = y2 1.49 + def width(self): 1.50 + return self.x2 - self.x 1.51 + def height(self): 1.52 + return self.y2 - self.y 1.53 + 1.54 +class Props: 1.55 + def __init__(self): 1.56 + self.size = Size() 1.57 + 1.58 +class np: 1.59 + def __init__(self, n, p): 1.60 + self.n = n 1.61 + self.p = p 1.62 + 1.63 + def get_absolute(self, ref): 1.64 + if not self.p == 0: 1.65 + return self.p 1.66 + return self.n * ref 1.67 + 1.68 +def parse_p(tok): 1.69 + if tok[-2:] == "px": 1.70 + return float(tok[:-2]) 1.71 + print "Whoops, not a pixel value " + tok 1.72 + 1.73 +def parse_np(tok): 1.74 + if tok[-2:] == "px": 1.75 + return np(0, float(tok[:-2])) 1.76 + return np(float(tok), 0) 1.77 + 1.78 +def parse(filename): 1.79 + f = open(filename, "r") 1.80 + props = Props() 1.81 + for l in f: 1.82 + l = l.strip() 1.83 + if not l[-1] == ";": 1.84 + continue 1.85 + toks = l[:-1].split() 1.86 + if toks[0] == "border-width:": 1.87 + props.width = parse_p(toks[1]) 1.88 + if toks[0] == "height:": 1.89 + props.size.height = parse_p(toks[1]) 1.90 + if toks[0] == "width:": 1.91 + props.size.width = parse_p(toks[1]) 1.92 + if toks[0] == "border-image-source:": 1.93 + props.source = l[l.find(":")+1:l.rfind(";")].strip() 1.94 + if toks[0] == "border-image-repeat:": 1.95 + props.repeat = toks[1] 1.96 + if toks[0] == "border-image-slice:": 1.97 + props.slice = map(parse_p, toks[1:5]) 1.98 + if toks[0] == "border-image-width:": 1.99 + props.image_width = map(parse_np, toks[1:5]) 1.100 + if toks[0] == "border-image-outset:": 1.101 + props.outset = map(parse_np, toks[1:5]) 1.102 + f.close() 1.103 + return props 1.104 + 1.105 +# the result of normalisation is that all sizes are in pixels and the size, 1.106 +# widths, and outset have been normalised to a size and width - the former is 1.107 +# the element's interior, the latter is the width of the drawn border. 1.108 +def normalise(props): 1.109 + result = Props() 1.110 + result.source = props.source 1.111 + result.repeat = props.repeat 1.112 + result.width = map(lambda x: x.get_absolute(props.width), props.image_width) 1.113 + outsets = map(lambda x: x.get_absolute(props.width), props.outset) 1.114 + result.size.width = props.size.width + 2*props.width + outsets[1] + outsets[3] 1.115 + result.size.height = props.size.height + 2*props.width + outsets[0] + outsets[2] 1.116 + result.slice = props.slice 1.117 + for i in [0,2]: 1.118 + if result.slice[i] > result.size.height: 1.119 + result.slice[i] = result.size.height 1.120 + if result.slice[i+1] > result.size.width: 1.121 + result.slice[i+1] = result.size.width 1.122 + 1.123 + return result 1.124 + 1.125 +def check_parse(props): 1.126 + if not hasattr(props, 'source'): 1.127 + print "missing border-image-source" 1.128 + return False 1.129 + if not hasattr(props.size, 'width'): 1.130 + print "missing width" 1.131 + return False 1.132 + if not hasattr(props.size, 'height'): 1.133 + print "missing height" 1.134 + return False 1.135 + if not hasattr(props, 'width'): 1.136 + print "missing border-width" 1.137 + return False 1.138 + if not hasattr(props, 'image_width'): 1.139 + print "missing border-image-width" 1.140 + return False 1.141 + if not hasattr(props, 'slice'): 1.142 + print "missing border-image-slice" 1.143 + return False 1.144 + if not hasattr(props, 'repeat') or (props.repeat not in ["stretch", "repeat", "round"]): 1.145 + print "missing or incorrect border-image-repeat '" + props.repeat + "'" 1.146 + return False 1.147 + if not hasattr(props, 'outset'): 1.148 + print "missing border-image-outset" 1.149 + return False 1.150 + 1.151 + return True 1.152 + 1.153 +def check_normalise(props): 1.154 + if not hasattr(props, 'source'): 1.155 + print "missing border-image-source" 1.156 + return False 1.157 + if not hasattr(props.size, 'width'): 1.158 + print "missing width" 1.159 + return False 1.160 + if not hasattr(props.size, 'height'): 1.161 + print "missing height" 1.162 + return False 1.163 + if not hasattr(props, 'slice'): 1.164 + print "missing border-image-slice" 1.165 + return False 1.166 + if not hasattr(props, 'repeat') or (props.repeat not in ["stretch", "repeat", "round"]): 1.167 + print "missing or incorrect border-image-repeat '" + props.repeat + "'" 1.168 + return False 1.169 + 1.170 + return True 1.171 + 1.172 +class Tile: 1.173 + def __init__(self): 1.174 + self.slice = Rect() 1.175 + self.border_width = Rect() 1.176 + 1.177 +# throughout, we will use arrays for nine-patches, the indices correspond thusly: 1.178 +# 0 1 2 1.179 +# 3 4 5 1.180 +# 6 7 8 1.181 + 1.182 +# Compute the source tiles' slice and border-width sizes 1.183 +def make_src_tiles(): 1.184 + tiles = [Tile() for i in range(9)] 1.185 + 1.186 + rows = [range(3*i, 3*(i+1)) for i in range(3)] 1.187 + cols = [[i, i+3, i+6] for i in range(3)] 1.188 + 1.189 + row_limits_slice = [0, props.slice[3], props.size.width - props.slice[1], props.size.width] 1.190 + row_limits_width = [0, props.width[3], props.size.width - props.width[1], props.size.width] 1.191 + for r in range(3): 1.192 + for t in [tiles[i] for i in cols[r]]: 1.193 + t.slice.x = row_limits_slice[r] 1.194 + t.slice.x2 = row_limits_slice[r+1] 1.195 + t.border_width.x = row_limits_width[r] 1.196 + t.border_width.x2 = row_limits_width[r+1] 1.197 + 1.198 + col_limits_slice = [0, props.slice[0], props.size.height - props.slice[2], props.size.height] 1.199 + col_limits_width = [0, props.width[0], props.size.height - props.width[2], props.size.height] 1.200 + for c in range(3): 1.201 + for t in [tiles[i] for i in rows[c]]: 1.202 + t.slice.y = col_limits_slice[c] 1.203 + t.slice.y2 = col_limits_slice[c+1] 1.204 + t.border_width.y = col_limits_width[c] 1.205 + t.border_width.y2 = col_limits_width[c+1] 1.206 + 1.207 + return tiles 1.208 + 1.209 +def compute(props): 1.210 + tiles = make_src_tiles() 1.211 + 1.212 + # corners scale easy 1.213 + for t in [tiles[i] for i in [0, 2, 6, 8]]: 1.214 + t.scale = Point(t.border_width.width()/t.slice.width(), t.border_width.height()/t.slice.height()) 1.215 + # edges are by their secondary dimension 1.216 + for t in [tiles[i] for i in [1, 7]]: 1.217 + t.scale = Point(t.border_width.height()/t.slice.height(), t.border_width.height()/t.slice.height()) 1.218 + for t in [tiles[i] for i in [3, 5]]: 1.219 + t.scale = Point(t.border_width.width()/t.slice.width(), t.border_width.width()/t.slice.width()) 1.220 + # the middle is scaled by the factors for the top and left edges 1.221 + tiles[4].scale = Point(tiles[1].scale.x, tiles[3].scale.y) 1.222 + 1.223 + # the size of a source tile for the middle section 1.224 + src_tile_size = Size(tiles[4].slice.width()*tiles[4].scale.x, tiles[4].slice.height()*tiles[4].scale.y) 1.225 + 1.226 + # the size of a single destination tile in the central part 1.227 + dest_tile_size = Size() 1.228 + if props.repeat == "stretch": 1.229 + dest_tile_size.width = tiles[4].border_width.width() 1.230 + dest_tile_size.height = tiles[4].border_width.height() 1.231 + for t in [tiles[i] for i in [1, 7]]: 1.232 + t.scale.x = t.border_width.width()/t.slice.width() 1.233 + for t in [tiles[i] for i in [3, 5]]: 1.234 + t.scale.y = t.border_width.height()/t.slice.height() 1.235 + elif props.repeat == "repeat": 1.236 + dest_tile_size = src_tile_size 1.237 + elif props.repeat == "round": 1.238 + dest_tile_size.width = tiles[4].border_width.width() / math.ceil(tiles[4].border_width.width() / src_tile_size.width) 1.239 + dest_tile_size.height = tiles[4].border_width.height() / math.ceil(tiles[4].border_width.height() / src_tile_size.height) 1.240 + for t in [tiles[i] for i in [1, 4, 7]]: 1.241 + t.scale.x = dest_tile_size.width/t.slice.width() 1.242 + for t in [tiles[i] for i in [3, 4, 5]]: 1.243 + t.scale.y = dest_tile_size.height/t.slice.height() 1.244 + else: 1.245 + print "Whoops, invalid border-image-repeat value" 1.246 + 1.247 + # catch overlapping slices. Its easier to deal with it here than to catch 1.248 + # earlier and have to avoid all the divide by zeroes above 1.249 + for t in tiles: 1.250 + if t.slice.width() < 0: 1.251 + t.scale.x = 0 1.252 + if t.slice.height() < 0: 1.253 + t.scale.y = 0 1.254 + 1.255 + tiles_h = int(math.ceil(tiles[4].border_width.width()/dest_tile_size.width)+2) 1.256 + tiles_v = int(math.ceil(tiles[4].border_width.height()/dest_tile_size.height)+2) 1.257 + 1.258 + # if border-image-repeat: repeat, then we will later center the tiles, that 1.259 + # means we need an extra tile for the two 'half' tiles at either end 1.260 + if props.repeat == "repeat": 1.261 + if tiles_h % 2 == 0: 1.262 + tiles_h += 1 1.263 + if tiles_v % 2 == 0: 1.264 + tiles_v += 1 1.265 + dest_tiles = [Tile() for i in range(tiles_h * tiles_v)] 1.266 + 1.267 + # corners 1.268 + corners = [(0, 0), (tiles_h-1, 2), (tiles_v*(tiles_h-1), 6), (tiles_v*tiles_h-1, 8)] 1.269 + for d,s in corners: 1.270 + dest_tiles[d].size = Size(tiles[s].scale.x*props.size.width, tiles[s].scale.y*props.size.height) 1.271 + dest_tiles[d].dest_size = Size(tiles[s].border_width.width(), tiles[s].border_width.height()) 1.272 + dest_tiles[0].offset = Point(0, 0) 1.273 + dest_tiles[tiles_h-1].offset = Point(tiles[2].border_width.width() - dest_tiles[tiles_h-1].size.width, 0) 1.274 + dest_tiles[tiles_v*(tiles_h-1)].offset = Point(0, tiles[6].border_width.height() - dest_tiles[tiles_v*(tiles_h-1)].size.height) 1.275 + dest_tiles[tiles_v*tiles_h-1].offset = Point(tiles[8].border_width.width() - dest_tiles[tiles_h*tiles_v-1].size.width, tiles[8].border_width.height() - dest_tiles[tiles_h*tiles_v-1].size.height) 1.276 + 1.277 + # horizontal edges 1.278 + for i in range(1, tiles_h-1): 1.279 + dest_tiles[i].size = Size(tiles[1].scale.x*props.size.width, tiles[1].scale.y*props.size.height) 1.280 + dest_tiles[(tiles_v-1)*tiles_h + i].size = Size(tiles[7].scale.x*props.size.width, tiles[7].scale.y*props.size.height) 1.281 + dest_tiles[i].dest_size = Size(dest_tile_size.width, tiles[1].border_width.height()) 1.282 + dest_tiles[(tiles_v-1)*tiles_h + i].dest_size = Size(dest_tile_size.width, tiles[7].border_width.height()) 1.283 + dest_tiles[i].offset = Point(-tiles[1].scale.x*tiles[1].slice.x, -tiles[1].scale.y*tiles[1].slice.y) 1.284 + dest_tiles[(tiles_v-1)*tiles_h + i].offset = Point(-tiles[7].scale.x*tiles[7].slice.x, -tiles[7].scale.y*tiles[7].slice.y) 1.285 + 1.286 + # vertical edges 1.287 + for i in range(1, tiles_v-1): 1.288 + dest_tiles[i*tiles_h].size = Size(tiles[3].scale.x*props.size.width, tiles[3].scale.y*props.size.height) 1.289 + dest_tiles[(i+1)*tiles_h-1].size = Size(tiles[5].scale.x*props.size.width, tiles[5].scale.y*props.size.height) 1.290 + dest_tiles[i*tiles_h].dest_size = Size(tiles[3].border_width.width(), dest_tile_size.height) 1.291 + dest_tiles[(i+1)*tiles_h-1].dest_size = Size(tiles[5].border_width.width(), dest_tile_size.height) 1.292 + dest_tiles[i*tiles_h].offset = Point(-tiles[3].scale.x*tiles[3].slice.x, -tiles[3].scale.y*tiles[3].slice.y) 1.293 + dest_tiles[(i+1)*tiles_h-1].offset = Point(-tiles[5].scale.x*tiles[5].slice.x, -tiles[5].scale.y*tiles[5].slice.y) 1.294 + 1.295 + # middle 1.296 + for i in range(1, tiles_v-1): 1.297 + for j in range(1, tiles_h-1): 1.298 + dest_tiles[i*tiles_h+j].size = Size(tiles[4].scale.x*props.size.width, tiles[4].scale.y*props.size.height) 1.299 + dest_tiles[i*tiles_h+j].offset = Point(-tiles[4].scale.x*tiles[4].slice.x, -tiles[4].scale.y*tiles[4].slice.y) 1.300 + dest_tiles[i*tiles_h+j].dest_size = dest_tile_size 1.301 + 1.302 + # edge and middle tiles are centered with border-image-repeat: repeat 1.303 + # we need to change the offset to take account of this and change the dest_size 1.304 + # of the tiles at the sides of the edges if they are clipped 1.305 + if props.repeat == "repeat": 1.306 + diff_h = ((tiles_h-2)*dest_tile_size.width - tiles[4].border_width.width()) / 2 1.307 + diff_v = ((tiles_v-2)*dest_tile_size.height - tiles[4].border_width.height()) / 2 1.308 + for i in range(0, tiles_h): 1.309 + dest_tiles[tiles_h + i].dest_size.height -= diff_v 1.310 + dest_tiles[tiles_h + i].offset.y -= diff_v #* tiles[4].scale.y 1.311 + dest_tiles[(tiles_v-2)*tiles_h + i].dest_size.height -= diff_v 1.312 + for i in range(0, tiles_v): 1.313 + dest_tiles[i*tiles_h + 1].dest_size.width -= diff_h 1.314 + dest_tiles[i*tiles_h + 1].offset.x -= diff_h #* tiles[4].scale.x 1.315 + dest_tiles[(i+1)*tiles_h-2].dest_size.width -= diff_h 1.316 + 1.317 + # output the table to simulate the border 1.318 + print "<table>" 1.319 + for i in range(tiles_h): 1.320 + print "<col style=\"width: " + str(dest_tiles[i].dest_size.width) + "px;\">" 1.321 + for i in range(tiles_v): 1.322 + print "<tr style=\"height: " + str(dest_tiles[i*tiles_h].dest_size.height) + "px;\">" 1.323 + for j in range(tiles_h): 1.324 + width = dest_tiles[i*tiles_h+j].size.width 1.325 + height = dest_tiles[i*tiles_h+j].size.height 1.326 + # catch any tiles with negative widths/heights 1.327 + # this happends when the total of the border-image-slices > borde drawing area 1.328 + if width <= 0 or height <= 0: 1.329 + print " <td style=\"background: white;\"></td>" 1.330 + else: 1.331 + print " <td style=\"background-image: " + props.source + "; background-size: " + str(width) + "px " + str(height) + "px; background-position: " + str(dest_tiles[i*tiles_h+j].offset.x) + "px " + str(dest_tiles[i*tiles_h+j].offset.y) + "px;\"></td>" 1.332 + print "</tr>" 1.333 + print "</table>" 1.334 + 1.335 + 1.336 +# start here 1.337 +args = sys.argv[1:] 1.338 +if len(args) == 0: 1.339 + print "whoops: no source file" 1.340 + exit(1) 1.341 + 1.342 + 1.343 +props = parse(args[0]) 1.344 +if not check_parse(props): 1.345 + print dir(props) 1.346 + exit(1) 1.347 +props = normalise(props) 1.348 +if not check_normalise(props): 1.349 + exit(1) 1.350 +compute(props)