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.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

dcam’s picture

Have 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.

c470ip’s picture

Yeah 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.

David_Rothstein’s picture

#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?

c470ip’s picture

I'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.

mondrake’s picture

Version: 7.37 » 7.x-dev
Status: Active » Needs review
FileSize
500 bytes

A bit shooting in the dark... however this is what could have happened:

  1. the GIF images used as overlays (I assume that BOTH the text watermark AND the rounded corners are GIF images, i.e. you are not using the 'rounded corners' effect from imagecache_actions) have their transparent color set out of the palette
  2. in that case, image_gd_create_tmp creates a temp truecolor canvass with white background
  3. then image_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 transparent
  4. finally the watermark image is overlayed onto the original image, with full white background

Can 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.

mondrake’s picture

Issue tags: +Needs tests
c470ip’s picture

Version: 7.x-dev » 7.37
Status: Needs review » Active

Thank 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:

Version: 7.x-dev » 7.37
Status: Needs review » Active

I didn't change any flags or attributes!

mondrake’s picture

Version: 7.37 » 7.x-dev
Status: Active » Needs review

@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 ;)

mondrake’s picture

BTW - @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

c470ip’s picture

FileSize
140 bytes

Thank 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 ;)

c470ip’s picture

Status: Needs review » Needs work
mondrake’s picture

Priority: Normal » Major
Status: Needs work » Needs review
FileSize
1.09 KB

@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

Status: Needs review » Needs work

The last submitted patch, 12: 2485761-12.patch, failed testing.

Status: Needs work » Needs review

mondrake queued 12: 2485761-12.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 12: 2485761-12.patch, failed testing.

mondrake’s picture

Status: Needs work » Needs review

Please 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.

c470ip’s picture

Priority: Major » Normal
Status: Needs review » Needs work

The patch in #15 worked for me, thank you! Now the white backgrounds are gone.

c470ip’s picture

Status: Needs work » Reviewed & tested by the community
c470ip’s picture

Priority: Normal » Major
mondrake’s picture

Version: 7.x-dev » 8.0.x-dev
Assigned: Unassigned » mondrake
Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs backport to D7, +Regression

Thanks 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.

mondrake’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
6.78 KB

Test-only patch, incorporating a test for the indexed GIF file provided by @c470ip in #10. Should have 2 fails.

Status: Needs review » Needs work

The last submitted patch, 21: 2485761-21-test-only.patch, failed testing.

mondrake’s picture

Status: Needs work » Needs review
FileSize
8.34 KB
1.56 KB

The additional test image file broke the sequence of the array returned by drupalGetTestFiles('image'), and ImageFieldDisplayTest. Fixed here, still test-only patch due to have 2 fails.

Status: Needs review » Needs work

The last submitted patch, 23: 2485761-23-test-only.patch, failed testing.

mondrake’s picture

Issue summary: View changes
mondrake’s picture

Status: Needs work » Needs review
FileSize
2.34 KB
10.35 KB

Full 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.

mondrake’s picture

Assigned: mondrake » Unassigned

mondrake queued 26: 2485761-26.patch for re-testing.

jhedstrom queued 26: 2485761-26.patch for re-testing.

mondrake queued 26: 2485761-26.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 26: 2485761-26.patch, failed testing.

mondrake’s picture

Status: Needs work » Needs review
FileSize
10.12 KB

Reroll.

jhedstrom’s picture

One small nit, and a question about a method rename:

  1. +++ b/core/modules/system/src/Tests/Image/ToolkitGdTest.php
    @@ -271,11 +278,9 @@ function testManipulations() {
    +          // Cannot expect transparency for a GIF that has not.
    

    I don't understand this code comment, and it could probably be rephrased for clarity.

  2. +++ b/core/modules/system/src/Tests/Image/ToolkitGdTest.php
    @@ -436,9 +441,42 @@ public function testResourceDestruction() {
    -   * Tests loading an image whose transparent color index is out of range.
    

    Doesn't this method still also test the out of range bit?

mondrake’s picture

FileSize
11.19 KB
2.12 KB

Thanks 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.

jhedstrom’s picture

Status: Needs review » Reviewed & tested by the community

This looks good to me. Marking RTBC.

catch’s picture

Issue tags: +rc target triage
catch’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: -rc target triage +rc target
+++ b/core/modules/image/src/Tests/ImageFieldDisplayTest.php
@@ -343,6 +343,8 @@ function testImageFieldDefaultImage() {
+    // $images[2] = image-test.gif

We 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.

mondrake’s picture

#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 the simpletest/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 with current() or array_shift(), since all of a sudden it will result in fetching a file object for an invalid image. More info in that issue.

Status: Needs review » Needs work

The last submitted patch, 38: 2485761-38.patch, failed testing.

mondrake’s picture

Status: Needs work » Needs review
FileSize
980 bytes
8.14 KB

Ok, ok.

jhedstrom’s picture

Status: Needs review » Reviewed & tested by the community

I think the issues from #37 have been addressed here.

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(!)).

Perhaps a follow-up to fix how these tests work?

mondrake’s picture

@jhedstrom

Perhaps a follow-up to fix how these tests work?

there is a proposal for such a fix in #2377747: Incorrect node create validation error when an invalid image is attached to a field

  • alexpott committed 020858b on 8.0.x
    Issue #2485761 by mondrake, c470ip, jhedstrom: Support for transparent...
alexpott’s picture

Version: 8.0.x-dev » 7.x-dev
Status: Reviewed & tested by the community » Patch (to be ported)

Based on @xjm's post release triage document, committed 12db22c and pushed to 8.0.x and 8.1.x. Thanks!

  • alexpott committed 12db22c on 8.1.x
    Issue #2485761 by mondrake, c470ip, jhedstrom: Support for transparent...
mondrake’s picture

Assigned: Unassigned » mondrake

Working on a D7 patch

mondrake’s picture

Let'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.

mondrake’s picture

Status: Patch (to be ported) » Needs review

The last submitted patch, 47: 2485761-47-test-only.patch, failed testing.

The last submitted patch, 47: 2485761-47-test-only.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 47: 2485761-47.patch, failed testing.

mondrake’s picture

FileSize
2.94 KB
8.97 KB

testImageFieldDefaultImage() requires the same changes due to sequence of test files as in its D8 equivalent. Also a small change to testGifTransparentImages() to ensure that the resource generated by image_gd_create_tmp() is the one saved to disk.

mondrake’s picture

Status: Needs work » Needs review

The last submitted patch, 47: 2485761-47.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 52: 2485761-52.patch, failed testing.

mondrake’s picture

Status: Needs work » Needs review
FileSize
9.18 KB

Missed to add --binary option in git diff for the new test file.

The last submitted patch, 52: 2485761-52.patch, failed testing.

mondrake’s picture

#56 is green.

  • alexpott committed 12db22c on 8.3.x
    Issue #2485761 by mondrake, c470ip, jhedstrom: Support for transparent...

  • alexpott committed 12db22c on 8.3.x
    Issue #2485761 by mondrake, c470ip, jhedstrom: Support for transparent...

  • alexpott committed 12db22c on 8.4.x
    Issue #2485761 by mondrake, c470ip, jhedstrom: Support for transparent...

  • alexpott committed 12db22c on 8.4.x
    Issue #2485761 by mondrake, c470ip, jhedstrom: Support for transparent...
ytsurk’s picture

#59 works fine for me in the latest D7.