RESOLVED FIXED Bug 65767
toDataURL() uses stale data after putImageData()
https://bugs.webkit.org/show_bug.cgi?id=65767
Summary toDataURL() uses stale data after putImageData()
M8R-1o5lig
Reported 2011-08-05 06:05:12 PDT
Created attachment 103065 [details] Test case and workaround toDataURL() appears to be using stale data if the canvas is updated using putImageData(). If the canvas is subsequently updated using ordinary drawing commands, a call to toDataURL() will work as expected. To reproduce, open the attached sample code to generate an ordered list of: 1. The real canvas. 2. The toDataURL()-image for the uninitialized canvas. 3. The toDataURL()-image for the canvas after it is filled with red pixels using putImageData(). 4. The toDataURL()-image for the canvas after a no-op drawing command. Expected output (OK in Chromium 15.0.845.0 for Mac OS X 10.7.0): 1. A red square. 2. A blank square. 3. A red square. 4. A red square. Output on Safari 5.1 (7534.48.3) and WebKit Nightly 5.1 (7534.48.3, r92445) for Mac OS X 10.7.0: 1. A red square. 2. A blank square. 3. A blank square (UNEXPECTED). 4. A red square. By removing the moveTo(), lineTo(), stroke() lines, it can be verified that the canvas is indeed a red square already after putImageData(). Workaround: As in the sample code, attempt to do some no-ops on the canvas: context.moveTo( 0, 0 ); context.lineTo( 0, 0 ); context.stroke(); (Beware of anti-aliasing ghosting!) Observation: If the size of the canvas is reduced (10x10 works) the problem goes away. Perhaps the issue is related to hardware acceleration?
Attachments
Test case and workaround (978 bytes, text/html)
2011-08-05 06:05 PDT, M8R-1o5lig
no flags
Patch (2.52 KB, patch)
2012-01-17 22:44 PST, Matthew Delaney
no flags
Patch (27.94 KB, patch)
2012-02-03 00:41 PST, Matthew Delaney
no flags
Patch (28.12 KB, patch)
2012-02-03 11:24 PST, Matthew Delaney
no flags
Patch (28.07 KB, patch)
2012-02-03 12:11 PST, Matthew Delaney
no flags
Patch (28.68 KB, patch)
2012-02-03 13:11 PST, Matthew Delaney
no flags
Patch (29.54 KB, patch)
2012-02-03 13:47 PST, Matthew Delaney
no flags
Patch (44.03 KB, patch)
2012-02-06 11:54 PST, Matthew Delaney
cmarrin: review+
Matthew Delaney
Comment 1 2011-08-24 11:26:56 PDT
Test comment. Please ignore.
Radar WebKit Bug Importer
Comment 2 2011-08-24 11:27:57 PDT
Matthew Delaney
Comment 3 2012-01-17 20:28:06 PST
The issue here is largely one of timing and false assumptions about CG behavior. Basically, we had loosely assumed before that modifying the backing store's bits behind the back of the graphics context would magically always make its way to the screen. However there's no guarantee of that - how is CG supposed to know that we modified the backing store memory in the case where we do no drawing through its API as your testcase here showcases, is the question. I've tried out a handful of possible solutions, but none of them get it right other than adding in a NoOp draw call type hack similar to the one suggested in the attached testcase. Essentially, CG wasn't' designed to have the likes of putImageData and getImageData meddling around with its backing store's data directly, so there's no easy and nice way to hint to CG that the context's bits have changed. I'm uploading a patch now that uses a noop draw call after any putImageData call to trick the context into thinking its been drawn into.
Matthew Delaney
Comment 4 2012-01-17 22:44:12 PST
Simon Fraser (smfr)
Comment 5 2012-01-18 09:55:48 PST
Comment on attachment 122875 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=122875&action=review > Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp:301 > + FloatRect rect(1, 1, 0, 0); > + context->fillRect(rect); Doesn't this need to set the fill color to transparent?
Matthew Delaney
Comment 6 2012-01-18 11:35:00 PST
(In reply to comment #5) > (From update of attachment 122875 [details]) > View in context: https://bugs.webkit.org/attachment.cgi?id=122875&action=review > > > Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp:301 > > + FloatRect rect(1, 1, 0, 0); > > + context->fillRect(rect); > > Doesn't this need to set the fill color to transparent? It's a fill rect of area=width=height=0, so it shouldn't matter. I'm still trying to rack my brain to think if there's some composite operation or something else that might affect the bits still in this case, but I currently can't think of anything.
Matthew Delaney
Comment 7 2012-02-02 23:54:37 PST
Trying a new approach: wrap the bits in a CGImage and draw them to the ImageBuffer with the CGContext in a state where it'll copy the bits down just as canvas's putImageData requires.
Matthew Delaney
Comment 8 2012-02-03 00:41:34 PST
WebKit Review Bot
Comment 9 2012-02-03 01:01:38 PST
Comment on attachment 125277 [details] Patch Attachment 125277 [details] did not pass chromium-ews (chromium-xvfb): Output: http://queues.webkit.org/results/11421191
Early Warning System Bot
Comment 10 2012-02-03 01:04:04 PST
Gyuyoung Kim
Comment 11 2012-02-03 01:22:18 PST
Gustavo Noronha (kov)
Comment 12 2012-02-03 01:34:01 PST
Matthew Delaney
Comment 13 2012-02-03 11:24:16 PST
Matthew Delaney
Comment 14 2012-02-03 12:11:10 PST
Matthew Delaney
Comment 15 2012-02-03 12:11:33 PST
New patch to fix Skia build error.
WebKit Review Bot
Comment 16 2012-02-03 12:49:54 PST
Comment on attachment 125380 [details] Patch Attachment 125380 [details] did not pass chromium-ews (chromium-xvfb): Output: http://queues.webkit.org/results/11423359
Matthew Delaney
Comment 17 2012-02-03 13:11:43 PST
WebKit Review Bot
Comment 18 2012-02-03 13:28:08 PST
Comment on attachment 125395 [details] Patch Attachment 125395 [details] did not pass chromium-ews (chromium-xvfb): Output: http://queues.webkit.org/results/11416497
Matthew Delaney
Comment 19 2012-02-03 13:47:01 PST
Andy Estes
Comment 20 2012-02-03 17:26:53 PST
Comment on attachment 125403 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=125403&action=review I don't know enough about the new implementation of ImageBuffer::putByteArray() to review this, but I do have a few comments. > Source/WebCore/ChangeLog:16 > + TODO: Will add a pixel test for this (since this is only a painting issue) in the next patch I think it's worth writing the test as part of this patch instead of putting it off for later. > Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp:312 > if (m_context->isAcceleratedContext()) { There would be less indentation if you wrote this as: if (!m_context->isAcceleratedContext()) { m_data.putData(source, sourceSize, sourceRect, destPoint, m_size, m_context->isAcceleratedContext(), multiplied == Unmultiplied); return; } // accelerated context case goes here > Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp:321 > + if (multiplied == Unmultiplied) > + sourceCopy->m_data.putData(source, sourceSize, sourceRect, IntPoint(-sourceRect.x(), -sourceRect.y()), sourceCopy->size(), false, true); > + else > + sourceCopy->m_data.putData(source, sourceSize, sourceRect, IntPoint(-sourceRect.x(), -sourceRect.y()), sourceCopy->size(), false, false); You could write this more concisely as: sourceCopy->m_data.putData(source, sourceSize, sourceRect, IntPoint(-sourceRect.x(), -sourceRect.y()), sourceCopy->size(), false, (multiplied == Unmultiplied));
Andy Estes
Comment 21 2012-02-03 17:30:37 PST
Comment on attachment 125403 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=125403&action=review > Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp:319 > + sourceCopy->m_data.putData(source, sourceSize, sourceRect, IntPoint(-sourceRect.x(), -sourceRect.y()), sourceCopy->size(), false, true); Shouldn't the second-to-last argument here be true? In the !isAcceleratedContext() case you set this argument to the value of m_context->isAcceleratedContext in your call to m_data.putData(). Why is it false here as well?
Matthew Delaney
Comment 22 2012-02-03 17:35:20 PST
(In reply to comment #21) > (From update of attachment 125403 [details]) > View in context: https://bugs.webkit.org/attachment.cgi?id=125403&action=review > > > Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp:319 > > + sourceCopy->m_data.putData(source, sourceSize, sourceRect, IntPoint(-sourceRect.x(), -sourceRect.y()), sourceCopy->size(), false, true); > > Shouldn't the second-to-last argument here be true? In the !isAcceleratedContext() case you set this argument to the value of m_context->isAcceleratedContext in your call to m_data.putData(). Why is it false here as well? Yea, I see how this one looks weird, though it's actually correct. The false is from the fact that sourceCopy is not accelerated (though, we ourselves are accelerated) since ImageBuffers pass down that knowledge to their ImageBufferData objects for this call. I should replace the false with sourceCopy()->context()->isAccelerated() instead to make it clearer. Really, this whole could use a good cleanup...
Matthew Delaney
Comment 23 2012-02-03 17:38:27 PST
(In reply to comment #20) > (From update of attachment 125403 [details]) > View in context: https://bugs.webkit.org/attachment.cgi?id=125403&action=review > > I don't know enough about the new implementation of ImageBuffer::putByteArray() to review this, but I do have a few comments. > > > Source/WebCore/ChangeLog:16 > > + TODO: Will add a pixel test for this (since this is only a painting issue) in the next patch > > I think it's worth writing the test as part of this patch instead of putting it off for later. Oop, yea, meant to do that along with the Skia port fixes. Cooking that up now then. > > Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp:312 > > if (m_context->isAcceleratedContext()) { > > There would be less indentation if you wrote this as: > > if (!m_context->isAcceleratedContext()) { > m_data.putData(source, sourceSize, sourceRect, destPoint, m_size, m_context->isAcceleratedContext(), multiplied == Unmultiplied); > return; > } Good call. > > You could write this more concisely as: > > sourceCopy->m_data.putData(source, sourceSize, sourceRect, IntPoint(-sourceRect.x(), -sourceRect.y()), sourceCopy->size(), false, (multiplied == Unmultiplied)); Good calls!
Matthew Delaney
Comment 24 2012-02-06 11:54:43 PST
Matthew Delaney
Comment 25 2012-02-06 12:54:20 PST
Note You need to log in before you can comment on or make changes to this bug.