Problem/Motivation
#375062: imagecolorsforindex() Color index nnn out of range in GDToolkit broke support for transparent indexed GIF images. This was noticed only after backport of the patch to D7 and in combination with usage of contrib modules, see original report below.
Transparent GIF images used as watermarks do not retain transparency and the transparent part of the image is replaced with white color.
GDToolkit::load
creates a new truecolor image when loading a GIF indexed image, overlays the loaded image onto the truecolor one, and reallocates the transparent color from the loaded GIF image in the new one.
However, it's not enough to set the GIF transparent color to ensure transparency while images are getting processed in memory by GD.
Proposed resolution
We are creating a new truecolor image in CreateNew::execute
, and we need to set alpha channel (to 127) for the color indicated as transparent to support in-memory processing.
Remaining tasks
- review patch
User interface changes
None
API changes
None
Original report by @c470ip:
On two of my production sites I am using Drupal 7.35 together with Imagecache Actions module. I use that module to watermark some photos with transparent GIF files so that the photo looks like this: https://www.drupal.org/files/issues/dscn4994%20%281%29.jpg
After upgrading system core to 7.36 or higher, the transparent part of GIF images turns to white, which breaks the image completely: https://www.drupal.org/files/issues/dscn4994.jpg . I suppose this is a core Image module/system bug as it is gone once I revert to Drupal core 7.35, all third-party modules remaining unchanged. Hopefully this can be fixed somehow or there may be some kind of a workaround if this was done on purpose. Unfortunately could not find any info regarding this problem so I am opening this issue.
Comment | File | Size | Author |
---|---|---|---|
#59 | 2485761-59.patch | 9.04 KB | mondrake |
#56 | 2485761-56.patch | 9.18 KB | mondrake |
#52 | interdiff_47-52.txt | 2.94 KB | mondrake |
#47 | 2485761-47.patch | 7.29 KB | mondrake |
#10 | corner-lb.gif | 140 bytes | c470ip |
Comments
Comment #1
dcam CreditAttribution: dcam commentedHave you looked through the 7.36 or 7.37 release notes to try and identify the issue that might have caused this problem? I looked through them, but didn't really see anything. Sorry, I'm just not familiar with this functionality.
Comment #2
c470ip CreditAttribution: c470ip commentedYeah of course I've studied the release notes, not much regarding Image system there. The only suspicious thing could be this fix:
#375062 by cs_shadow, David_Rothstein, mondrake, juampy, theunraveler, hswong3i, smk-ka, fietserwin: "imagecolorsforindex() Color index nnn out of range in GDToolkit" message sometimes appears
in 7.36 as it deals with handling image transparency, but I am not that deep into Drupal core to state that for sure.
Comment #3
David_Rothstein CreditAttribution: David_Rothstein as a volunteer commented#375062: imagecolorsforindex() Color index nnn out of range in GDToolkit does seem likely, yes.
I'll leave a comment there cross-linking it to this issue; it's a pretty esoteric topic but maybe someone there will be able to look at the image and know right away what the problem is.
Otherwise, any way to debug what's going on as Drupal generates the image and see where the code is breaking the transparency?
Comment #4
c470ip CreditAttribution: c470ip commentedI'm afraid I have no clue how to debug this :(
Can only say that there are no any system notices or errors in available logs. BTW I also never saw that
Color index nnn out of range in GDToolkit
error since v.7.22.Comment #5
mondrakeA bit shooting in the dark... however this is what could have happened:
image_gd_create_tmp
creates a temp truecolor canvass with white backgroundimage_gd_load
copies the image from the file onto the canvass - but here transparency is preserved (?) and you end up with white where you expect transparentCan you try the patch attached just to test this hypothesis - here we are changing the full white background to full transparency as in fact we are running
imagefill
on a truecolor image where we have full alpha channel available, and while in memory we do not need to set GIF transparent color.Can't say if there would be side effects or find a way to develop an automated test ATM, but let's just see if there's any difference.
Please leave to 7.x-dev while we have a clear case as per OP. It should be fixed in 8.0.x-dev first I know, but we do not have a real life example to test there, so let's use the 7.x report to see if we make progress.
Comment #6
mondrakeComment #7
c470ip CreditAttribution: c470ip commentedThank you mondrake! Yes, both the rounded corners and the text in my example are GIFs with transparency, AFAIK there is no similar problem with textual overlays.
I will try your patch a bit later and report results. Thanks once again.
Sorry, cannot understand why these lines appear in my post:
I didn't change any flags or attributes!
Comment #8
mondrake@c470ip re:#7
I made those changes to the issue bacause they are needed to make sure
a) that the automated test framework picks the patch and test it (from: 'active' to 'needs review')
b) that the test is executed against the proper development branch (from: '7.37' to '7.x-dev')
and also to flag to the community that a patch is there to be reviewed and tested, to which community members can feedback setting status to 'needs work' or 'RTBC'. Welcome to Drupal's patch review process! See details here if you wish ;)
Comment #9
mondrakeBTW - @c470ip it would help if you could upload here one of the GIF images you are using as overlays, not just the final result. We could use that to build an automated test that checks colors at different stages of the image object lifecycle. Thanks
Comment #10
c470ip CreditAttribution: c470ip commentedThank you mondrake. I tried the patch but unfortunately the problem is still there, i.e. the converted image still has lots of white polygons overlayed. Here is one of the GIF images used for making rounded corners, this one is bottom-left, but I don't think it's important ;)
Comment #11
c470ip CreditAttribution: c470ip commentedComment #12
mondrake@c470ip thanks for sharing the image file. It turns out I was on the right track, but incomplete.
The entire issue: it's not enough to set the GIF transparent color to ensure transparency while images are getting processed in memory by GD.
Since we are creating a truecolor image in
image_gd_create_tmp
, we need to set alpha channel (to 127) for the color indicated as transparent, plus set the GIF transparent color separately.If the transparent color is out of the color palette (that was the fix in #375062: imagecolorsforindex() Color index nnn out of range in GDToolkit), still we may 'expect' that the image being loaded would be (partially) transparent while in memory, and only if the image is saved to disk then the missing color index would lead to the transparent parts be replaced by white.
This worked for me locally, please check.
If this proves ok, then we will need to move this issue to the Drupal 8 queue, since bug fixes have to be addressed in the current developing version first, then backported.
EDIT - changed status to major since it's a regression
Comment #16
mondrakePlease forget about the failing test for the moment. Can you test the patch anyway manually. The test will have to be fixed too in Drupal 8 first.
Comment #17
c470ip CreditAttribution: c470ip commentedThe patch in #15 worked for me, thank you! Now the white backgrounds are gone.
Comment #18
c470ip CreditAttribution: c470ip commentedComment #19
c470ip CreditAttribution: c470ip commentedComment #20
mondrakeThanks for feedback @c470ip, good news :)
We need to fix this in Drupal 8 first, as the problem is there as well. I will work on a patch and tests.
Comment #21
mondrakeTest-only patch, incorporating a test for the indexed GIF file provided by @c470ip in #10. Should have 2 fails.
Comment #23
mondrakeThe additional test image file broke the sequence of the array returned by
drupalGetTestFiles('image')
, andImageFieldDisplayTest
. Fixed here, still test-only patch due to have 2 fails.Comment #25
mondrakeComment #26
mondrakeFull patch. Interdiff from #23: basically the fix is changes to 2 lines of code in CreateNew. The fix in the test is needed because with the change in CreateNew the transparent GIF used in the desaturate test is still transparent (in memory) after the desaturate operation.
Comment #27
mondrakeComment #32
mondrakeReroll.
Comment #33
jhedstromOne small nit, and a question about a method rename:
I don't understand this code comment, and it could probably be rephrased for clarity.
Doesn't this method still also test the out of range bit?
Comment #34
mondrakeThanks for review, @jhedstrom!
#33.1 - merged that bit with some checks done a few lines below, and clarified what's going on.
#33.2 - yes it does, that line removed from the method's docblock has been added verbatim a few lines below, where the out-of-range test code is. The method is renamed because now it does more than it was doing before, so I prefer to do that not to confuse. If that's not OK, then I would suggest to have a separate test method here, but I was doing that on the basis to keep the number of test methods down.
Comment #35
jhedstromThis looks good to me. Marking RTBC.
Comment #36
catchComment #37
catchWe shouldn't add commented out code for this.
Also are all the test changes here necessary to fix the test coverage? Or could some be split to a clean- up issue?
Discussed with @alexpott, @xjm, @effulgentsia and agreed this should be rc target.
Comment #38
mondrake#37: adjusted that, reverted non-essential test changes, and removed usage of SafeMarkup::format from the patch.
One comment regarding the changes to
ImageFieldDisplayTest
. The changes are needed not for functional changes, but only because we introduce a new image file in thesimpletest/misc
directory for testing purposes. IMHO we have a general issue with how we manage test image files.drupalGetTestFiles('image')
returns an array of files sorted by size (if we need to take a specific file for testing, we would need to find it by its size(!)).The problem appears in any test (like the one here) that is built based on an expected sequence of files: if we introduce a file in within the sequence (because its size is between the sizes of two existing ones), the test breaks as the expected file is not showing up when expected. This is not so visible here, but is strikingly evident in #2377747: Incorrect node create validation error when an invalid image is attached to a field - there we introduce, for testing, an invalid image file of 0 bytes size: that breaks almost all the tests that use
drupalGetTestFiles('image')
and then get the file withcurrent()
orarray_shift()
, since all of a sudden it will result in fetching a file object for an invalid image. More info in that issue.Comment #40
mondrakeOk, ok.
Comment #41
jhedstromI think the issues from #37 have been addressed here.
Perhaps a follow-up to fix how these tests work?
Comment #42
mondrake@jhedstrom
there is a proposal for such a fix in #2377747: Incorrect node create validation error when an invalid image is attached to a field
Comment #44
alexpottBased on @xjm's post release triage document, committed 12db22c and pushed to 8.0.x and 8.1.x. Thanks!
Comment #46
mondrakeWorking on a D7 patch
Comment #47
mondrakeLet's see this. It takes a bit more changes than the D8 patch because image_gd_create_tmp was not dealing with image with no transparency set.
Comment #48
mondrakeComment #52
mondraketestImageFieldDefaultImage()
requires the same changes due to sequence of test files as in its D8 equivalent. Also a small change totestGifTransparentImages()
to ensure that the resource generated byimage_gd_create_tmp()
is the one saved to disk.Comment #53
mondrakeComment #56
mondrakeMissed to add --binary option in git diff for the new test file.
Comment #58
mondrake#56 is green.
Comment #59
mondrakeReroll after #2215369: Various bugs with PHP 5.5 imagerotate(), including when incorrect color indices are passed in.
Comment #64
ytsurk#59 works fine for me in the latest D7.