简体   繁体   中英

draw embossed arc using core graphics

I am trying to implement a custom slider as shown in figure below.

在此输入图像描述

what I have done so far looks something like this

在此输入图像描述

please help me out for drawing the arc with such effect. my code is as below, what I am doing is drawing the arc using CGContextAddArc with line width kLineWidth.

- (void)drawThumbAtPoint:(CGPoint)sliderButtonCenterPoint inContext: (CGContextRef)context {
UIGraphicsPushContext(context);
CGContextBeginPath(context);

CGContextMoveToPoint(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y);

CGImageRef imageRef = [UIImage imageNamed:@"circle25.png"].CGImage;
CGRect rect = CGRectMake(sliderButtonCenterPoint.x - kThumbRadius, sliderButtonCenterPoint.y - kThumbRadius, kThumbRadius*2, kThumbRadius*2);
CGContextDrawImage(context, rect, imageRef);

//CGContextAddArc(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y, kThumbRadius, 0.0, 2*M_PI, NO);

CGContextFillPath(context);
UIGraphicsPopContext();
}

- (CGPoint)drawArcTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius inContext:(CGContextRef)context {
UIGraphicsPushContext(context);
CGContextBeginPath(context);

float angleFromTrack = translateValueFromSourceIntervalToDestinationInterval(track, self.minimumValue, self.maximumValue, 0, M_PI/3);// 2*M_PI

CGFloat startAngle = (4*M_PI)/3;
CGFloat endAngle = startAngle + angleFromTrack;

CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, NO);

CGPoint arcEndPoint = CGContextGetPathCurrentPoint(context);

CGContextStrokePath(context);   

UIGraphicsPopContext();

return arcEndPoint;
}

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGPoint middlePoint;
    middlePoint.x = self.bounds.origin.x + self.bounds.size.width/2;
middlePoint.y = self.bounds.origin.y + self.bounds.size.width;

CGContextSetLineWidth(context, kLineWidth);

CGFloat radius = [self sliderRadius];


[self.maximumTrackTintColor setStroke];         
[self drawArcTrack:self.maximumValue atPoint:middlePoint withRadius:radius inContext:context];          
[self.minimumTrackTintColor setStroke];         
self.thumbCenterPoint = [self drawArcTrack:self.value atPoint:middlePoint withRadius:radius inContext:context];     

[self.thumbTintColor setFill];
[self drawThumbAtPoint:self.thumbCenterPoint inContext:context];
}

Unless you are going to be changing the shape dynamically, you would probably be better off just creating the image in an image editor. I know it's easy to create that effect in Photoshop, Illustrator, or Fireworks.

That said, drawing an inner shadow like that with Core Graphics requires several steps:

  1. Clip to the shape (using eg CGContextClip or CGContextClipToMask ).
  2. Make a path or mask of everything but the shape.
  3. Set your shadow parameters (using CGContextSetShadowWithColor ).
  4. Fill the path or mask from step 2. This casts a shadow inside the shape, and only the shadow is drawn because you clipped to the shape in step 1.

If you do all of that correctly, you can get a nice result like this:

内部阴影的弧形屏幕截图

Here's the code I wrote to draw that. I wrote it in the drawRect: of a custom view subclass, but you can easily use this code to draw into any graphics context.

- (void)drawRect:(CGRect)rect {
    CGContextRef gc = UIGraphicsGetCurrentContext();

First, I create a path that's just an arc:

    static CGFloat const kArcThickness = 20.0f;
    CGRect arcBounds = CGRectInset(self.bounds, 10.0f, 10.0f);
    CGPoint arcCenter = CGPointMake(CGRectGetMidX(arcBounds), CGRectGetMidY(arcBounds));
    CGFloat arcRadius = 0.5f * (MIN(arcBounds.size.width, arcBounds.size.height) - kArcThickness);
    UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:arcRadius startAngle:-M_PI / 3.0 endAngle:-2.0 * M_PI / 3.0 clockwise:NO];

Next, I ask Core Graphics to make a new path that is the outline of the arc path. Note how I ask it for a stroke width of kArcThickness and round line caps:

    CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);

I also need the inverse of that path: a path that includes every point except the points in shape . It so happens (although I don't think it's documented) that CGContextCreateCopyByStrokingPath and CGPathAddRect draw in opposite directions. So if I copy shape and draw an enormous rectangle around it, the non-zero winding rule means that the new path will be the inverse of shape :

    CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
    CGPathAddRect(shapeInverse, NULL, CGRectInfinite);

Now I can actually start drawing. First, I'll fill in the shape with a light gray color:

    CGContextBeginPath(gc);
    CGContextAddPath(gc, shape);
    CGContextSetFillColorWithColor(gc, [UIColor colorWithWhite:.9 alpha:1].CGColor);
    CGContextFillPath(gc);

Next I actually perform the four steps I listed above. I have to save the graphics state so I can undo the clipping and shadow parameters when I'm done.

    CGContextSaveGState(gc); {

Step 1: clip to the shape:

        CGContextBeginPath(gc);
        CGContextAddPath(gc, shape);
        CGContextClip(gc);

Step 2: Well, I did this step already when I created shapeInverse .

Step 3: I set the shadow parameters:

        CGContextSetShadowWithColor(gc, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);

Step 4: I fill the inverse shape from step 2:

        CGContextBeginPath(gc);
        CGContextAddPath(gc, shapeInverse);
        CGContextFillPath(gc);

Now I restore the graphics state, which specifically restores the clipping path and unsets the shadow parameters.

    } CGContextRestoreGState(gc);

Finally, I'll stroke shape with a light gray to make the edge crisper:

    CGContextSetStrokeColorWithColor(gc, [UIColor colorWithWhite:.75 alpha:1].CGColor);
    CGContextSetLineWidth(gc, 1);
    CGContextSetLineJoin(gc, kCGLineCapRound);
    CGContextBeginPath(gc);
    CGContextAddPath(gc, shape);
    CGContextStrokePath(gc);

Of course I clean up when I'm done:

    CGPathRelease(shape);
    CGPathRelease(shapeInverse);
}

For more complex shapes, you can look at my answer here and my answer here .

Here's all the code together for easy copying:

- (void)drawRect:(CGRect)rect {
    CGContextRef gc = UIGraphicsGetCurrentContext();

    static CGFloat const kArcThickness = 20.0f;
    CGRect arcBounds = CGRectInset(self.bounds, 10.0f, 10.0f);
    CGPoint arcCenter = CGPointMake(CGRectGetMidX(arcBounds), CGRectGetMidY(arcBounds));
    CGFloat arcRadius = 0.5f * (MIN(arcBounds.size.width, arcBounds.size.height) - kArcThickness);
    UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:arcRadius startAngle:-M_PI / 3.0 endAngle:-2.0 * M_PI / 3.0 clockwise:NO];
    CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
    CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
    CGPathAddRect(shapeInverse, NULL, CGRectInfinite);

    CGContextBeginPath(gc);
    CGContextAddPath(gc, shape);
    CGContextSetFillColorWithColor(gc, [UIColor colorWithWhite:.9 alpha:1].CGColor);
    CGContextFillPath(gc);

    CGContextSaveGState(gc); {
        CGContextBeginPath(gc);
        CGContextAddPath(gc, shape);
        CGContextClip(gc);
        CGContextSetShadowWithColor(gc, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
        CGContextBeginPath(gc);
        CGContextAddPath(gc, shapeInverse);
        CGContextFillPath(gc);
    } CGContextRestoreGState(gc);

    CGContextSetStrokeColorWithColor(gc, [UIColor colorWithWhite:.75 alpha:1].CGColor);
    CGContextSetLineWidth(gc, 1);
    CGContextSetLineJoin(gc, kCGLineCapRound);
    CGContextBeginPath(gc);
    CGContextAddPath(gc, shape);
    CGContextStrokePath(gc);

    CGPathRelease(shape);
    CGPathRelease(shapeInverse);
}

check the below code for fully functional version of the above component, its working (may be its bit messy).

#import "UIArcSlider.h"

@interface UIArcSlider()

@property (nonatomic) CGPoint thumbCenterPoint;

#pragma mark - Init and Setup methods
- (void)setup;

#pragma mark - Thumb management methods
- (BOOL)isPointInThumb:(CGPoint)point;

#pragma mark - Drawing methods
- (CGFloat)sliderRadius;
- (void)drawThumbAtPoint:(CGPoint)sliderButtonCenterPoint inContext:(CGContextRef)context;
- (CGPoint)drawTheArcTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius AndColor:(Boolean) isWhite inContext:(CGContextRef)context;
- (CGPoint)drawTheLineTrack:(float)track withColor:(Boolean) isWhite inContext:(CGContextRef)context;

@end

#pragma mark -
@implementation UIArcSlider

@synthesize sliderStyle = _sliderStyle;
- (void)setSliderStyle:(UISliderStyle)sliderStyle {
    if (sliderStyle != _sliderStyle) {
        _sliderStyle = sliderStyle;
        [self setNeedsDisplay];
    }
}

@synthesize value = _value;
- (void)setValue:(float)value {
    if (value != _value) {
        if (value > self.maximumValue) { value = self.maximumValue; }
        if (value < self.minimumValue) { value = self.minimumValue; }
        _value = value;
        [self setNeedsDisplay];
        [self sendActionsForControlEvents:UIControlEventValueChanged];
    }
}
@synthesize minimumValue = _minimumValue;
- (void)setMinimumValue:(float)minimumValue {
    if (minimumValue != _minimumValue) {
        _minimumValue = minimumValue;
        if (self.maximumValue < self.minimumValue)  { self.maximumValue = self.minimumValue; }
        if (self.value < self.minimumValue)         { self.value = self.minimumValue; }
    }
}
@synthesize maximumValue = _maximumValue;
- (void)setMaximumValue:(float)maximumValue {
    if (maximumValue != _maximumValue) {
        _maximumValue = maximumValue;
        if (self.minimumValue > self.maximumValue)  { self.minimumValue = self.maximumValue; }
        if (self.value > self.maximumValue)         { self.value = self.maximumValue; }
    }
}

@synthesize thumbCenterPoint = _thumbCenterPoint;

/** @name Init and Setup methods */
#pragma mark - Init and Setup methods
- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self setup];
    } 
    return self;
}
- (void)awakeFromNib {
    [self setup];
}

- (void)setup {
    self.value = 0.0;
    self.minimumValue = 0.0;
    self.maximumValue = 100.0;
    self.thumbCenterPoint = CGPointZero;

    UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureHappened:)];
    [self addGestureRecognizer:tapGestureRecognizer];

    UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureHappened:)];
    panGestureRecognizer.maximumNumberOfTouches = panGestureRecognizer.minimumNumberOfTouches;
    [self addGestureRecognizer:panGestureRecognizer];
}

/** @name Drawing methods */
#pragma mark - Drawing methods
#define kLineWidth 22.0
#define kThumbRadius 20.0
#define kPadding 20.0

- (CGFloat)sliderRadius {
    CGFloat radius = self.bounds.size.width;
    radius -= MAX(kLineWidth + kPadding, kThumbRadius  + kPadding); 
    return radius;
}

-(CGFloat)sliderWidth {    
    return self.bounds.size.width - kThumbRadius*2;
}

- (void)drawThumbAtPoint:(CGPoint)sliderButtonCenterPoint inContext:(CGContextRef)context {
    UIGraphicsPushContext(context);
    CGContextBeginPath(context);    

    CGContextMoveToPoint(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y);

    CGRect rect = CGRectMake(sliderButtonCenterPoint.x - kThumbRadius, sliderButtonCenterPoint.y - kThumbRadius, kThumbRadius*2, kThumbRadius*2);
    CGImageRef imageRef = [UIImage imageNamed:@"circle25.png"].CGImage;

    CGContextDrawImage(context, rect, imageRef);

    UIGraphicsPopContext();        
}

- (CGPoint)drawTheArcTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius AndColor:(Boolean) isWhite inContext:(CGContextRef)context 
{
    static CGFloat const kArcThickness = kLineWidth;
    CGPoint arcCenter = center;
    CGFloat arcRadius = radius;

    float angleFromTrack = translateValueFromSourceIntervalToDestinationInterval(track, self.minimumValue, self.maximumValue, 0, M_PI/3);// 2*M_PI

    CGFloat startAngle = (4*M_PI)/3;
    CGFloat endAngle = startAngle + angleFromTrack;

    UIBezierPath *arc = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:arcRadius startAngle:startAngle endAngle:endAngle clockwise:YES];

    CGPathRef shape = CGPathCreateCopyByStrokingPath(arc.CGPath, NULL, kArcThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
    CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
    CGPathAddRect(shapeInverse, NULL, CGRectInfinite);

    CGPoint arcEndPoint = [arc currentPoint];

    CGContextBeginPath(context);
    CGContextAddPath(context, shape);

    if (isWhite) {
        CGContextSetFillColorWithColor(context, [UIColor colorWithWhite:.9 alpha:1].CGColor);
    } else {//70,172, 220
        CGContextSetFillColorWithColor(context, [UIColor colorWithRed:70/255.0 green:172/255.0 blue:220/255.0 alpha:1].CGColor);
    }    

    CGContextFillPath(context);

    CGContextSaveGState(context); {
        CGContextBeginPath(context);
        CGContextAddPath(context, shape);
        CGContextClip(context);
        if (isWhite) {
            CGContextSetShadowWithColor(context, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
        } else {
            CGContextSetShadowWithColor(context, CGSizeZero, 7, [UIColor colorWithRed:85/255.0 green:183/255.0 blue:230/255.0 alpha:.25].CGColor);
        }       
        CGContextBeginPath(context);
        CGContextAddPath(context, shapeInverse);
        CGContextFillPath(context);
    } CGContextRestoreGState(context);

    if (isWhite) {
        CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:.75 alpha:1].CGColor);
    } else {
        CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:62/255.0 green:135/255.0 blue:169/255.0 alpha:1].CGColor);
    }

    CGContextSetLineWidth(context, 1);
    CGContextSetLineJoin(context, kCGLineCapRound);
    CGContextBeginPath(context);
    CGContextAddPath(context, shape);
    CGContextStrokePath(context);

    CGPathRelease(shape);
    CGPathRelease(shapeInverse);        

    return arcEndPoint;    
}


- (CGPoint)drawTheLineTrack:(float)track withColor:(Boolean) isWhite inContext:(CGContextRef)context
{        
    CGFloat sliderWidth = [self sliderWidth];
    CGFloat xStart = self.bounds.origin.x + (self.bounds.size.width - sliderWidth)/2;
    CGFloat lineRectX = (self.bounds.size.width - sliderWidth)/2;
    CGFloat lineThickness = kLineWidth/2;
    CGFloat lineRectY = (self.bounds.size.height - lineThickness)/2;

    CGFloat xPoint = translateValueToPoint(track, xStart, self.maximumValue, sliderWidth);
    sliderWidth = xPoint;

    CGRect lineRect = CGRectMake(lineRectX, lineRectY, sliderWidth, lineThickness);
    UIBezierPath *line = [UIBezierPath bezierPathWithRoundedRect:lineRect cornerRadius:lineThickness/2];

    CGPathRef shape = CGPathCreateCopyByStrokingPath(line.CGPath, NULL, lineThickness, kCGLineCapRound, kCGLineJoinRound, 10.0f);
    CGMutablePathRef shapeInverse = CGPathCreateMutableCopy(shape);
    CGPathAddRect(shapeInverse, NULL, CGRectInfinite);

    CGPoint arcEndPoint = CGPointMake(lineRectX + xPoint, lineRectY + (lineThickness/2));

    CGContextBeginPath(context);
    CGContextAddPath(context, shape);

    if (isWhite) {
        CGContextSetFillColorWithColor(context, [UIColor colorWithWhite:.9 alpha:1].CGColor);
    } else {//70,172, 220
        CGContextSetFillColorWithColor(context, [UIColor colorWithRed:70/255.0 green:172/255.0 blue:220/255.0 alpha:1].CGColor);
    }    

    CGContextFillPath(context);

    CGContextSaveGState(context); {
        CGContextBeginPath(context);
        CGContextAddPath(context, shape);
        CGContextClip(context);
        if (isWhite) {
            CGContextSetShadowWithColor(context, CGSizeZero, 7, [UIColor colorWithWhite:0 alpha:.25].CGColor);
        } else {
            CGContextSetShadowWithColor(context, CGSizeZero, 7, [UIColor colorWithRed:85/255.0 green:183/255.0 blue:230/255.0 alpha:.25].CGColor);
        }       
        CGContextBeginPath(context);
        CGContextAddPath(context, shapeInverse);
        CGContextFillPath(context);
    } CGContextRestoreGState(context);

    if (isWhite) {
        CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:.75 alpha:1].CGColor);
    } else {
        CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:62/255.0 green:135/255.0 blue:169/255.0 alpha:1].CGColor);
    }

    CGRect outlineRect = CGRectMake(lineRectX - (lineThickness/2), lineRectY - (lineThickness/2), sliderWidth + lineThickness, lineThickness * 2);
    UIBezierPath *outline = [UIBezierPath bezierPathWithRoundedRect:outlineRect cornerRadius:lineThickness];
    CGContextSetLineWidth(context, 1);
    CGContextSetLineJoin(context, kCGLineCapSquare);
    CGContextBeginPath(context);
    CGContextAddPath(context, outline.CGPath);
    CGContextStrokePath(context);

    CGPathRelease(shape);
    CGPathRelease(shapeInverse);        

    return arcEndPoint;    
}

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGPoint middlePoint;

    switch (self.sliderStyle) {
        case UISliderStyleLine:

            [self drawTheLineTrack:self.maximumValue withColor:YES inContext:context];
            self.thumbCenterPoint = [self drawTheLineTrack:self.value withColor:NO inContext:context];

            [self drawThumbAtPoint:self.thumbCenterPoint inContext:context];
            break;

        case UISliderStyleArc:
        default:
            middlePoint.x = self.bounds.origin.x + self.bounds.size.width/2;
            middlePoint.y = self.bounds.origin.y + self.bounds.size.width;

            CGFloat radius = [self sliderRadius];                
            [self drawTheArcTrack:self.maximumValue atPoint:middlePoint withRadius:radius AndColor:YES inContext:context];

            self.thumbCenterPoint = [self drawTheArcTrack:self.value atPoint:middlePoint withRadius:radius AndColor:NO inContext:context];  

            [self drawThumbAtPoint:self.thumbCenterPoint inContext:context];
            break;
    }
}

/** @name Thumb management methods */
#pragma mark - Thumb management methods
- (BOOL)isPointInThumb:(CGPoint)point {
    CGRect thumbTouchRect = CGRectMake(self.thumbCenterPoint.x - kThumbRadius, self.thumbCenterPoint.y - kThumbRadius, kThumbRadius*2, kThumbRadius*2);
    return CGRectContainsPoint(thumbTouchRect, point);
}

/** @name UIGestureRecognizer management methods */
#pragma mark - UIGestureRecognizer management methods
- (void)panGestureHappened:(UIPanGestureRecognizer *)panGestureRecognizer {
    CGPoint tapLocation = [panGestureRecognizer locationInView:self];
    switch (panGestureRecognizer.state) {
        case UIGestureRecognizerStateChanged: {

            CGFloat radius;
            CGPoint sliderCenter;
            CGPoint sliderStartPoint;
            CGFloat angle;


            CGFloat xStart;
            CGFloat maximumValue;
            CGFloat sliderWidth;
            CGFloat point;

            switch (self.sliderStyle) {
                case UISliderStyleLine:

                    sliderWidth = [self sliderWidth];
                    xStart = self.bounds.origin.x + (self.bounds.size.width - sliderWidth)/2;
                    maximumValue = self.maximumValue;
                    point = tapLocation.x;

                    self.value = translatePointToValue(point, xStart, maximumValue, sliderWidth);

                    break;

                case UISliderStyleArc:
                default:

                    radius = [self sliderRadius];
                    sliderCenter = CGPointMake(self.bounds.size.width/2, self.bounds.size.width);
                    sliderStartPoint = CGPointMake(sliderCenter.x - (radius * 1.15), sliderCenter.y - (radius * 2.1) );
                    angle = angleBetweenThreePoints(sliderCenter, sliderStartPoint, tapLocation);

                    if (angle < 0) {
                        angle = -angle;
                    }
                    else {
                        angle = 0.0;
                        //angle = M_PI/3 - angle;
                    }

                    self.value = translateValueFromSourceIntervalToDestinationInterval(angle, 0, M_PI/3, self.minimumValue, self.maximumValue);
                    break;
            }
        }
        default:
            break;
    }
}
- (void)tapGestureHappened:(UITapGestureRecognizer *)tapGestureRecognizer {
    if (tapGestureRecognizer.state == UIGestureRecognizerStateEnded) {
        CGPoint tapLocation = [tapGestureRecognizer locationInView:self];
        if ([self isPointInThumb:tapLocation]) {
            // do something on tap
        }
        else {
        }
    }
}

@end

/** @name Utility Functions */
#pragma mark - Utility Functions
float translateValueFromSourceIntervalToDestinationInterval(float sourceValue, float sourceIntervalMinimum, float sourceIntervalMaximum, float destinationIntervalMinimum, float destinationIntervalMaximum) {
    float a, b, destinationValue;

    a = (destinationIntervalMaximum - destinationIntervalMinimum) / (sourceIntervalMaximum - sourceIntervalMinimum);
    b = destinationIntervalMaximum - a*sourceIntervalMaximum;

    destinationValue = a*sourceValue + b;

    return destinationValue;
}

float translateValueToPoint(float value, float xStart, float maximum, float width)
{
    float point = (width * value) / maximum;
//    
//    point = xStart + point;
//    
    return point;   
}

float translatePointToValue(float point, float xStart, float maximum, float width)
{
    float value = (point) * maximum;
    value = value / width;

    return value;
}


CGFloat angleBetweenThreePoints(CGPoint centerPoint, CGPoint p1, CGPoint p2) {
    CGPoint v1 = CGPointMake(p1.x - centerPoint.x, p1.y - centerPoint.y);
    CGPoint v2 = CGPointMake(p2.x - centerPoint.x, p2.y - centerPoint.y);

    CGFloat angle = atan2f(v2.x*v1.y - v1.x*v2.y, v1.x*v2.x + v1.y*v2.y);

    return angle;
}

You'll require the thumb image as well 在此输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM