【问题标题】:How to draw many gradient lines quickly如何快速绘制多条渐变线
【发布时间】:2014-01-15 17:11:29
【问题描述】:

我正在开发一个应用程序,它必须在 iPhone 纵向屏幕上绘制 320 条垂直渐变线,其中每条渐变线的宽度为 1 像素或 2 像素(非视网膜与视网膜)。每条渐变线有 1000 个位置,每个位置都可以有唯一的颜色。这 1000 种颜色(浮点数)位于 C 样式的 2D 数组中(数组的数组,1000 种颜色的 320 个数组)

目前,渐变线是在自定义 UIView 的 drawRect 方法内的 For 循环中绘制的。我遇到的问题是循环通过 For 循环并绘制所有 320 条线需要超过一秒的时间。在那一秒内,我有另一个线程正在更新颜色数组,但由于绘制时间超过一秒,我看不到每次更新。我每第二次或第三次更新一次。

我在我的 Android 代码中使用完全相同的程序,使用 SurfaceView 在每秒内多次绘制 640 条渐变线(数量翻倍)没有问题。我的 Android 应用不会错过任何更新。

如果您查看 Android 代码,它实际上会在两个单独的画布上绘制渐变线。数组大小是动态的,最多可以是 Android 手机横向分辨率宽度的一半(例如 1280 宽度 = 1280/2 = 640 行)。由于 Android 应用程序足够快,我允许横向模式。即使将数据加倍作为 iPhone 并绘制到两个单独的画布上,Android 代码每秒也会运行多次。行数减半且只绘制到单个上下文的 iPhone 代码,一秒钟内都画不出来。

有没有更快的方法在 iPhone 上绘制 320 条垂直渐变线(每条有 1000 个位置)?

是否有适用于 iOS 的硬件加速 SurfaceView 等价物,可以非常快速地绘制许多渐变?

//IPHONE - drawRect method
int totalNumberOfColors = 1000;
int i;
CGFloat *locations = malloc(totalNumberOfColors * sizeof locations[0]);
for (i = 0; i < totalNumberOfColors; i++) {
    float division = (float)1 / (float)(totalNumberOfColors - 1);
    locations[i] = i * division;
}
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGColorSpaceRef colorSpace  = CGColorSpaceCreateDeviceRGB();
for (int k = 0; k < 320; k++) {
    CGFloat * colorComponents = arrayOfFloatArrays[k];

    CGGradientRef gradient = CGGradientCreateWithColorComponents(
                                                                 colorSpace,
                                                                 colorComponents,
                                                                 locations,
                                                                 (size_t)(totalNumberOfColors));
    CGRect newRect;
    if (currentPositionOffset >=320) {
        newRect = CGRectMake(0, 0, 1, CGRectGetMaxY(rect));
    } else {
        newRect = CGRectMake(319 - (k * 1), 0, 1, CGRectGetMaxY(rect));
    }
    CGContextSaveGState(ctx);
    //NO CLIPPING STATE
    CGContextAddRect(ctx, newRect);
    CGContextClip(ctx);
    //CLIPPING STATE
    CGContextDrawLinearGradient(
                                ctx,
                                gradient,
                                CGPointMake(0, 0),
                                CGPointMake(0, CGRectGetMaxY(rect)),
                                (CGGradientDrawingOptions)NULL);


    CGContextRestoreGState(ctx);
    //RESTORE TO NO CLIPPING STATE
    CGGradientRelease(gradient);
}

//ANDROID - public void run() method on SurfaceView
for (i = 0; i < sonarData.arrayOfColorIntColumns.size() - currentPositionOffset; i++) {
    Paint paint = new Paint();
    int[] currentColors = sonarData.arrayOfColorIntColumns.get(currentPositionOffset + i);
    //Log.d("currentColors.toString()",currentColors.toString());
    LinearGradient linearGradient;
    if (currentScaleFactor > 1.0) {
        int numberOfColorsToUse = (int)(1000.0/currentScaleFactor);

        int tmpTopOffset = currentTopOffset;
        if (currentTopOffset + numberOfColorsToUse > 1000) {
            //shift tmpTopOffset
            tmpTopOffset = 1000 - numberOfColorsToUse - 1;
        }
        int[] subsetOfCurrentColors = new int[numberOfColorsToUse];
        System.arraycopy(currentColors, tmpTopOffset, subsetOfCurrentColors, 0, numberOfColorsToUse);
        linearGradient = new LinearGradient(0, tmpTopOffset, 0, getHeight(), subsetOfCurrentColors, null, Shader.TileMode.MIRROR);
        //Log.d("getHeight()","" + getHeight());
        //Log.d("subsetOfCurrentColors.length","" + subsetOfCurrentColors.length);
    } else {
        //use all colors
        linearGradient = new LinearGradient(0, 0, 0, getHeight(), currentColors, null, Shader.TileMode.MIRROR);
        //Log.d("getHeight()","" + getHeight());
        //Log.d("currentColors.length","" + currentColors.length);
    }
    paint.setShader(linearGradient);
    sonarData.checkAndAddPaint(paint);

    numberOfColumnsToDraw = i + 1;
}
//Log.d(TAG,"numberOfColumnsToDraw " + numberOfColumnsToDraw);
currentPositionOffset = currentPositionOffset + i;

if (currentPositionOffset >= sonarData.getMaxNumberOfColumns()) {
    currentPositionOffset = sonarData.getMaxNumberOfColumns() - 1;
}

if (numberOfColumnsToDraw > 0) {
    Canvas canvas = surfaceHolder.lockCanvas();
    if (AppInstanceData.sonarBackgroundImage != null && canvas != null) {
        canvas.drawBitmap(AppInstanceData.sonarBackgroundImage, 0, getHeight()- AppInstanceData.sonarBackgroundImage.getHeight(), null);
        if (cacheCanvas != null) {
            cacheCanvas.drawBitmap(AppInstanceData.sonarBackgroundImage, 0, getHeight()- AppInstanceData.sonarBackgroundImage.getHeight(), null);
        }

    }
    for (i = drawOffset; i < sizeToDraw + drawOffset; i++) {
        Paint p = sonarData.paintArray.get(i - dataStartOffset);
        p.setStrokeWidth(2);
        //Log.d("drawGradientLines", "canvas.getHeight() " + canvas.getHeight());
        canvas.drawLine(getWidth() - (i - drawOffset) * 2, 0, getWidth() - (i - drawOffset) * 2, canvas.getHeight(), p);

        if (cacheCanvas != null) {
            cacheCanvas.drawLine(getWidth() - (i - drawOffset) * 2, 0, getWidth() - (i - drawOffset) * 2, canvas.getHeight(), p);
        }

    }
    surfaceHolder.unlockCanvasAndPost(canvas);
}

【问题讨论】:

    标签: ios objective-c gradient


    【解决方案1】:

    没有对 CG 代码发表评论——我已经有一段时间没有绘制任何渐变了——但有几点说明:

    • 您不应该在 drawRect 中这样做,因为它被调用了很多。绘制图像并显示它。
    • malloc 没有免费的匹配项,因此您正在疯狂地泄漏内存。

    【讨论】:

      【解决方案2】:

      它会有一个学习曲线,但要使用 OpenGL ES 2.0 来实现。我之前也采用了一些绘制大量渐变的方法,并使用 OpenGL ES 2.0 和自定义顶点和片段着色器重新实现了它。它比使用 Core Graphics 完成的等效绘图要快得多,因此您可能也会看到速度大幅提升。

      如果您还不了解任何 OpenGL,我建议您查找一些在 iOS 上使用 OpenGL ES 2.0(必须是 2.0,因为它提供了编写自定义着色器的能力)的教程,以学习基础知识。一旦你这样做了,你应该能够显着提高绘图的性能,远远超过 Android 版本,并且可能会激励 Android 版本也使用 OpenGL。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-03-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多