UNPKG

50.8 kBJavaScriptView Raw
1import { LinearGradient } from './linear-gradient';
2import { Screen } from '../../platform';
3import { isDataURI, isFileOrResourcePath, layout } from '../../utils';
4import { ios as iosViewUtils } from '../utils';
5import { ImageSource } from '../../image-source';
6import { parse as cssParse } from '../../css-value';
7export * from './background-common';
8const clearCGColor = UIColor.clearColor.CGColor;
9const uriPattern = /url\(('|")(.*?)\1\)/;
10const symbolUrl = Symbol('backgroundImageUrl');
11export var CacheMode;
12(function (CacheMode) {
13 CacheMode[CacheMode["none"] = 0] = "none";
14})(CacheMode || (CacheMode = {}));
15export var ios;
16(function (ios) {
17 function createBackgroundUIColor(view, callback, flip) {
18 const background = view.style.backgroundInternal;
19 const nativeView = view.nativeViewProtected;
20 if (!nativeView) {
21 return;
22 }
23 // Unset this in case another layer handles background color (e.g. gradient)
24 nativeView.layer.backgroundColor = null;
25 // Cleanup of previous values
26 clearBackgroundVisualEffects(view);
27 // Borders, shadows, etc
28 drawBackgroundVisualEffects(view);
29 if (!background.image) {
30 callback(background?.color?.ios);
31 }
32 else {
33 if (!(background.image instanceof LinearGradient)) {
34 setUIColorFromImage(view, nativeView, callback, flip);
35 }
36 }
37 }
38 ios.createBackgroundUIColor = createBackgroundUIColor;
39 function drawBackgroundVisualEffects(view) {
40 const background = view.style.backgroundInternal;
41 const nativeView = view.nativeViewProtected;
42 const layer = nativeView.layer;
43 let needsLayerAdjustmentOnScroll = false;
44 // Add new gradient layer or update existing one
45 if (background.image instanceof LinearGradient) {
46 if (!nativeView.gradientLayer) {
47 nativeView.gradientLayer = CAGradientLayer.new();
48 layer.insertSublayerAtIndex(nativeView.gradientLayer, 0);
49 }
50 iosViewUtils.drawGradient(nativeView, nativeView.gradientLayer, background.image);
51 needsLayerAdjustmentOnScroll = true;
52 }
53 // Initialize clipping mask (usually for clip-path and non-uniform rounded borders)
54 maskLayerIfNeeded(nativeView, background);
55 if (background.hasUniformBorder()) {
56 const borderColor = background.getUniformBorderColor();
57 layer.borderColor = borderColor?.ios?.CGColor;
58 layer.borderWidth = layout.toDeviceIndependentPixels(background.getUniformBorderWidth());
59 layer.cornerRadius = getUniformBorderRadius(view, layer.bounds);
60 }
61 else {
62 drawNonUniformBorders(nativeView, background);
63 needsLayerAdjustmentOnScroll = true;
64 }
65 // Clip-path should be called after borders are applied
66 if (nativeView.maskType === iosViewUtils.LayerMask.CLIP_PATH && layer.mask instanceof CAShapeLayer) {
67 layer.mask.path = generateClipPath(view, layer.bounds);
68 }
69 if (background.hasBoxShadow()) {
70 drawBoxShadow(view);
71 needsLayerAdjustmentOnScroll = true;
72 }
73 if (needsLayerAdjustmentOnScroll) {
74 registerAdjustLayersOnScrollListener(view);
75 }
76 }
77 ios.drawBackgroundVisualEffects = drawBackgroundVisualEffects;
78 function clearBackgroundVisualEffects(view) {
79 const nativeView = view.nativeViewProtected;
80 if (!nativeView) {
81 return;
82 }
83 const background = view.style.backgroundInternal;
84 const hasGradientBackground = background.image && background.image instanceof LinearGradient;
85 // Remove mask if there is no clip path or non-uniform border with radius
86 let needsMask;
87 switch (nativeView.maskType) {
88 case iosViewUtils.LayerMask.BORDER:
89 needsMask = !background.hasUniformBorder() && background.hasBorderRadius();
90 break;
91 case iosViewUtils.LayerMask.CLIP_PATH:
92 needsMask = !!background.clipPath;
93 break;
94 default:
95 needsMask = false;
96 break;
97 }
98 if (!needsMask) {
99 clearLayerMask(nativeView);
100 }
101 // Clear box shadow if it's no longer needed
102 if (background.clearFlags & 2 /* BackgroundClearFlags.CLEAR_BOX_SHADOW */) {
103 clearBoxShadow(nativeView);
104 }
105 // Non-uniform borders cleanup
106 if (nativeView.hasNonUniformBorder) {
107 if (nativeView.hasNonUniformBorderColor && background.hasUniformBorderColor()) {
108 clearNonUniformColorBorders(nativeView);
109 }
110 if (background.hasUniformBorder()) {
111 clearNonUniformBorders(nativeView);
112 }
113 }
114 if (nativeView.gradientLayer && !hasGradientBackground) {
115 nativeView.gradientLayer.removeFromSuperlayer();
116 nativeView.gradientLayer = null;
117 }
118 // Force unset scroll listener
119 unregisterAdjustLayersOnScrollListener(view);
120 // Reset clear flags
121 background.clearFlags = 0 /* BackgroundClearFlags.NONE */;
122 }
123 ios.clearBackgroundVisualEffects = clearBackgroundVisualEffects;
124 function generateShadowLayerPaths(view, bounds) {
125 const background = view.style.backgroundInternal;
126 const nativeView = view.nativeViewProtected;
127 const layer = nativeView.layer;
128 const boxShadow = background.getBoxShadow();
129 const spreadRadius = layout.toDeviceIndependentPixels(boxShadow.spreadRadius);
130 const { width, height } = bounds.size;
131 let innerPath, shadowPath;
132 // Generate more detailed paths if view has border radius
133 if (background.hasBorderRadius()) {
134 if (background.hasUniformBorder()) {
135 const cornerRadius = layer.cornerRadius;
136 const cappedRadius = getBorderCapRadius(cornerRadius, width / 2, height / 2);
137 const cappedOuterRadii = {
138 topLeft: cappedRadius,
139 topRight: cappedRadius,
140 bottomLeft: cappedRadius,
141 bottomRight: cappedRadius,
142 };
143 const cappedOuterRadiiWithSpread = {
144 topLeft: cappedRadius + spreadRadius,
145 topRight: cappedRadius + spreadRadius,
146 bottomLeft: cappedRadius + spreadRadius,
147 bottomRight: cappedRadius + spreadRadius,
148 };
149 innerPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadii);
150 shadowPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadiiWithSpread, spreadRadius);
151 }
152 else {
153 const outerTopLeftRadius = layout.toDeviceIndependentPixels(background.borderTopLeftRadius);
154 const outerTopRightRadius = layout.toDeviceIndependentPixels(background.borderTopRightRadius);
155 const outerBottomRightRadius = layout.toDeviceIndependentPixels(background.borderBottomRightRadius);
156 const outerBottomLeftRadius = layout.toDeviceIndependentPixels(background.borderBottomLeftRadius);
157 const topRadii = outerTopLeftRadius + outerTopRightRadius;
158 const rightRadii = outerTopRightRadius + outerBottomRightRadius;
159 const bottomRadii = outerBottomRightRadius + outerBottomLeftRadius;
160 const leftRadii = outerBottomLeftRadius + outerTopLeftRadius;
161 const cappedOuterRadii = {
162 topLeft: getBorderCapRadius(outerTopLeftRadius, (outerTopLeftRadius / topRadii) * width, (outerTopLeftRadius / leftRadii) * height),
163 topRight: getBorderCapRadius(outerTopRightRadius, (outerTopRightRadius / topRadii) * width, (outerTopRightRadius / rightRadii) * height),
164 bottomLeft: getBorderCapRadius(outerBottomLeftRadius, (outerBottomLeftRadius / bottomRadii) * width, (outerBottomLeftRadius / leftRadii) * height),
165 bottomRight: getBorderCapRadius(outerBottomRightRadius, (outerBottomRightRadius / bottomRadii) * width, (outerBottomRightRadius / rightRadii) * height),
166 };
167 // Add spread radius to corners that actually have radius as shadow has grown larger
168 // than view itself and needs to be rounded accordingly
169 const cappedOuterRadiiWithSpread = {
170 topLeft: cappedOuterRadii.topLeft > 0 ? cappedOuterRadii.topLeft + spreadRadius : cappedOuterRadii.topLeft,
171 topRight: cappedOuterRadii.topRight > 0 ? cappedOuterRadii.topRight + spreadRadius : cappedOuterRadii.topRight,
172 bottomLeft: cappedOuterRadii.bottomLeft > 0 ? cappedOuterRadii.bottomLeft + spreadRadius : cappedOuterRadii.bottomLeft,
173 bottomRight: cappedOuterRadii.bottomRight > 0 ? cappedOuterRadii.bottomRight + spreadRadius : cappedOuterRadii.bottomRight,
174 };
175 innerPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadii);
176 shadowPath = generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadiiWithSpread, spreadRadius);
177 }
178 }
179 else {
180 innerPath = CGPathCreateWithRect(bounds, null);
181 shadowPath = CGPathCreateWithRect(CGRectInset(bounds, -spreadRadius, -spreadRadius), null);
182 }
183 return {
184 maskPath: generateShadowMaskPath(bounds, boxShadow, innerPath),
185 shadowPath,
186 };
187 }
188 ios.generateShadowLayerPaths = generateShadowLayerPaths;
189 function generateClipPath(view, bounds) {
190 const background = view.style.backgroundInternal;
191 const { origin, size } = bounds;
192 const position = {
193 left: origin.x,
194 top: origin.y,
195 bottom: size.height,
196 right: size.width,
197 };
198 if (position.right === 0 || position.bottom === 0) {
199 return;
200 }
201 let path;
202 const clipPath = background.clipPath;
203 const functionName = clipPath.substring(0, clipPath.indexOf('('));
204 const value = clipPath.replace(`${functionName}(`, '').replace(')', '');
205 switch (functionName) {
206 case 'rect':
207 path = rectPath(value, position);
208 break;
209 case 'inset':
210 path = insetPath(value, position);
211 break;
212 case 'circle':
213 path = circlePath(value, position);
214 break;
215 case 'ellipse':
216 path = ellipsePath(value, position);
217 break;
218 case 'polygon':
219 path = polygonPath(value, position);
220 break;
221 }
222 return path;
223 }
224 ios.generateClipPath = generateClipPath;
225 function getUniformBorderRadius(view, bounds) {
226 const background = view.style.backgroundInternal;
227 const { width, height } = bounds.size;
228 const cornerRadius = layout.toDeviceIndependentPixels(background.getUniformBorderRadius());
229 return Math.min(Math.min(width / 2, height / 2), cornerRadius);
230 }
231 ios.getUniformBorderRadius = getUniformBorderRadius;
232 function generateNonUniformBorderInnerClipRoundedPath(view, bounds) {
233 const background = view.style.backgroundInternal;
234 const cappedOuterRadii = calculateNonUniformBorderCappedRadii(bounds, background);
235 return generateNonUniformBorderInnerClipPath(bounds, background, cappedOuterRadii);
236 }
237 ios.generateNonUniformBorderInnerClipRoundedPath = generateNonUniformBorderInnerClipRoundedPath;
238 function generateNonUniformBorderOuterClipRoundedPath(view, bounds) {
239 const background = view.style.backgroundInternal;
240 const cappedOuterRadii = calculateNonUniformBorderCappedRadii(bounds, background);
241 return generateNonUniformBorderOuterClipPath(bounds, cappedOuterRadii);
242 }
243 ios.generateNonUniformBorderOuterClipRoundedPath = generateNonUniformBorderOuterClipRoundedPath;
244 function generateNonUniformMultiColorBorderRoundedPaths(view, bounds) {
245 const background = view.style.backgroundInternal;
246 return generateNonUniformMultiColorBorderPaths(bounds, background);
247 }
248 ios.generateNonUniformMultiColorBorderRoundedPaths = generateNonUniformMultiColorBorderRoundedPaths;
249})(ios || (ios = {}));
250function maskLayerIfNeeded(nativeView, background) {
251 const layer = nativeView.layer;
252 // Check if layer should be masked
253 if (!(layer.mask instanceof CAShapeLayer)) {
254 // Since layers can only accept up to a single mask at a time, clip path is given more priority
255 if (background.clipPath) {
256 nativeView.maskType = iosViewUtils.LayerMask.CLIP_PATH;
257 }
258 else if (!background.hasUniformBorder() && background.hasBorderRadius()) {
259 nativeView.maskType = iosViewUtils.LayerMask.BORDER;
260 }
261 else {
262 nativeView.maskType = null;
263 }
264 if (nativeView.maskType != null) {
265 nativeView.originalMask = layer.mask;
266 layer.mask = CAShapeLayer.new();
267 }
268 }
269}
270function clearLayerMask(nativeView) {
271 if (nativeView.outerShadowContainerLayer) {
272 nativeView.outerShadowContainerLayer.mask = null;
273 }
274 nativeView.layer.mask = nativeView.originalMask;
275 nativeView.originalMask = null;
276 nativeView.maskType = null;
277}
278function onBackgroundViewScroll(args) {
279 const view = args.object;
280 const nativeView = view.nativeViewProtected;
281 if (nativeView instanceof UIScrollView) {
282 adjustLayersForScrollView(nativeView);
283 }
284}
285function adjustLayersForScrollView(nativeView) {
286 // Compensates with transition for the background layers for scrolling in ScrollView based controls.
287 CATransaction.begin();
288 CATransaction.setDisableActions(true);
289 const offset = nativeView.contentOffset;
290 const transform = {
291 a: 1,
292 b: 0,
293 c: 0,
294 d: 1,
295 tx: offset.x,
296 ty: offset.y,
297 };
298 if (nativeView.layer.mask) {
299 nativeView.layer.mask.setAffineTransform(transform);
300 }
301 // Nested layers
302 if (nativeView.gradientLayer) {
303 nativeView.gradientLayer.setAffineTransform(transform);
304 }
305 if (nativeView.borderLayer) {
306 nativeView.borderLayer.setAffineTransform(transform);
307 }
308 if (nativeView.outerShadowContainerLayer) {
309 // Update bounds of shadow layer as it belongs to parent view
310 nativeView.outerShadowContainerLayer.bounds = nativeView.bounds;
311 nativeView.outerShadowContainerLayer.setAffineTransform(transform);
312 }
313 CATransaction.setDisableActions(false);
314 CATransaction.commit();
315}
316function unregisterAdjustLayersOnScrollListener(view) {
317 if (view.nativeViewProtected instanceof UIScrollView) {
318 view.off('scroll', onBackgroundViewScroll);
319 }
320}
321function registerAdjustLayersOnScrollListener(view) {
322 if (view.nativeViewProtected instanceof UIScrollView) {
323 view.off('scroll', onBackgroundViewScroll);
324 view.on('scroll', onBackgroundViewScroll);
325 adjustLayersForScrollView(view.nativeViewProtected);
326 }
327}
328function clearNonUniformColorBorders(nativeView) {
329 if (nativeView.borderLayer) {
330 nativeView.borderLayer.mask = null;
331 nativeView.borderLayer.sublayers = null;
332 }
333 nativeView.hasNonUniformBorderColor = false;
334}
335function clearNonUniformBorders(nativeView) {
336 if (nativeView.borderLayer) {
337 nativeView.borderLayer.removeFromSuperlayer();
338 nativeView.borderLayer = null;
339 }
340 nativeView.hasNonUniformBorder = false;
341}
342function setUIColorFromImage(view, nativeView, callback, flip) {
343 const frame = nativeView.frame;
344 const boundsWidth = view.scaleX ? frame.size.width / view.scaleX : frame.size.width;
345 const boundsHeight = view.scaleY ? frame.size.height / view.scaleY : frame.size.height;
346 if (!boundsWidth || !boundsHeight) {
347 return undefined;
348 }
349 const style = view.style;
350 const background = style.backgroundInternal;
351 let imageUri = background.image;
352 if (imageUri) {
353 const match = imageUri.match(uriPattern);
354 if (match && match[2]) {
355 imageUri = match[2];
356 }
357 }
358 let bitmap;
359 if (isDataURI(imageUri)) {
360 const base64Data = imageUri.split(',')[1];
361 if (base64Data !== undefined) {
362 const imageSource = ImageSource.fromBase64Sync(base64Data);
363 bitmap = imageSource && imageSource.ios;
364 }
365 }
366 else if (isFileOrResourcePath(imageUri)) {
367 const imageSource = ImageSource.fromFileOrResourceSync(imageUri);
368 bitmap = imageSource && imageSource.ios;
369 }
370 else if (imageUri.indexOf('http') !== -1) {
371 style[symbolUrl] = imageUri;
372 ImageSource.fromUrl(imageUri)
373 .then((r) => {
374 if (style && style[symbolUrl] === imageUri) {
375 uiColorFromImage(r.ios, view, callback, flip);
376 }
377 })
378 .catch(() => { });
379 }
380 uiColorFromImage(bitmap, view, callback, flip);
381}
382function parsePosition(pos) {
383 const values = cssParse(pos);
384 if (values.length === 2) {
385 return { x: values[0], y: values[1] };
386 }
387 if (values.length === 1) {
388 const center = { type: 'ident', string: 'center' };
389 if (values[0].type === 'ident') {
390 const val = values[0].string.toLocaleLowerCase();
391 // If you only one keyword is specified, the other value is "center"
392 if (val === 'left' || val === 'right') {
393 return { x: values[0], y: center };
394 }
395 else if (val === 'top' || val === 'bottom') {
396 return { x: center, y: values[0] };
397 }
398 else if (val === 'center') {
399 return { x: center, y: center };
400 }
401 }
402 else if (values[0].type === 'number') {
403 return { x: values[0], y: center };
404 }
405 }
406 return null;
407}
408function getDrawParams(image, background, width, height) {
409 if (!image) {
410 return null;
411 }
412 const res = {
413 repeatX: true,
414 repeatY: true,
415 posX: 0,
416 posY: 0,
417 };
418 // repeat
419 if (background.repeat) {
420 switch (background.repeat.toLowerCase()) {
421 case 'no-repeat':
422 res.repeatX = false;
423 res.repeatY = false;
424 break;
425 case 'repeat-x':
426 res.repeatY = false;
427 break;
428 case 'repeat-y':
429 res.repeatX = false;
430 break;
431 }
432 }
433 const imageSize = image.size;
434 let imageWidth = imageSize.width;
435 let imageHeight = imageSize.height;
436 // size
437 const size = background.size;
438 if (size) {
439 const values = cssParse(size);
440 if (values.length === 2) {
441 const vx = values[0];
442 const vy = values[1];
443 if (vx.unit === '%' && vy.unit === '%') {
444 imageWidth = (width * vx.value) / 100;
445 imageHeight = (height * vy.value) / 100;
446 res.sizeX = imageWidth;
447 res.sizeY = imageHeight;
448 }
449 else if (vx.type === 'number' && vy.type === 'number' && ((vx.unit === 'px' && vy.unit === 'px') || (vx.unit === '' && vy.unit === ''))) {
450 imageWidth = vx.value;
451 imageHeight = vy.value;
452 res.sizeX = imageWidth;
453 res.sizeY = imageHeight;
454 }
455 }
456 else if (values.length === 1 && values[0].type === 'ident') {
457 let scale = 0;
458 if (values[0].string === 'cover') {
459 scale = Math.max(width / imageWidth, height / imageHeight);
460 }
461 else if (values[0].string === 'contain') {
462 scale = Math.min(width / imageWidth, height / imageHeight);
463 }
464 if (scale > 0) {
465 imageWidth *= scale;
466 imageHeight *= scale;
467 res.sizeX = imageWidth;
468 res.sizeY = imageHeight;
469 }
470 }
471 }
472 // position
473 const position = background.position;
474 if (position) {
475 const v = parsePosition(position);
476 if (v) {
477 const spaceX = width - imageWidth;
478 const spaceY = height - imageHeight;
479 if (v.x.unit === '%' && v.y.unit === '%') {
480 res.posX = (spaceX * v.x.value) / 100;
481 res.posY = (spaceY * v.y.value) / 100;
482 }
483 else if (v.x.type === 'number' && v.y.type === 'number' && ((v.x.unit === 'px' && v.y.unit === 'px') || (v.x.unit === '' && v.y.unit === ''))) {
484 res.posX = v.x.value;
485 res.posY = v.y.value;
486 }
487 else if (v.x.type === 'ident' && v.y.type === 'ident') {
488 if (v.x.string.toLowerCase() === 'center') {
489 res.posX = spaceX / 2;
490 }
491 else if (v.x.string.toLowerCase() === 'right') {
492 res.posX = spaceX;
493 }
494 if (v.y.string.toLowerCase() === 'center') {
495 res.posY = spaceY / 2;
496 }
497 else if (v.y.string.toLowerCase() === 'bottom') {
498 res.posY = spaceY;
499 }
500 }
501 else if (v.x.type === 'number' && v.y.type === 'ident') {
502 if (v.x.unit === '%') {
503 res.posX = (spaceX * v.x.value) / 100;
504 }
505 else if (v.x.unit === 'px' || v.x.unit === '') {
506 res.posX = v.x.value;
507 }
508 if (v.y.string.toLowerCase() === 'center') {
509 res.posY = spaceY / 2;
510 }
511 else if (v.y.string.toLowerCase() === 'bottom') {
512 res.posY = spaceY;
513 }
514 }
515 }
516 }
517 return res;
518}
519function uiColorFromImage(img, view, callback, flip) {
520 const background = view.style.backgroundInternal;
521 const nativeView = view.nativeViewProtected;
522 if (!img || !nativeView) {
523 callback(background.color && background.color.ios);
524 return;
525 }
526 const frame = nativeView.frame;
527 const boundsWidth = view.scaleX ? frame.size.width / view.scaleX : frame.size.width;
528 const boundsHeight = view.scaleY ? frame.size.height / view.scaleY : frame.size.height;
529 const params = getDrawParams(img, background, boundsWidth, boundsHeight);
530 if (params.sizeX > 0 && params.sizeY > 0) {
531 const resizeRect = CGRectMake(0, 0, params.sizeX, params.sizeY);
532 UIGraphicsBeginImageContextWithOptions(resizeRect.size, false, 0.0);
533 img.drawInRect(resizeRect);
534 img = UIGraphicsGetImageFromCurrentImageContext();
535 UIGraphicsEndImageContext();
536 }
537 UIGraphicsBeginImageContextWithOptions(CGSizeFromString(`{${boundsWidth},${boundsHeight}}`), false, 0.0);
538 const context = UIGraphicsGetCurrentContext();
539 if (background.color && background.color.ios) {
540 CGContextSetFillColorWithColor(context, background.color.ios.CGColor);
541 CGContextFillRect(context, CGRectMake(0, 0, boundsWidth, boundsHeight));
542 }
543 if (!params.repeatX && !params.repeatY) {
544 img.drawAtPoint(CGPointMake(params.posX, params.posY));
545 }
546 else {
547 const w = params.repeatX ? boundsWidth : img.size.width;
548 const h = params.repeatY ? boundsHeight : img.size.height;
549 CGContextSetPatternPhase(context, CGSizeMake(params.posX, params.posY));
550 params.posX = params.repeatX ? 0 : params.posX;
551 params.posY = params.repeatY ? 0 : params.posY;
552 const patternRect = CGRectMake(params.posX, params.posY, w, h);
553 img.drawAsPatternInRect(patternRect);
554 }
555 const bkgImage = UIGraphicsGetImageFromCurrentImageContext();
556 UIGraphicsEndImageContext();
557 if (flip) {
558 const flippedImage = _flipImage(bkgImage);
559 callback(UIColor.alloc().initWithPatternImage(flippedImage));
560 }
561 else {
562 callback(UIColor.alloc().initWithPatternImage(bkgImage));
563 }
564}
565// Flipping the default coordinate system
566// https://developer.apple.com/library/ios/documentation/2DDrawing/Conceptual/DrawingPrintingiOS/GraphicsDrawingOverview/GraphicsDrawingOverview.html
567function _flipImage(originalImage) {
568 UIGraphicsBeginImageContextWithOptions(originalImage.size, false, 0.0);
569 const context = UIGraphicsGetCurrentContext();
570 CGContextSaveGState(context);
571 CGContextTranslateCTM(context, 0.0, originalImage.size.height);
572 CGContextScaleCTM(context, 1.0, -1.0);
573 originalImage.drawInRect(CGRectMake(0, 0, originalImage.size.width, originalImage.size.height));
574 CGContextRestoreGState(context);
575 const flippedImage = UIGraphicsGetImageFromCurrentImageContext();
576 UIGraphicsEndImageContext();
577 return flippedImage;
578}
579function cssValueToDeviceIndependentPixels(source, total) {
580 source = source.trim();
581 if (source.indexOf('px') !== -1) {
582 return layout.toDeviceIndependentPixels(parseFloat(source.replace('px', '')));
583 }
584 else if (source.indexOf('%') !== -1 && total > 0) {
585 return (parseFloat(source.replace('%', '')) / 100) * total;
586 }
587 else {
588 return parseFloat(source);
589 }
590}
591function getBorderCapRadius(a, b, c) {
592 return a && Math.min(a, Math.min(b, c));
593}
594function calculateNonUniformBorderCappedRadii(bounds, background) {
595 const { width, height } = bounds.size;
596 const { x, y } = bounds.origin;
597 const outerTopLeftRadius = layout.toDeviceIndependentPixels(background.borderTopLeftRadius);
598 const outerTopRightRadius = layout.toDeviceIndependentPixels(background.borderTopRightRadius);
599 const outerBottomRightRadius = layout.toDeviceIndependentPixels(background.borderBottomRightRadius);
600 const outerBottomLeftRadius = layout.toDeviceIndependentPixels(background.borderBottomLeftRadius);
601 const topRadii = outerTopLeftRadius + outerTopRightRadius;
602 const rightRadii = outerTopRightRadius + outerBottomRightRadius;
603 const bottomRadii = outerBottomRightRadius + outerBottomLeftRadius;
604 const leftRadii = outerBottomLeftRadius + outerTopLeftRadius;
605 const cappedOuterRadii = {
606 topLeft: getBorderCapRadius(outerTopLeftRadius, (outerTopLeftRadius / topRadii) * width, (outerTopLeftRadius / leftRadii) * height),
607 topRight: getBorderCapRadius(outerTopRightRadius, (outerTopRightRadius / topRadii) * width, (outerTopRightRadius / rightRadii) * height),
608 bottomLeft: getBorderCapRadius(outerBottomLeftRadius, (outerBottomLeftRadius / bottomRadii) * width, (outerBottomLeftRadius / leftRadii) * height),
609 bottomRight: getBorderCapRadius(outerBottomRightRadius, (outerBottomRightRadius / bottomRadii) * width, (outerBottomRightRadius / rightRadii) * height),
610 };
611 return cappedOuterRadii;
612}
613function drawNonUniformBorders(nativeView, background) {
614 const layer = nativeView.layer;
615 const layerBounds = layer.bounds;
616 layer.borderColor = null;
617 layer.borderWidth = 0;
618 layer.cornerRadius = 0;
619 const cappedOuterRadii = calculateNonUniformBorderCappedRadii(layerBounds, background);
620 if (nativeView.maskType === iosViewUtils.LayerMask.BORDER && layer.mask instanceof CAShapeLayer) {
621 layer.mask.path = generateNonUniformBorderOuterClipPath(layerBounds, cappedOuterRadii);
622 }
623 if (background.hasBorderWidth()) {
624 if (!nativeView.hasNonUniformBorder) {
625 nativeView.borderLayer = CAShapeLayer.new();
626 nativeView.borderLayer.fillRule = kCAFillRuleEvenOdd;
627 layer.addSublayer(nativeView.borderLayer);
628 nativeView.hasNonUniformBorder = true;
629 }
630 if (background.hasUniformBorderColor()) {
631 nativeView.borderLayer.fillColor = background.borderTopColor?.ios?.CGColor || UIColor.blackColor.CGColor;
632 nativeView.borderLayer.path = generateNonUniformBorderInnerClipPath(layerBounds, background, cappedOuterRadii);
633 }
634 else {
635 // Non-uniform borders need more layers in order to display multiple colors at the same time
636 let borderTopLayer, borderRightLayer, borderBottomLayer, borderLeftLayer;
637 if (!nativeView.hasNonUniformBorderColor) {
638 const maskLayer = CAShapeLayer.new();
639 maskLayer.fillRule = kCAFillRuleEvenOdd;
640 nativeView.borderLayer.mask = maskLayer;
641 borderTopLayer = CAShapeLayer.new();
642 borderRightLayer = CAShapeLayer.new();
643 borderBottomLayer = CAShapeLayer.new();
644 borderLeftLayer = CAShapeLayer.new();
645 nativeView.borderLayer.addSublayer(borderTopLayer);
646 nativeView.borderLayer.addSublayer(borderRightLayer);
647 nativeView.borderLayer.addSublayer(borderBottomLayer);
648 nativeView.borderLayer.addSublayer(borderLeftLayer);
649 nativeView.hasNonUniformBorderColor = true;
650 }
651 else {
652 borderTopLayer = nativeView.borderLayer.sublayers[0];
653 borderRightLayer = nativeView.borderLayer.sublayers[1];
654 borderBottomLayer = nativeView.borderLayer.sublayers[2];
655 borderLeftLayer = nativeView.borderLayer.sublayers[3];
656 }
657 const paths = generateNonUniformMultiColorBorderPaths(layerBounds, background);
658 borderTopLayer.fillColor = background.borderTopColor?.ios?.CGColor || UIColor.blackColor.CGColor;
659 borderTopLayer.path = paths[0];
660 borderRightLayer.fillColor = background.borderRightColor?.ios?.CGColor || UIColor.blackColor.CGColor;
661 borderRightLayer.path = paths[1];
662 borderBottomLayer.fillColor = background.borderBottomColor?.ios?.CGColor || UIColor.blackColor.CGColor;
663 borderBottomLayer.path = paths[2];
664 borderLeftLayer.fillColor = background.borderLeftColor?.ios?.CGColor || UIColor.blackColor.CGColor;
665 borderLeftLayer.path = paths[3];
666 // Clip inner area to create borders
667 if (nativeView.borderLayer.mask instanceof CAShapeLayer) {
668 nativeView.borderLayer.mask.path = generateNonUniformBorderInnerClipPath(layerBounds, background, cappedOuterRadii);
669 }
670 }
671 }
672}
673function calculateInnerBorderClipRadius(radius, insetX, insetY) {
674 const innerXRadius = Math.max(0, radius - insetX);
675 const innerYRadius = Math.max(0, radius - insetY);
676 const innerMaxRadius = Math.max(innerXRadius, innerYRadius);
677 return {
678 xRadius: innerXRadius,
679 yRadius: innerYRadius,
680 maxRadius: innerMaxRadius,
681 };
682}
683/**
684 * Generates a path that represents the rounded view area.
685 *
686 * @param bounds
687 * @param cappedRadii
688 * @param offset
689 * @returns
690 */
691export function generateNonUniformBorderOuterClipPath(bounds, cappedRadii, offset = 0) {
692 const { width, height } = bounds.size;
693 const { x, y } = bounds.origin;
694 const left = x - offset;
695 const top = y - offset;
696 const right = x + width + offset;
697 const bottom = y + height + offset;
698 const clipPath = CGPathCreateMutable();
699 CGPathMoveToPoint(clipPath, null, left + cappedRadii.topLeft, top);
700 CGPathAddArcToPoint(clipPath, null, right, top, right, top + cappedRadii.topRight, cappedRadii.topRight);
701 CGPathAddArcToPoint(clipPath, null, right, bottom, right - cappedRadii.bottomRight, bottom, cappedRadii.bottomRight);
702 CGPathAddArcToPoint(clipPath, null, left, bottom, left, bottom - cappedRadii.bottomLeft, cappedRadii.bottomLeft);
703 CGPathAddArcToPoint(clipPath, null, left, top, left + cappedRadii.topLeft, top, cappedRadii.topLeft);
704 CGPathCloseSubpath(clipPath);
705 return clipPath;
706}
707/**
708 * Generates a path that represents the area inside borders.
709 *
710 * @param bounds
711 * @param background
712 * @param cappedOuterRadii
713 * @returns
714 */
715function generateNonUniformBorderInnerClipPath(bounds, background, cappedOuterRadii) {
716 const { width, height } = bounds.size;
717 const { x, y } = bounds.origin;
718 const position = {
719 left: x,
720 top: y,
721 bottom: y + height,
722 right: x + width,
723 };
724 const borderTopWidth = Math.max(0, layout.toDeviceIndependentPixels(background.borderTopWidth));
725 const borderRightWidth = Math.max(0, layout.toDeviceIndependentPixels(background.borderRightWidth));
726 const borderBottomWidth = Math.max(0, layout.toDeviceIndependentPixels(background.borderBottomWidth));
727 const borderLeftWidth = Math.max(0, layout.toDeviceIndependentPixels(background.borderLeftWidth));
728 const borderVWidth = borderTopWidth + borderBottomWidth;
729 const borderHWidth = borderLeftWidth + borderRightWidth;
730 const cappedBorderTopWidth = borderTopWidth && borderTopWidth * Math.min(1, height / borderVWidth);
731 const cappedBorderRightWidth = borderRightWidth && borderRightWidth * Math.min(1, width / borderHWidth);
732 const cappedBorderBottomWidth = borderBottomWidth && borderBottomWidth * Math.min(1, height / borderVWidth);
733 const cappedBorderLeftWidth = borderLeftWidth && borderLeftWidth * Math.min(1, width / borderHWidth);
734 const clipPath = CGPathCreateMutable();
735 CGPathAddRect(clipPath, null, CGRectMake(x, y, width, height));
736 // Inner clip paths
737 if (cappedBorderTopWidth > 0 || cappedBorderLeftWidth > 0) {
738 CGPathMoveToPoint(clipPath, null, position.left + cappedOuterRadii.topLeft, position.top + cappedBorderTopWidth);
739 }
740 else {
741 CGPathMoveToPoint(clipPath, null, position.left, position.top);
742 }
743 if (cappedBorderTopWidth > 0 || cappedBorderRightWidth > 0) {
744 const { xRadius, yRadius, maxRadius } = calculateInnerBorderClipRadius(cappedOuterRadii.topRight, cappedBorderRightWidth, cappedBorderTopWidth);
745 const innerTopRightTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.right - cappedBorderRightWidth - xRadius, position.top + cappedBorderTopWidth + yRadius);
746 CGPathAddArc(clipPath, innerTopRightTransform, 0, 0, maxRadius, (Math.PI * 3) / 2, 0, false);
747 }
748 else {
749 CGPathAddLineToPoint(clipPath, null, position.right, position.top);
750 }
751 if (cappedBorderBottomWidth > 0 || cappedBorderRightWidth > 0) {
752 const { xRadius, yRadius, maxRadius } = calculateInnerBorderClipRadius(cappedOuterRadii.bottomRight, cappedBorderRightWidth, cappedBorderBottomWidth);
753 const innerBottomRightTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.right - cappedBorderRightWidth - xRadius, position.bottom - cappedBorderBottomWidth - yRadius);
754 CGPathAddArc(clipPath, innerBottomRightTransform, 0, 0, maxRadius, 0, Math.PI / 2, false);
755 }
756 else {
757 CGPathAddLineToPoint(clipPath, null, position.right, position.bottom);
758 }
759 if (cappedBorderBottomWidth > 0 || cappedBorderLeftWidth > 0) {
760 const { xRadius, yRadius, maxRadius } = calculateInnerBorderClipRadius(cappedOuterRadii.bottomLeft, cappedBorderLeftWidth, cappedBorderBottomWidth);
761 const innerBottomLeftTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.left + cappedBorderLeftWidth + xRadius, position.bottom - cappedBorderBottomWidth - yRadius);
762 CGPathAddArc(clipPath, innerBottomLeftTransform, 0, 0, maxRadius, Math.PI / 2, Math.PI, false);
763 }
764 else {
765 CGPathAddLineToPoint(clipPath, null, position.left, position.bottom);
766 }
767 if (cappedBorderTopWidth > 0 || cappedBorderLeftWidth > 0) {
768 const { xRadius, yRadius, maxRadius } = calculateInnerBorderClipRadius(cappedOuterRadii.topLeft, cappedBorderLeftWidth, cappedBorderTopWidth);
769 const innerTopLeftTransform = CGAffineTransformMake(maxRadius && xRadius / maxRadius, 0, 0, maxRadius && yRadius / maxRadius, position.left + cappedBorderLeftWidth + xRadius, position.top + cappedBorderTopWidth + yRadius);
770 CGPathAddArc(clipPath, innerTopLeftTransform, 0, 0, maxRadius, Math.PI, (Math.PI * 3) / 2, false);
771 }
772 else {
773 CGPathAddLineToPoint(clipPath, null, position.left, position.top);
774 }
775 CGPathCloseSubpath(clipPath);
776 return clipPath;
777}
778/**
779 * Calculates the needed widths for creating triangular shapes for each border.
780 * To achieve this, all border widths are scaled according to view bounds.
781 *
782 * @param bounds
783 * @param background
784 * @returns
785 */
786function getBorderTriangleWidths(bounds, background) {
787 const width = bounds.origin.x + bounds.size.width;
788 const height = bounds.origin.y + bounds.size.height;
789 const borderTopWidth = Math.max(0, layout.toDeviceIndependentPixels(background.borderTopWidth));
790 const borderRightWidth = Math.max(0, layout.toDeviceIndependentPixels(background.borderRightWidth));
791 const borderBottomWidth = Math.max(0, layout.toDeviceIndependentPixels(background.borderBottomWidth));
792 const borderLeftWidth = Math.max(0, layout.toDeviceIndependentPixels(background.borderLeftWidth));
793 const verticalBorderWidth = borderTopWidth + borderBottomWidth;
794 const horizontalBorderWidth = borderLeftWidth + borderRightWidth;
795 let verticalBorderMultiplier = verticalBorderWidth > 0 ? height / verticalBorderWidth : 0;
796 let horizontalBorderMultiplier = horizontalBorderWidth > 0 ? width / horizontalBorderWidth : 0;
797 // Both directions should consider each other in order to scale widths properly, as a view might have different width and height
798 if (verticalBorderMultiplier > 0 && verticalBorderMultiplier < horizontalBorderMultiplier) {
799 horizontalBorderMultiplier -= horizontalBorderMultiplier - verticalBorderMultiplier;
800 }
801 if (horizontalBorderMultiplier > 0 && horizontalBorderMultiplier < verticalBorderMultiplier) {
802 verticalBorderMultiplier -= verticalBorderMultiplier - horizontalBorderMultiplier;
803 }
804 return {
805 top: borderTopWidth * verticalBorderMultiplier,
806 right: borderRightWidth * horizontalBorderMultiplier,
807 bottom: borderBottomWidth * verticalBorderMultiplier,
808 left: borderLeftWidth * horizontalBorderMultiplier,
809 };
810}
811/**
812 * Generates paths for visualizing borders with different colors per side.
813 * This is achieved by extending all borders enough to consume entire view size,
814 * then using an even-odd inner mask to clip and eventually render borders according to their corresponding width.
815 *
816 * @param bounds
817 * @param background
818 * @returns
819 */
820function generateNonUniformMultiColorBorderPaths(bounds, background) {
821 const { width, height } = bounds.size;
822 const { x, y } = bounds.origin;
823 const position = {
824 left: x,
825 top: y,
826 bottom: y + height,
827 right: x + width,
828 };
829 const borderWidths = getBorderTriangleWidths(bounds, background);
830 const paths = new Array(4);
831 const lto = {
832 x: position.left,
833 y: position.top,
834 }; // left-top-outside
835 const lti = {
836 x: position.left + borderWidths.left,
837 y: position.top + borderWidths.top,
838 }; // left-top-inside
839 const rto = {
840 x: position.right,
841 y: position.top,
842 }; // right-top-outside
843 const rti = {
844 x: position.right - borderWidths.right,
845 y: position.top + borderWidths.top,
846 }; // right-top-inside
847 const rbo = {
848 x: position.right,
849 y: position.bottom,
850 }; // right-bottom-outside
851 const rbi = {
852 x: position.right - borderWidths.right,
853 y: position.bottom - borderWidths.bottom,
854 }; // right-bottom-inside
855 const lbo = {
856 x: position.left,
857 y: position.bottom,
858 }; // left-bottom-outside
859 const lbi = {
860 x: position.left + borderWidths.left,
861 y: position.bottom - borderWidths.bottom,
862 }; // left-bottom-inside
863 const borderTopColor = background.borderTopColor;
864 const borderRightColor = background.borderRightColor;
865 const borderBottomColor = background.borderBottomColor;
866 const borderLeftColor = background.borderLeftColor;
867 if (borderWidths.top > 0 && borderTopColor?.ios) {
868 const topBorderPath = CGPathCreateMutable();
869 CGPathMoveToPoint(topBorderPath, null, lto.x, lto.y);
870 CGPathAddLineToPoint(topBorderPath, null, rto.x, rto.y);
871 CGPathAddLineToPoint(topBorderPath, null, rti.x, rti.y);
872 if (rti.x !== lti.x) {
873 CGPathAddLineToPoint(topBorderPath, null, lti.x, lti.y);
874 }
875 CGPathAddLineToPoint(topBorderPath, null, lto.x, lto.y);
876 paths[0] = topBorderPath;
877 }
878 if (borderWidths.right > 0 && borderRightColor?.ios) {
879 const rightBorderPath = CGPathCreateMutable();
880 CGPathMoveToPoint(rightBorderPath, null, rto.x, rto.y);
881 CGPathAddLineToPoint(rightBorderPath, null, rbo.x, rbo.y);
882 CGPathAddLineToPoint(rightBorderPath, null, rbi.x, rbi.y);
883 if (rbi.y !== rti.y) {
884 CGPathAddLineToPoint(rightBorderPath, null, rti.x, rti.y);
885 }
886 CGPathAddLineToPoint(rightBorderPath, null, rto.x, rto.y);
887 paths[1] = rightBorderPath;
888 }
889 if (borderWidths.bottom > 0 && borderBottomColor?.ios) {
890 const bottomBorderPath = CGPathCreateMutable();
891 CGPathMoveToPoint(bottomBorderPath, null, rbo.x, rbo.y);
892 CGPathAddLineToPoint(bottomBorderPath, null, lbo.x, lbo.y);
893 CGPathAddLineToPoint(bottomBorderPath, null, lbi.x, lbi.y);
894 if (lbi.x !== rbi.x) {
895 CGPathAddLineToPoint(bottomBorderPath, null, rbi.x, rbi.y);
896 }
897 CGPathAddLineToPoint(bottomBorderPath, null, rbo.x, rbo.y);
898 paths[2] = bottomBorderPath;
899 }
900 if (borderWidths.left > 0 && borderLeftColor?.ios) {
901 const leftBorderPath = CGPathCreateMutable();
902 CGPathMoveToPoint(leftBorderPath, null, lbo.x, lbo.y);
903 CGPathAddLineToPoint(leftBorderPath, null, lto.x, lto.y);
904 CGPathAddLineToPoint(leftBorderPath, null, lti.x, lti.y);
905 if (lti.y !== lbi.y) {
906 CGPathAddLineToPoint(leftBorderPath, null, lbi.x, lbi.y);
907 }
908 CGPathAddLineToPoint(leftBorderPath, null, lbo.x, lbo.y);
909 paths[3] = leftBorderPath;
910 }
911 return paths;
912}
913function drawBoxShadow(view) {
914 const background = view.style.backgroundInternal;
915 const nativeView = view.nativeViewProtected;
916 const layer = nativeView.layer;
917 // There is no parent to add shadow to
918 if (!layer.superlayer) {
919 return;
920 }
921 const bounds = nativeView.bounds;
922 const boxShadow = background.getBoxShadow();
923 // Initialize outer shadows
924 let outerShadowContainerLayer;
925 if (nativeView.outerShadowContainerLayer) {
926 outerShadowContainerLayer = nativeView.outerShadowContainerLayer;
927 }
928 else {
929 outerShadowContainerLayer = CALayer.new();
930 // TODO: Make this dynamic when views get support for multiple shadows
931 const shadowLayer = CALayer.new();
932 // This mask is necessary to maintain transparent background
933 const maskLayer = CAShapeLayer.new();
934 maskLayer.fillRule = kCAFillRuleEvenOdd;
935 shadowLayer.mask = maskLayer;
936 outerShadowContainerLayer.addSublayer(shadowLayer);
937 // Instead of nesting it, add shadow container layer underneath view so that it's not affected by border masking
938 layer.superlayer.insertSublayerBelow(outerShadowContainerLayer, layer);
939 nativeView.outerShadowContainerLayer = outerShadowContainerLayer;
940 }
941 // Apply clip path to shadow
942 if (nativeView.maskType === iosViewUtils.LayerMask.CLIP_PATH && layer.mask instanceof CAShapeLayer) {
943 if (!outerShadowContainerLayer.mask) {
944 outerShadowContainerLayer.mask = CAShapeLayer.new();
945 }
946 if (outerShadowContainerLayer.mask instanceof CAShapeLayer) {
947 outerShadowContainerLayer.mask.path = layer.mask.path;
948 }
949 }
950 outerShadowContainerLayer.bounds = bounds;
951 outerShadowContainerLayer.anchorPoint = layer.anchorPoint;
952 outerShadowContainerLayer.position = nativeView.center;
953 outerShadowContainerLayer.zPosition = layer.zPosition;
954 // Inherit view visibility values
955 outerShadowContainerLayer.opacity = layer.opacity;
956 outerShadowContainerLayer.hidden = layer.hidden;
957 const outerShadowLayers = outerShadowContainerLayer.sublayers;
958 if (outerShadowLayers?.count) {
959 for (let i = 0, count = outerShadowLayers.count; i < count; i++) {
960 const shadowLayer = outerShadowLayers[i];
961 const shadowRadius = layout.toDeviceIndependentPixels(boxShadow.blurRadius);
962 const spreadRadius = layout.toDeviceIndependentPixels(boxShadow.spreadRadius);
963 const offsetX = layout.toDeviceIndependentPixels(boxShadow.offsetX);
964 const offsetY = layout.toDeviceIndependentPixels(boxShadow.offsetY);
965 const { maskPath, shadowPath } = ios.generateShadowLayerPaths(view, bounds);
966 shadowLayer.allowsEdgeAntialiasing = true;
967 shadowLayer.contentsScale = Screen.mainScreen.scale;
968 // Shadow opacity is handled on the shadow's color instance
969 shadowLayer.shadowOpacity = boxShadow.color?.a ? boxShadow.color.a / 255 : 1;
970 shadowLayer.shadowRadius = shadowRadius;
971 shadowLayer.shadowColor = boxShadow.color?.ios?.CGColor;
972 shadowLayer.shadowOffset = CGSizeMake(offsetX, offsetY);
973 // Apply spread radius by expanding shadow layer bounds (this has a nice glow with radii set to 0)
974 shadowLayer.shadowPath = shadowPath;
975 // A mask that ensures that view maintains transparent background
976 if (shadowLayer.mask instanceof CAShapeLayer) {
977 shadowLayer.mask.path = maskPath;
978 }
979 }
980 }
981}
982function clearBoxShadow(nativeView) {
983 if (nativeView.outerShadowContainerLayer) {
984 nativeView.outerShadowContainerLayer.removeFromSuperlayer();
985 nativeView.outerShadowContainerLayer = null;
986 }
987}
988/**
989 * Creates a mask that ensures no shadow will be displayed underneath transparent backgrounds.
990 *
991 * @param bounds
992 * @param boxShadow
993 * @param bordersClipPath
994 * @returns
995 */
996function generateShadowMaskPath(bounds, boxShadow, innerClipPath) {
997 const shadowRadius = layout.toDeviceIndependentPixels(boxShadow.blurRadius);
998 const spreadRadius = layout.toDeviceIndependentPixels(boxShadow.spreadRadius);
999 const offsetX = layout.toDeviceIndependentPixels(boxShadow.offsetX);
1000 const offsetY = layout.toDeviceIndependentPixels(boxShadow.offsetY);
1001 // This value has to be large enough to avoid clipping shadow halo effect
1002 const outerRectRadius = shadowRadius * 3 + spreadRadius;
1003 const maskPath = CGPathCreateMutable();
1004 // Proper clip position and size
1005 const outerRect = CGRectOffset(CGRectInset(bounds, -outerRectRadius, -outerRectRadius), offsetX, offsetY);
1006 CGPathAddPath(maskPath, null, innerClipPath);
1007 CGPathAddRect(maskPath, null, outerRect);
1008 return maskPath;
1009}
1010function rectPath(value, position) {
1011 const arr = value.split(/[\s]+/);
1012 const top = cssValueToDeviceIndependentPixels(arr[0], position.top);
1013 const right = cssValueToDeviceIndependentPixels(arr[1], position.right);
1014 const bottom = cssValueToDeviceIndependentPixels(arr[2], position.bottom);
1015 const left = cssValueToDeviceIndependentPixels(arr[3], position.left);
1016 return UIBezierPath.bezierPathWithRect(CGRectMake(left, top, right - left, bottom - top)).CGPath;
1017}
1018function insetPath(value, position) {
1019 const arr = value.split(/[\s]+/);
1020 let topString;
1021 let rightString;
1022 let bottomString;
1023 let leftString;
1024 if (arr.length === 1) {
1025 topString = rightString = bottomString = leftString = arr[0];
1026 }
1027 else if (arr.length === 2) {
1028 topString = bottomString = arr[0];
1029 rightString = leftString = arr[1];
1030 }
1031 else if (arr.length === 3) {
1032 topString = arr[0];
1033 rightString = leftString = arr[1];
1034 bottomString = arr[2];
1035 }
1036 else if (arr.length === 4) {
1037 topString = arr[0];
1038 rightString = arr[1];
1039 bottomString = arr[2];
1040 leftString = arr[3];
1041 }
1042 const top = cssValueToDeviceIndependentPixels(topString, position.bottom);
1043 const right = cssValueToDeviceIndependentPixels('100%', position.right) - cssValueToDeviceIndependentPixels(rightString, position.right);
1044 const bottom = cssValueToDeviceIndependentPixels('100%', position.bottom) - cssValueToDeviceIndependentPixels(bottomString, position.bottom);
1045 const left = cssValueToDeviceIndependentPixels(leftString, position.right);
1046 return UIBezierPath.bezierPathWithRect(CGRectMake(left, top, right - left, bottom - top)).CGPath;
1047}
1048function circlePath(value, position) {
1049 const arr = value.split(/[\s]+/);
1050 const radius = cssValueToDeviceIndependentPixels(arr[0], (position.right > position.bottom ? position.bottom : position.right) / 2);
1051 const y = cssValueToDeviceIndependentPixels(arr[2], position.bottom);
1052 const x = cssValueToDeviceIndependentPixels(arr[3], position.right);
1053 return UIBezierPath.bezierPathWithArcCenterRadiusStartAngleEndAngleClockwise(CGPointMake(x, y), radius, 0, 360, true).CGPath;
1054}
1055function ellipsePath(value, position) {
1056 const arr = value.split(/[\s]+/);
1057 const rX = cssValueToDeviceIndependentPixels(arr[0], position.right);
1058 const rY = cssValueToDeviceIndependentPixels(arr[1], position.bottom);
1059 const cX = cssValueToDeviceIndependentPixels(arr[3], position.right);
1060 const cY = cssValueToDeviceIndependentPixels(arr[4], position.bottom);
1061 const left = cX - rX;
1062 const top = cY - rY;
1063 const width = rX * 2;
1064 const height = rY * 2;
1065 return UIBezierPath.bezierPathWithOvalInRect(CGRectMake(left, top, width, height)).CGPath;
1066}
1067function polygonPath(value, position) {
1068 const path = CGPathCreateMutable();
1069 let firstPoint;
1070 const arr = value.split(/[,]+/);
1071 for (let i = 0; i < arr.length; i++) {
1072 const xy = arr[i].trim().split(/[\s]+/);
1073 const point = {
1074 x: cssValueToDeviceIndependentPixels(xy[0], position.right),
1075 y: cssValueToDeviceIndependentPixels(xy[1], position.bottom),
1076 };
1077 if (!firstPoint) {
1078 firstPoint = point;
1079 CGPathMoveToPoint(path, null, point.x, point.y);
1080 }
1081 CGPathAddLineToPoint(path, null, point.x, point.y);
1082 }
1083 CGPathAddLineToPoint(path, null, firstPoint.x, firstPoint.y);
1084 return path;
1085}
1086//# sourceMappingURL=background.ios.js.map
\No newline at end of file