2018-11-07 21:22:45 renzibei 阅读数 260
  • NinePatch图片制作从入门到精通

    在Android应用开发当中经常需要使用图片,而图片又经常需要进行拉申,图片拉申的时候会出现变形,制作NinePatch图片可以指定图片的拉申区域,使图片拉申后还能获得比较好的效果。

    14067 人正在学习 去看看 戴振良

OpenCV iOS 上颜色的坑 drawContours 等draw操作

ios上相机画面draw的坑

因为实验适配移动端,然后在ios相机获得的Mat上使用drawContours, 发现轮廓总是白的,原来自己定义的颜色有问题,ios相机获得了四通道画面。
我的color是

Scalar color = Scalar(rand()%255, rand()%255, rand()%255);

但是画出来无论是轮廓还是形状都是白的。
google了半天关于这方面的回答也不多。不过总算看到一个相近的回答指出了核心,那就是opencv iOS camera获得的为四通道图像,不能用三通道的标量颜色去搞,要换成四通道的,把alpha通道赋成不透明255。
因此代码变为

Scalar color = Scalar(rand()%255, rand()%255, rand()%255, 255);

这样以后画轮廓或形状就都有颜色了。

2016-01-26 15:24:31 iOS_wq 阅读数 827
  • NinePatch图片制作从入门到精通

    在Android应用开发当中经常需要使用图片,而图片又经常需要进行拉申,图片拉申的时候会出现变形,制作NinePatch图片可以指定图片的拉申区域,使图片拉申后还能获得比较好的效果。

    14067 人正在学习 去看看 戴振良

iOS绘图基础(draw)

先说几个基本概念:

  • context:上下文,ios绘图的方法都需要传一个上下文context,这个context在重写uiview的drawRect的方法里调用UIGraphicsGetCurrentContext()获取
  • path:路径,ios绘图可以想象为你拿着一支笔去画图,画几条线或几个点从而形成一个路径,之后可以利用理解去填色或者描边
  • stroke,fill 描边和填充,每个路径都需要填充或者描边后才能在视图中看见,他们都各自有很多样式可以设置,常见的有颜色、粗细、渐变,连接样式等等。
  • 画图可以使用默认路径画,或者单独创建path画图,对应画图的api并不完全相同,是两组名称相似的api,两组pi常用的方法如下

CGContextMoveToPoint设置起点
CGContextClosePath 连接起点和当前点
CGPathCreateMutable 类似于 CGContextBeginPath
CGPathMoveToPoint 类似于 CGContextMoveToPoint
CGPathAddLineToPoint 类似于 CGContextAddLineToPoint
CGPathAddCurveToPoint 类似于 CGContextAddCurveToPoint
CGPathAddEllipseInRect 类似于 CGContextAddEllipseInRect
CGPathAddArc 类似于 CGContextAddArc
CGPathAddRect 类似于 CGContextAddRect
CGPathCloseSubpath 类似于 CGContextClosePath
CGContextAddPath函数把一个路径添加到graphics

画图步骤

1:获取context,

2:设置路径

CGContextStrokePath(ctx); //描出路径
CGContextFillPath(ctx)  使用非零绕数规则填充当前路径
CGContextDrawPath    两个参数决定填充规则,kCGPathFill表示用非零绕数规则,kCGPathEOFill表示用奇偶规则,kCGPathFillStroke表示填充,kCGPathEOFillStroke表示描线,不是填充
CGContextEOFillPath  使用奇偶规则填充当前路径
CGContextFillRect    填充指定的矩形
CGContextFillRects   填充指定的一些矩形
CGContextFillEllipseInRect   填充指定矩形中的椭圆

3:填充或描边路径

关于填充颜色 填充颜色有3种模式,分别是1:填充笔触,就是只给路径描边,2:根据路径填充颜色 3:填充笔触和颜色。填充颜色也分为非零绕数规则和奇偶规则,这个概念比较复杂难以解释,大家可以百度看看或者花几个图试试就明白。

几种基础绘图的代码

1.准备工作

新建一个继承于UIView的类, 重写-(void)drawRect:(CGRect)rect; 方法

// 重写drawRect
- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
    
    // 1.获取上下文
    CGContextRef contextRef = UIGraphicsGetCurrentContext();
    /**********2.设置画图相关样式参数*********/
    
    // 设置笔触的颜色
    CGContextSetStrokeColorWithColor(contextRef, [UIColor blueColor].CGColor); // 设置颜色有很多方法,推荐这个
    
    // 设置笔触的宽度
    CGContextSetLineWidth(contextRef, 2);
    
    // 设置填充色
    CGContextSetFillColorWithColor(contextRef, [UIColor purpleColor].CGColor);
    
    // 设置拐点样式
    /**
     *  enum CGLineJoin {
     *      kCGLineJoinMiter, //尖的,斜接
     *      kCGLineJoinRound, //圆
     *      kCGLineJoinBevel //斜面
     *  }
     */
    CGContextSetLineJoin(contextRef, kCGLineJoinRound);
    
    // 设置线两端的样式
    /**
     *  enum CGLineCap {
     *      kCGLineCapButt,
     *      kCGLineCapRound,
     *      kCGLineCapSquare
     *  };
     */
    CGContextSetLineCap(contextRef, kCGLineCapRound);
    
    // 设置虚线线条的样式
//    CGFloat lengths[] = {10, 10};
//    CGContextSetLineDash(contextRef, 0, lengths, 2);
    
    /**********设置画图相关样式参数*********/

    /**********3.开始画图*********/

    // 画线
    [self drawline:contextRef];
    
    // 画圆,圆弧,贝塞尔曲线
    [self drawCircle:contextRef];
    
    // 画矩形,画椭圆,多边形
    [self drawShape:contextRef];
    
    // 画文字
    [self drawText:contextRef];
    
    // 画图片
    [self drawPicture:contextRef];
    
    /**********结束画图*********/

}

2.画线

第一个方法我写的比较详细,写了使用path的方式和直接画线的方式。推荐使用path的方式画线。 另外,第一个方法也写了移动笔触画线和用点集合画线。后面方法只会涉及其中一种,因为方法都比较类似。

- (void)drawline:(CGContextRef)contextRef
{
    // 画一条简单的线
    CGPoint points1[] = {CGPointMake(10, 30), CGPointMake(300, 30)};
    CGContextAddLines(contextRef, points1, 2);
    
    
    // 画线方法1, 使用CGContextAddLineToPoint添加点画线,需要先设置一个起始点
    // 设置起始点
    CGContextMoveToPoint(contextRef, 50, 50);
    // 添加一个点
    CGContextAddLineToPoint(contextRef, 100, 50);
    // 在添加一个点,变成折现
    CGContextAddLineToPoint(contextRef, 150, 100);
    // 在添加一个点,再折一下
    CGContextAddLineToPoint(contextRef, 280, 100);
    
    
    // 画线方法2
    // 构造线路的点数组
    CGPoint points2[] = {CGPointMake(50, 120), CGPointMake(100, 150), CGPointMake(150, 120), CGPointMake(200, 150), CGPointMake(250, 120)};
    CGContextAddLines(contextRef, points2, 5); // 最后一个参数为要绘制点的个数
    
    
    // 画线方法3
    // 利用路径去画一组点(推荐使用路径的方式,虽然多了几行代码,带上逻辑更加清晰了)
    // 路径1
    CGMutablePathRef path1 = CGPathCreateMutable();
    CGPathMoveToPoint(path1, &CGAffineTransformIdentity, 30, 180); // 起始点, CGAffineTransformIdentity 类似于初始化一些参数
    CGPathAddLineToPoint(path1, &CGAffineTransformIdentity, 30, 220); // 添加一个点
    CGPathAddLineToPoint(path1, &CGAffineTransformIdentity, 80, 220); // 添加一个点
    // 将路径1加入contextRef
    CGContextAddPath(contextRef, path1);
    // path同样有方法CGPathAddLines()添加一组路径,和CGContextAddLines()差不多
    
    /******** 上面的方法都要写下面这两句代码 ********/
    // 描出笔触
    CGContextStrokePath(contextRef);
    // 填充
    CGContextFillPath(contextRef);
}

3. 画圆,圆弧,贝塞尔曲线

画圆和圆弧是一回事,只是起点和重点位置不同,画圆画弧线主要依赖于这几个方法CGContextAddArc,CGContextAddArcToPoint,CGContextAddCurveToPoint,CGContextAddQuadCurveToPoint后面两个方法是贝塞尔二次曲线和三次曲线

- (void)drawCircle:(CGContextRef)contextRef
{
    // 画笔改成紫色,便于区别
    CGContextSetStrokeColorWithColor(contextRef, [UIColor greenColor].CGColor);
    
    /** 绘制路径 方法一
     *  void CGContextAddArc (
     *      CGContextRef c,
     *      CGFloat x,             //圆心的x坐标
     *      CGFloat y,    //圆心的x坐标
     *      CGFloat radius,   //圆的半径
     *      CGFloat startAngle,    //开始弧度
     *      CGFloat endAngle,   //结束弧度
     *      int clockwise          //0表示顺时针,1表示逆时针
     *  );
     */
    
    // 圆
    CGContextAddArc(contextRef, 100, 100, 50, 0, M_PI*2, 0);
    CGContextStrokePath(contextRef);
    
    // 半圆
    CGContextAddArc(contextRef, 100, 200, 50, M_PI, M_PI*2, 0);
    CGContextStrokePath(contextRef);
    
    /**
     *  绘制路径 方法二,这个方法适合绘制弧度,端点p1和p2是弧线的控制点,类似photoshop中的钢笔工具控制曲线,还不明白请去了解贝塞尔曲线
     *  void CGContextAddArcToPoint(
     *      CGContextRef c,
     *      CGFloat x1,  //端点1的x坐标
     *      CGFloat y1,  //端点1的y坐标
     *      CGFloat x2,  //端点2的x坐标
     *      CGFloat y2,  //端点2的y坐标
     *      CGFloat radius //半径
     *  )
     */
    
    
    // 1/4 弧度*4 划出一个圆形
    CGContextMoveToPoint(contextRef, 100, 100);
    CGContextAddArcToPoint(contextRef, 100, 50, 150, 50, 50);
    CGContextAddArcToPoint(contextRef, 200, 50, 200, 100, 50);
    CGContextAddArcToPoint(contextRef, 200, 150, 150, 150, 50);
    CGContextAddArcToPoint(contextRef, 100, 150, 100, 100, 50);
    CGContextStrokePath(contextRef);
    
    
    // 贝塞尔曲线
    CGContextSetStrokeColorWithColor(contextRef, [UIColor orangeColor].CGColor); // 画笔颜色
    /** 三次曲线函数
     *  void CGContextAddCurveToPoint (
     *      CGContextRef c,
     *      CGFloat cp1x, //控制点1 x坐标
     *      CGFloat cp1y, //控制点1 y坐标
     *      CGFloat cp2x, //控制点2 x坐标
     *      CGFloat cp2y, //控制点2 y坐标
     *      CGFloat x,  //直线的终点 x坐标
     *      CGFloat y  //直线的终点 y坐标
     *  );
     */
    
    CGContextMoveToPoint(contextRef, 100, 300);
    CGContextAddCurveToPoint(contextRef, 100, 300, 200, 0, 200, 300);
    CGContextStrokePath(contextRef);
    
    //三次曲线可以画圆弧
    CGContextMoveToPoint(contextRef, 200, 200);
    CGContextAddCurveToPoint(contextRef, 200, 100, 300, 100, 300 ,100);
    CGContextStrokePath(contextRef);
    
    /** 二次曲线
     *  void CGContextAddQuadCurveToPoint (
     *  CGContextRef c,
     *  CGFloat cpx,  //控制点 x坐标
     *  CGFloat cpy,  //控制点 y坐标
     *  CGFloat x,  //直线的终点 x坐标
     *  CGFloat y  //直线的终点 y坐标
     *  );
     */
    
    CGContextMoveToPoint(contextRef, 100, 100);
    CGContextAddQuadCurveToPoint(contextRef, 200, 0, 300, 150);
    CGContextStrokePath(contextRef);
}

4.画矩形,画椭圆,多边形

- (void)drawShape:(CGContextRef)contextRef
{
    CGContextSetFillColorWithColor(contextRef, [UIColor blackColor].CGColor);
    
    // 画椭圆,如果长宽相等就是圆
    CGContextAddEllipseInRect(contextRef, CGRectMake(50, 250, 50, 100));
    
    // 画矩形,长宽相等就是正方形
    CGContextAddRect(contextRef, CGRectMake(150, 250, 50, 100));
    
    // 画多边形,多边形是通过path完成的
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, &CGAffineTransformIdentity, 50, 350);
    CGPathAddLineToPoint(path, &CGAffineTransformIdentity, 100, 350);
    CGPathAddLineToPoint(path, &CGAffineTransformIdentity, 80, 400);
    CGPathCloseSubpath(path);
    CGContextAddPath(contextRef, path);
    
    
    // 填充
    CGContextFillPath(contextRef);
}

5. 画文字

- (void)drawText:(CGContextRef)contextRef
{
    // 文字样式
    UIFont *font = [UIFont systemFontOfSize:18];
    NSDictionary *dict = @{NSFontAttributeName:font, NSForegroundColorAttributeName:[UIColor colorWithRed:0.167 green:0.752 blue:1.000 alpha:1.000]};
    [@"荣耀归来" drawInRect:CGRectMake(100, 450, 100, 50) withAttributes:dict];
}

6.画图片

// 画图片
- (void)drawPicture:(CGContextRef)contextRef
{
    UIImage *image = [UIImage imageNamed:@"17.png"];
    [image drawInRect:CGRectMake(100, 500, 100, 150)]; // 在坐标系中画出图片
}







2015-08-06 20:39:13 realank 阅读数 634
  • NinePatch图片制作从入门到精通

    在Android应用开发当中经常需要使用图片,而图片又经常需要进行拉申,图片拉申的时候会出现变形,制作NinePatch图片可以指定图片的拉申区域,使图片拉申后还能获得比较好的效果。

    14067 人正在学习 去看看 戴振良

在iOS7中,UITableViewCell有一个叫做 UITableViewCellScrollView的层,
会遮盖住边框,应该设置这一层为透明颜色

但是这一层并不能被直接访问到,而是应该使用TableViewCel的contentView,访问父视图来操作
如果是iOS8,则contentView的父视图是UITableViewCell,如果是iOS7,父视图是UITableViewCellScrollView

if (IS_IOS_7) {
        if (![self.contentView.superview isKindOfClass:[UITableViewCell class]]) {
            self.contentView.superview.backgroundColor = [UIColor clearColor];
        }
    }
2014-01-08 00:47:23 majiakun1 阅读数 2078
  • NinePatch图片制作从入门到精通

    在Android应用开发当中经常需要使用图片,而图片又经常需要进行拉申,图片拉申的时候会出现变形,制作NinePatch图片可以指定图片的拉申区域,使图片拉申后还能获得比较好的效果。

    14067 人正在学习 去看看 戴振良
  //draw background circle
    UIBezierPath *backCircle = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetWidth(self.bounds) / 2, CGRectGetHeight(self.bounds) / 2)
                                                              radius:(CGRectGetWidth(self.bounds) - self.lineWidth) / 2
                                                          startAngle:(CGFloat) - M_PI_2 //(π/2)
                                                            endAngle:(CGFloat)(1.5 * M_PI)
                                                           clockwise:YES];
    [self.backColor setStroke];
    backCircle.lineWidth = self.lineWidth;
    [backCircle stroke];
    
    if (self.progress) {
        //draw progress circle
        UIBezierPath *progressCircle = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetWidth(self.bounds) / 2,CGRectGetHeight(self.bounds) / 2)
                                                                      radius:(CGRectGetWidth(self.bounds) - self.lineWidth) / 2
                                                                  startAngle:(CGFloat) - M_PI_2
                                                                    endAngle:(CGFloat)(- M_PI_2 + self.progress * 2 * M_PI)
                                                                   clockwise:YES];
        [self.progressColor setStroke];
        progressCircle.lineWidth = self.lineWidth;
        [progressCircle stroke];

    }

注:draw arc  实质是画线, 弧度的的宽度。 bezierPathWithArcCenter:  radius:   startAngle : endAngle: clockwise:该方法是画圆线,是在沿着半径画圆线,但不填充圆。 画圆弧只需要设置lineWidth即可。



2019-04-15 18:03:14 chenkaisq 阅读数 95
  • NinePatch图片制作从入门到精通

    在Android应用开发当中经常需要使用图片,而图片又经常需要进行拉申,图片拉申的时候会出现变形,制作NinePatch图片可以指定图片的拉申区域,使图片拉申后还能获得比较好的效果。

    14067 人正在学习 去看看 戴振良

View的draw流程

先看下view的draw()方法(ViewGroup并没有重写draw方法)

  public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

        /*
         * Draw traversal performs several drawing steps which must be executed
         * in the appropriate order:
         *
         *      1. Draw the background
         *      2. If necessary, save the canvas' layers to prepare for fading
         *      3. Draw view's content
         *      4. Draw children
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         */

        // Step 1, draw the background, if needed
        int saveCount;

        if (!dirtyOpaque) {
            drawBackground(canvas);
        }

        // skip step 2 & 5 if possible (common case)
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);

            // Step 4, draw the children
            dispatchDraw(canvas);

            drawAutofilledHighlight(canvas);

            // Overlay is part of the content and draws beneath Foreground
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            // Step 6, draw decorations (foreground, scrollbars)
            onDrawForeground(canvas);

            // Step 7, draw the default focus highlight
            drawDefaultFocusHighlight(canvas);

            if (debugDraw()) {
                debugDrawFocus(canvas);
            }

            // we're done...
            return;
        }
     
        .......
    }

首先根据draw方法的源码注释(省略了部分源码),draw总共有6步:

  1. 绘制背景
  2. 保存Canvas图层信息(如果有必要的话)
  3. 绘制View的内容
  4. 绘制子View
  5. 绘制保存的Canvas图层信息(如果有必要的话)
  6. 绘制View的装饰(比如滑动条)

其中,第二步和第五步可以跳过,所以我们重点看其他几步.
步骤一:绘制背景 drawBackground(canvas)

 private void drawBackground(Canvas canvas) {
        final Drawable background = mBackground;
        if (background == null) {
            return;
        }

        setBackgroundBounds();

        // Attempt to use a display list if requested.
        if (canvas.isHardwareAccelerated() && mAttachInfo != null
                && mAttachInfo.mThreadedRenderer != null) {
            mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);

            final RenderNode renderNode = mBackgroundRenderNode;
            if (renderNode != null && renderNode.isValid()) {
                setBackgroundRenderNodeProperties(renderNode);
                ((DisplayListCanvas) canvas).drawRenderNode(renderNode);
                return;
            }
        }

        final int scrollX = mScrollX;
        final int scrollY = mScrollY;
        if ((scrollX | scrollY) == 0) { // 1
            background.draw(canvas); // 2
        } else {
            canvas.translate(scrollX, scrollY);
            background.draw(canvas);
            canvas.translate(-scrollX, -scrollY);
        }
    }

在此方法中background是作为一个Drawable对象的,代码注释1处,表明绘制背景时要先考虑偏移,如果偏移值不为0,需要在偏移之后再绘制背景,然后会调用Drawable的draw()方法
步骤三:绘制view内容 onDraw(canvas)

 protected void onDraw(Canvas canvas) {
    }

然后会发现这个方法是个空方法,这意味着,不同的view有不同的实现,自定义view的时候需要我们自己来实现
步骤四:绘制子view dispatchDraw(canvas);

  protected void dispatchDraw(Canvas canvas) {

    }

在View中,这个方法也是一个空实现,但是在ViewGroup中重写了这个方法(因为一个纯粹的view是没有子view的,只有viewgroup才有子view)

 protected void dispatchDraw(Canvas canvas) {
      ......
        for (int i = 0; i < childrenCount; i++) {
            while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
                final View transientChild = mTransientViews.get(transientIndex);
                if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                        transientChild.getAnimation() != null) {
                    more |= drawChild(canvas, transientChild, drawingTime);
                }
                transientIndex++;
                if (transientIndex >= transientCount) {
                    transientIndex = -1;
                }
            }

            final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
            final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                more |= drawChild(canvas, child, drawingTime);
            }
        }
   ......
    }

上述代码省略了部分源码,可从上述源码中看出ViewGroup的dispatchDraw()方法中,遍历了所有子view,并调用了drawChild()方法

 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime);
    }

然后又调用了子view的draw方法(此draw方法与文章开头的draw方法不是一个方法),这个方法源码有点长,这里省略了大部分代码,重点关注下computeScroll()这个方法,之前在view的滑动方法一篇中提到过得

 boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
        .....

        int sx = 0;
        int sy = 0;
        if (!drawingWithRenderNode) {
            computeScroll();
            sx = mScrollX;
            sy = mScrollY;
        }

   ......
    }

步骤六:绘制装饰 onDrawForeground(Canvas canvas)

 public void onDrawForeground(Canvas canvas) {
        onDrawScrollIndicators(canvas);
        onDrawScrollBars(canvas);

        final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
        if (foreground != null) {
            if (mForegroundInfo.mBoundsChanged) {
                mForegroundInfo.mBoundsChanged = false;
                final Rect selfBounds = mForegroundInfo.mSelfBounds;
                final Rect overlayBounds = mForegroundInfo.mOverlayBounds;

                if (mForegroundInfo.mInsidePadding) {
                    selfBounds.set(0, 0, getWidth(), getHeight());
                } else {
                    selfBounds.set(getPaddingLeft(), getPaddingTop(),
                            getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
                }

                final int ld = getLayoutDirection();
                Gravity.apply(mForegroundInfo.mGravity, foreground.getIntrinsicWidth(),
                        foreground.getIntrinsicHeight(), selfBounds, overlayBounds, ld);
                foreground.setBounds(overlayBounds);
            }

            foreground.draw(canvas);
        }
    }

在这个方法中会依次调用onDrawScrollIndicators和onDrawScrollbars绘制滑动条的指示器和滑动条,最后绘制View的前景色

ios绘图基础

阅读数 613

draw

阅读数 543

没有更多推荐了,返回首页