Comment 31 for bug 381677

Revision history for this message
Hector Martin (marcan42) wrote :

I think the problem is the opacity is being triple-applied.

Looking at an ASCII-ified PDF with mutool:

$ mutool clean -a -d pdf_blur_test.pdf pdf_blur_test_aa.pdf

In the PDF, the page uses this resource dictionary:

3 0 obj
<<
  /ExtGState <<
    /a0 <<
      /CA .5
      /ca .5
    >>
    /a1 <<
      /CA .498039
      /ca .498039
    >>
    /a2 <<
      /CA 1
      /ca 1
    >>
  >>
  /XObject <<
    /x6 6 0 R
    /x7 7 0 R
    /x8 8 0 R
    /x9 9 0 R
  >>
>>
endobj

Note the three opacity GStates (/a0, /a1, /a2). /a0 is used for *both* the unblurred and blurred leftmost circles (I guess there is some deduplication; /a1 is slightly different from 0.5 due to rounding).

The page contents are:

4 0 obj
<<
  /Length 852
>>
stream
<snip>
#### this is the top left circle
1 0 0 -1 0 841.889771 cm
q
q
1 0 0 1 0 0 cm
/a0 gs /x6 Do <---- top left circle uses /a0 = opacity 0.5
Q
0 0 0 rg /a1 gs <---- top middle circle uses /a1 = opacity .498039
<snip circle outline>
0.498039 0.498039 0.498039 rg /a2 gs <---- top right circle uses /a2 = opacity 1.0
<snip circle outline>
q
1 0 0 1 0 0 cm
/a0 gs /x7 Do <---- bottom left circle uses /a0 = opacity 0.5
Q
Q q
201.207 389.941 192.859 192.859 re W n
q
192.858322 0 0 -192.858322 201.208612 582.801606 cm
/a2 gs /x8 Do <---- bottom middle circle uses /a0 = opacity 1.0
Q
Q q
358.793 389.941 192.859 192.859 re W n
q
192.858322 0 0 -192.858322 358.792416 582.801606 cm
/a2 gs /x9 Do <---- bottom right circle uses /a0 = opacity 1.0
Q
Q

If I change "/a0 gs /x7 Do" to read "/a2 gs /x7 Do" then the circle becomes darker, but still not quite dark enough. For that we have to follow the /x7 reference to the data for the bottom left circle:

7 0 obj
<<
  /Length 116
  /Resources 12 0 R
  /Type /XObject
<snip>
/a0 gs /x14 Do
Q
Q

Which uses this resource dictionary:

12 0 obj
<<
  /ExtGState <<
    /a0 <<
      /CA .5
      /ca .5
    >>
  >>
  /XObject <<
    /x14 14 0 R
  >>
>>
endobj

And here we have another /a0 with opacity .5. Changing *that* to 1 makes the bottom left circle look like how it's supposed to look. The circle uses an all-black image (object 14) with a mask at object 21, and the mask never goes above 7f.

So the opacity is baked into the bitmap when it is rasterized, but then somehow it gets applied not once but *twice* again when the bitmap is rendered. And hence it winds up much, much lighter than it should.

I guess the fix here is that when the object is rasterized, any opacity tag/attribute needs to be reset to 100% since the opacity is now baked into the bitmap.