代码之家  ›  专栏  ›  技术社区  ›  user1118321

如何防止NSBitmapImageRep创建大量中间CGImages?

  •  0
  • user1118321  · 技术社区  · 6 年前

    我做了一件显而易见的事情,并将计算转移到另一个线程,这样UI就可以保持响应。这有帮助,但只有一点点。我通过一个 NSBitmapImageRep 我包了一个 NSGraphicsContext 这样我就可以进去了。但是我需要确保我没有试图在主UI线程上绘制它到屏幕上,同时我也在后台线程上绘制它。所以我引进了一把锁。随着数据越来越大,绘图可能需要很长时间,所以即使这样也有问题。

    我的最新版本有2个 NSBitmapImageRep s。一个保存最新绘制的版本,每当视图需要更新时就被绘制到屏幕上。另一个被绘制到背景线程上。当背景线程上的绘图完成后,它会复制到另一个线程上。我通过得到每个人的基本地址并简单地打电话来复制 memcpy() [-NSGraphicsContext flushContext]

    计算线程如下所示:

        BOOL    done    = NO;
        while (!done)
        {
            self->model->lockBranches();
            self->model->iterate();
            done = (!self->model->moreToDivide()) || (!self->keepIterating);
            self->model->unlockBranches();
    
            [self drawIntoOffscreen];
    
            dispatch_async(dispatch_get_main_queue(), ^{
                self.needsDisplay = YES;
            });
        }
    

    这对于保持UI的响应性来说已经足够好了。但是,每次我将绘制的图像复制到blitting图像时,我都会调用 [-NSBitmapImageRep baseAddress] CGImage 待创建。而且 CG图像 直到计算完成才发布,可能需要几分钟。这导致内存增长相当大。在我的过程中,我看到了大约3-4个cg图像,尽管我从来都不需要超过2个。计算完成后,缓存被清空,我的应用程序的内存下降到只有350-500 MB。我没有想过在计算循环中使用自动释放池,但是我会尝试一下。

    似乎操作系统正在缓存它创建的图像。但是,在计算完成之前,它不会清除缓存,因此在计算完成之前它将不受限制地增长。有没有办法阻止这一切的发生?

    1 回复  |  直到 6 年前
        1
  •  1
  •   Ken Thomases    6 年前

    不要使用 -bitmapData memcpy()

    我经常建议开发人员阅读 10.6 AppKit release notes :

    核心图形阻抗匹配和性能说明

    上面的发行说明详细说明了NSImage级别的核心更改 雪豹。同时也有实质性的变化 与CoreGraphics匹配。

    NSImage是一个相当抽象的图像表示。很漂亮 虽然它没有NSView那么抽象,但它只是一个可以画画的东西 因为它不应该根据上下文的不同方面表现出不同的行为 除了质量上的决定外,它还被牵扯进来。有点不透明 语句,但可以用一个例子来说明:如果你画一个 按钮来拉伸它的中间而不是它的端盖。图像不应 这样做(如果你尝试,你可能会崩溃!)。形象 应始终以线性和均匀的比例填充矩形,其中 该地区的质量。类似地,在 NSImage应该表示同一个图形。不要完全打包

    在我们之前,NSBitmapImageRep是一个更具体的例子 NSBitmapImageRep是一个数据块和像素格式 数据作为颜色值的矩形数组。

    这和CGImage差不多。在雪豹 解包并可能处理数据(从 位图文件格式),在SnowLeopard中我们努力保持 原始图像。

    这会对性能产生一些影响。大多数是好的!你应该看看 较少的编码和解码位图数据作为cg图像。如果你 从JPEG文件初始化NSImage,然后将其绘制为PDF,然后 Leopard你会看到一个解压图像大小的PDF文件。采取 另一个例子是CoreGraphics缓存,包括上传到 显卡,都绑定到CGImage实例上,所以越一样

    但是:在某种程度上,快速的操作 NSBitmapImageRep已更改。图像是不可变的, 很可能需要从CGImage中复制数据,包括 更改,并将其重新打包为新的CGImage。所以,基本上,画画 不是。这在豹子身上是真的,但现在更真实了。

    上面的步骤确实是懒洋洋的:如果你做了导致 它被绘制或直到它因为其他原因需要一个cg图像。所以, 在某些情况下做正确的事情,但总的来说你应该 像素,看看CoreImage吧-这是我们的API 真正用于像素处理的系统。

    变化是应用程序相当喜欢硬编码位图格式。安 NSBitmapImageRep可以是每像素8、32或128位,也可以是 浮点数与否,它可以是预乘与否,它可能是,也可能是 可能没有alpha通道等。这些方面用 位图属性,如-bitmapFormat。不幸的是,如果有人想 要从NSBitmapImageRep实例提取bitmapData,需要 32位每像素RGBA,如果它似乎工作,叫它一天。

    比以前的格式。其中一些硬编码格式可能是

    解决办法是 NSBitmapImageRep的数据可能在里面,这太难了。 取而代之的是,将位图绘制成您需要的格式 知道 看那个。

    看起来是这样的:

    NSBItmapImageRep *bitmapIGotFromAPIThatDidNotSpecifyFormat;
    NSBitmapImageRep *bitmapWhoseFormatIKnow = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:width pixelsHigh:height
                                                      bitsPerSample:bps samplesPerPixel:spp hasAlpha:alpha isPlanar:isPlanar
                                                      colorSpaceName:colorSpaceName bitmapFormat:bitmapFormat bytesPerRow:rowBytes
                                                      bitsPerPixel:pixelBits];
    [NSGraphicsContext saveGraphicsState];
    [NSGraphicsContext setContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmapWhoseFormatIKnow]];
    [bitmapIGotFromAPIThatDidNotSpecifyFormat draw];
    [NSGraphicsContext restoreGraphicsState];
    unsigned char *bitmapDataIUnderstand = [bitmapWhoseFormatIKnow bitmapData];
    

    bitmapigotfromapi的bitmapData没有指定格式,因为 无论如何,数据都需要从备份的CGImage中复制出来。阿尔索 这是一种获取任何图形的已知格式像素的方法,或者 例如,调用-TIFFRepresentation。它也比 锁定NSImage上的焦点并使用-[NSBitmapImageRep initWithFocusedViewRect:]。

    你认为你需要玩像素,(a)考虑是否有办法 使用绘图或(b)查看CoreImage(3) 如果你还活着 想得到像素,画成位图的格式你知道

    事实上,最好从前面的部分开始,使用类似的标题“NSImage、CGImage和CoreGraphics阻抗匹配”,然后通读到后面的部分。

    顺便说一句,有一个很好的机会,交换图像代表将工作,但你只是没有同步他们正确。你必须显示代码,其中两个代表是用来让我们确定。