懒惰的图像绘制

发布于 2024-11-03 04:11:32 字数 402 浏览 0 评论 0原文

我有一个需要按需绘制到图形上下文中的对象,但是,内容需要时间来渲染,并且在调用对象绘制方法时可能不可用。

这一般是如何实现的?存储对请求绘制的图形上下文或视图的引用,并在对象内部表示完全渲染时延迟将其绘制到那里?

或者是否有其他标准的可可机制来处理这个问题(例如 NSImage 在使用 NSURL 初始化时执行延迟绘制)?

澄清:


  • 我在 MacOS 上,而不是 iOS
  • 使用一些 NSViews -setNeedsDisplay: 不是我正在寻找的答案(NSImage 不依赖-setNeedsDisplay:

I have an object that needs to draw into a graphic context on demand, however, the content needs time to render and might not be available when the objects draw method is invoked.

How is this generally accomplished? Store a reference to the graphics context or view that requested drawing and draw it there delayed when the objects internal representation is completely rendered?

Or are there other standard cocoa mechanisms to handle this (for instance NSImage does lazy drawing when initialized with a NSURL)?

Clarifications:


  • I am on MacOS, not iOS
  • Using some NSViews -setNeedsDisplay: is not the answer I am looking for (NSImage doesn't rely on -setNeedsDisplay:)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

把梦留给海 2024-11-10 04:11:32

您应该考虑使用 NSOperation;如果您有一个特定的对象进行渲染,则可以是 NSInvocableOperation;如果渲染足够简单,可以放入单个函数中,则可以是 NSBlockOperation

如果您可以在实际到达视图的 drawRect: 之前开始渲染,那么就这样做(也许您的应用程序委托在启动时就开始该过程)。否则,检查drawRect:中内容是否可用;如果没有,则开始操作并继续另一张图。当渲染对象完成其工作时,它可能会发布通知,或者,如果您给它一个返回视图的引用,请调用 setNeedsDisplay:

按照最后一句的思路,您还可以考虑您的渲染对象能够返回部分渲染的图像。我不确定渲染的性质,但可能会在某些点(每 n 个循环的末尾,或每 n 行像素)获得结果,将其填充到单独的 NSImage< /code> 与最终图像的大小相同(如有必要,请在末尾进行填充),并使该部分图像可用于视图进行绘制。

更新:NSImage 不“依赖”setNeedsDisplay: 或具有视图引用,因为它不代表屏幕的一部分。它所做的只是包含图像的数据;它只能在视图内绘制自己,然后“显示”——实际上绘制在屏幕上。当您使用 initByReferencingURL: 时,它会存储 URL,然后当另一个对象(例如包含图像并需要显示的视图)向它请求其内容时,它会执行其本来应该执行的操作如果您使用了 initWithURL:,即打开文件并将其内容读入内存,则完成。但它并不懒惰地绘制;它仅在视图正在绘制时绘制到需要它的视图中。

子类化 NSImage 来实现您自己的延迟加载或延迟渲染可能并不容易;它使用我认为是 类簇,这就是为什么我建议使用一个包含并返回 NSImage 的“渲染器”对象。

更多:

自定义视图的drawRect

- (void)drawRect:(NSRect)dirtyRect {
    NSLog(@"Entered: %@", NSStringFromSelector(_cmd));
    // Use a nice big image of the Milky Way -- this is about 5MB
    NSImage * lazyImage = [[[NSImage alloc] initByReferencingURL:
                            [NSURL URLWithString:@"http://www.eso.org/public/archives/images/original/milkyway.jpg"]]
                           autorelease];
    NSLog(@"Image instantiated.");
    [lazyImage drawInRect:[self bounds] fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
    NSLog(@"Image drawn");  // 2 minutes later; sometimes 3 in my testing
    [[NSColor yellowColor] set];
    [[NSBezierPath bezierPathWithRect:NSInsetRect([self bounds], 4, 4)] stroke];
    NSLog(@"Bezier path drawn; exiting drawRect.");
}

由此输出的日志;请注意,实例化非常快,但加载和绘制需要两分钟,在此期间没有绘制任何内容,应用程序也没有执行任何其他操作(旋转沙滩球):

2011-04-27 21:33:00.899 SetNeedsDisplay[80162:a0b] Entered: drawRect:
2011-04-27 21:33:00.901 SetNeedsDisplay[80162:a0b] Image instantiated.
2011-04-27 21:34:57.911 SetNeedsDisplay[80162:a0b] Image drawn.
2011-04-27 21:34:57.912 SetNeedsDisplay[80162:a0b] Bezier path drawn; exiting drawRect.

You should look into using NSOperation; either an NSInvocationOperation, if you have a specific object doing the rendering, or NSBlockOperation, if the rendering is simple enough to fit into a single function.

If you can start rendering before you actually get to your view's drawRect:, then do that (maybe your app delegate starts the process right at launch). Otherwise, check in the drawRect: whether the content is available yet; if not, start the operation and continue with the other drawing. When the rendering object finishes its work, it will probably either post a notification or, if you give it a reference back to the view, call setNeedsDisplay:

Along the lines of your last sentence, you could also consider your rendering object being able to return a partially-rendered image. I'm not certain of the nature of your rendering, but it may be possible to get the results at certain points (the end of every n loops, or every n lines of pixels), stuff that into a separate NSImage of the same size as the final image (padding at the end if necessary), and make this partial image available to the view for drawing.

UPDATE: An NSImage doesn't "rely on" setNeedsDisplay: or having a view reference because it doesn't represent a piece of the screen. All it does is contain the data for an image; it can only draw itself inside a view, which is then "displayed" -- actually painted on the screen. When you use initByReferencingURL:, it stores the URL, and then when another object (like a view that contains the image and needs to be displayed) asks it for its contents, it does what it would've done if you had used initWithURL:, which is open the file and read its contents into memory. It doesn't draw lazily, though; it only draws into the view that wants it, when that view is drawing.

Subclassing NSImage to implement your own lazy loading or lazy rendering may not be easy; it uses helper classes that I believe are part of a class cluster, which is why I suggest having a "renderer" object which contains and returns an NSImage.

MORE:

A custom view's drawRect:

- (void)drawRect:(NSRect)dirtyRect {
    NSLog(@"Entered: %@", NSStringFromSelector(_cmd));
    // Use a nice big image of the Milky Way -- this is about 5MB
    NSImage * lazyImage = [[[NSImage alloc] initByReferencingURL:
                            [NSURL URLWithString:@"http://www.eso.org/public/archives/images/original/milkyway.jpg"]]
                           autorelease];
    NSLog(@"Image instantiated.");
    [lazyImage drawInRect:[self bounds] fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
    NSLog(@"Image drawn");  // 2 minutes later; sometimes 3 in my testing
    [[NSColor yellowColor] set];
    [[NSBezierPath bezierPathWithRect:NSInsetRect([self bounds], 4, 4)] stroke];
    NSLog(@"Bezier path drawn; exiting drawRect.");
}

The log output from this; notice that the instantiation is very quick, but the loading and drawing takes two minutes, during which nothing is drawn and the app does nothing else (spinning beach ball):

2011-04-27 21:33:00.899 SetNeedsDisplay[80162:a0b] Entered: drawRect:
2011-04-27 21:33:00.901 SetNeedsDisplay[80162:a0b] Image instantiated.
2011-04-27 21:34:57.911 SetNeedsDisplay[80162:a0b] Image drawn.
2011-04-27 21:34:57.912 SetNeedsDisplay[80162:a0b] Bezier path drawn; exiting drawRect.
格子衫的從容 2024-11-10 04:11:32

我喜欢用于这些目的的 EGOImageView 类。它确实很容易使用,并且包含一些不错的好处,例如缓存机制。

在此处阅读有关 EGOImageView 的更多信息(包括来自 Github 的下载链接):

http://developers.enormego.com/view /what_if_images_on_the_iphone_were_as_easy_as_html

I'm fond of the EGOImageView class for these purposes. It's really easy to use and includes some nice bonuses as will, e.g. a caching mechanism.

Read more about EGOImageView here (includes a download link from Github):

http://developers.enormego.com/view/what_if_images_on_the_iphone_were_as_easy_as_html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文