From 1da82165efb1658b67044dc72d45a5c772e46e72 Mon Sep 17 00:00:00 2001 From: Adrian Greeve Date: Tue, 9 Jun 2015 14:06:42 +0800 Subject: [PATCH 1/3] MDL-37308 files: convert_image() now supports transparency. --- lib/filestorage/file_storage.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/filestorage/file_storage.php b/lib/filestorage/file_storage.php index c832f619057..801825f8d87 100644 --- a/lib/filestorage/file_storage.php +++ b/lib/filestorage/file_storage.php @@ -1556,7 +1556,16 @@ class file_storage { $img = imagecreatefromstring($file->get_content()); if ($height != $newheight or $width != $newwidth) { $newimg = imagecreatetruecolor($newwidth, $newheight); - if (!imagecopyresized($newimg, $img, 0, 0, 0, 0, $newwidth, $newheight, $width, $height)) { + + // Maintain transparency. + if ($filerecord['mimetype'] == 'image/png' || $filerecord['mimetype'] == 'image/gif') { + $colour = imagecolorallocatealpha($newimg, 0, 0, 0, 127); + imagecolortransparent($newimg, $colour); + imagealphablending($newimg, false); + imagesavealpha($newimg, true); + } + + if (!imagecopyresampled($newimg, $img, 0, 0, 0, 0, $newwidth, $newheight, $width, $height)) { // weird throw new file_exception('storedfileproblem', 'Can not resize image'); } From 9993c6bcc02d1fb8b159b5a4931d557ae2b82976 Mon Sep 17 00:00:00 2001 From: Jetha Chan Date: Tue, 9 Jun 2015 17:12:31 +0800 Subject: [PATCH 2/3] MDL-37308 files: support transparency when not resizing; new unit test --- lib/filestorage/file_storage.php | 10 +++ lib/filestorage/tests/file_storage_test.php | 63 +++++++++++++++++++ lib/filestorage/tests/fixtures/testimage.png | Bin 0 -> 4224 bytes 3 files changed, 73 insertions(+) create mode 100644 lib/filestorage/tests/fixtures/testimage.png diff --git a/lib/filestorage/file_storage.php b/lib/filestorage/file_storage.php index 801825f8d87..8bdc73c37ce 100644 --- a/lib/filestorage/file_storage.php +++ b/lib/filestorage/file_storage.php @@ -1563,6 +1563,7 @@ class file_storage { imagecolortransparent($newimg, $colour); imagealphablending($newimg, false); imagesavealpha($newimg, true); + imagefill($newimg, 0, 0, $colour); } if (!imagecopyresampled($newimg, $img, 0, 0, 0, 0, $newwidth, $newheight, $width, $height)) { @@ -1576,6 +1577,8 @@ class file_storage { ob_start(); switch ($filerecord['mimetype']) { case 'image/gif': + imagealphablending($img, true); + imagesavealpha($img, true); imagegif($img); break; @@ -1589,6 +1592,13 @@ class file_storage { case 'image/png': $quality = (int)$quality; + + // Woah nelly! Because PNG quality is in the range 0 - 9 compared to JPEG quality, + // the latter of which can go to 100, we need to make sure that quality here is + // in a safe range or PHP WILL CRASH AND DIE. You have been warned. + $quality = $quality > 9 ? (int)(max(1.0, (float)$quality / 100.0) * 9.0) : $quality; + imagealphablending($img, true); + imagesavealpha($img, true); imagepng($img, NULL, $quality, NULL); break; diff --git a/lib/filestorage/tests/file_storage_test.php b/lib/filestorage/tests/file_storage_test.php index 9c7ff1dfec7..95d60652187 100644 --- a/lib/filestorage/tests/file_storage_test.php +++ b/lib/filestorage/tests/file_storage_test.php @@ -986,6 +986,69 @@ class core_files_file_storage_testcase extends advanced_testcase { $this->assertInstanceOf('stored_file', $converted); } + public function test_convert_image_png() { + global $CFG; + + $this->resetAfterTest(false); + + $filepath = $CFG->dirroot.'/lib/filestorage/tests/fixtures/testimage.png'; + $syscontext = context_system::instance(); + $filerecord = array( + 'contextid' => $syscontext->id, + 'component' => 'core', + 'filearea' => 'unittest', + 'itemid' => 0, + 'filepath' => '/images/', + 'filename' => 'testimage.png', + ); + + $fs = get_file_storage(); + $original = $fs->create_file_from_pathname($filerecord, $filepath); + + // Vanilla test. + $filerecord['filename'] = 'testimage-converted-nosize.png'; + $vanilla = $fs->convert_image($filerecord, $original); + $this->assertInstanceOf('stored_file', $vanilla); + // Assert that byte 25 has the ascii value 6 for PNG-24. + $this->assertTrue(ord(substr($vanilla->get_content(), 25, 1)) == 6); + + // 10x10 resize test; also testing for a ridiculous quality setting, which + // we should if necessary scale to the 0 - 9 range. + $filerecord['filename'] = 'testimage-converted-10x10.png'; + $converted = $fs->convert_image($filerecord, $original, 10, 10, true, 100); + $this->assertInstanceOf('stored_file', $converted); + // Assert that byte 25 has the ascii value 6 for PNG-24. + $this->assertTrue(ord(substr($converted->get_content(), 25, 1)) == 6); + + // Transparency test. + $filerecord['filename'] = 'testimage-converted-102x31.png'; + $converted = $fs->convert_image($filerecord, $original, 102, 31, true, 9); + $this->assertInstanceOf('stored_file', $converted); + // Assert that byte 25 has the ascii value 6 for PNG-24. + $this->assertTrue(ord(substr($converted->get_content(), 25, 1)) == 6); + + $originalfile = imagecreatefromstring($original->get_content()); + $convertedfile = imagecreatefromstring($converted->get_content()); + $vanillafile = imagecreatefromstring($vanilla->get_content()); + + $originalcolors = imagecolorsforindex($originalfile, imagecolorat($originalfile, 0, 0)); + $convertedcolors = imagecolorsforindex($convertedfile, imagecolorat($convertedfile, 0, 0)); + $vanillacolors = imagecolorsforindex($vanillafile, imagecolorat($vanillafile, 0, 0)); + $this->assertEquals(count($originalcolors), 4); + $this->assertEquals(count($convertedcolors), 4); + $this->assertEquals(count($vanillacolors), 4); + $this->assertEquals($originalcolors['red'], $convertedcolors['red']); + $this->assertEquals($originalcolors['green'], $convertedcolors['green']); + $this->assertEquals($originalcolors['blue'], $convertedcolors['blue']); + $this->assertEquals($originalcolors['alpha'], $convertedcolors['alpha']); + $this->assertEquals($originalcolors['red'], $vanillacolors['red']); + $this->assertEquals($originalcolors['green'], $vanillacolors['green']); + $this->assertEquals($originalcolors['blue'], $vanillacolors['blue']); + $this->assertEquals($originalcolors['alpha'], $vanillacolors['alpha']); + $this->assertEquals($originalcolors['alpha'], 127); + + } + private function generate_file_record() { $syscontext = context_system::instance(); $filerecord = new stdClass(); diff --git a/lib/filestorage/tests/fixtures/testimage.png b/lib/filestorage/tests/fixtures/testimage.png new file mode 100644 index 0000000000000000000000000000000000000000..791ad6e6c3099ca92d5ba5a74e8c138c2668ec2a GIT binary patch literal 4224 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 literal 0 HcmV?d00001 From 8948d046952b2d95a77b37bf31b964fe5c0cf8a0 Mon Sep 17 00:00:00 2001 From: Jun Pataleta Date: Tue, 17 Nov 2015 21:52:07 -0600 Subject: [PATCH 3/3] MDL-37308 files: Additional fixes for transparency Fixed transparency support when resizing and when not resizing. --- lib/filestorage/file_storage.php | 49 +++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/lib/filestorage/file_storage.php b/lib/filestorage/file_storage.php index 8bdc73c37ce..5009073eae7 100644 --- a/lib/filestorage/file_storage.php +++ b/lib/filestorage/file_storage.php @@ -1521,7 +1521,6 @@ class file_storage { $width = $imageinfo['width']; $height = $imageinfo['height']; - $mimetype = $imageinfo['mimetype']; if ($keepaspectratio) { if (0 >= $newwidth and 0 >= $newheight) { @@ -1553,32 +1552,58 @@ class file_storage { } } + // The original image. $img = imagecreatefromstring($file->get_content()); - if ($height != $newheight or $width != $newwidth) { - $newimg = imagecreatetruecolor($newwidth, $newheight); - // Maintain transparency. - if ($filerecord['mimetype'] == 'image/png' || $filerecord['mimetype'] == 'image/gif') { - $colour = imagecolorallocatealpha($newimg, 0, 0, 0, 127); - imagecolortransparent($newimg, $colour); - imagealphablending($newimg, false); + // A new true color image where we will copy our original image. + $newimg = imagecreatetruecolor($newwidth, $newheight); + + // Determine if the file supports transparency. + $hasalpha = $filerecord['mimetype'] == 'image/png' || $filerecord['mimetype'] == 'image/gif'; + + // Maintain transparency. + if ($hasalpha) { + imagealphablending($newimg, true); + + // Get the current transparent index for the original image. + $colour = imagecolortransparent($img); + if ($colour == -1) { + // Set a transparent colour index if there's none. + $colour = imagecolorallocatealpha($newimg, 255, 255, 255, 127); + // Save full alpha channel. imagesavealpha($newimg, true); - imagefill($newimg, 0, 0, $colour); } + imagecolortransparent($newimg, $colour); + imagefill($newimg, 0, 0, $colour); + } + // Process the image to be output. + if ($height != $newheight or $width != $newwidth) { + // Resample if the dimensions differ from the original. if (!imagecopyresampled($newimg, $img, 0, 0, 0, 0, $newwidth, $newheight, $width, $height)) { // weird throw new file_exception('storedfileproblem', 'Can not resize image'); } imagedestroy($img); $img = $newimg; + + } else if ($hasalpha) { + // Just copy to the new image with the alpha channel. + if (!imagecopy($newimg, $img, 0, 0, 0, 0, $width, $height)) { + // Weird. + throw new file_exception('storedfileproblem', 'Can not copy image'); + } + imagedestroy($img); + $img = $newimg; + + } else { + // No particular processing needed for the original image. + imagedestroy($newimg); } ob_start(); switch ($filerecord['mimetype']) { case 'image/gif': - imagealphablending($img, true); - imagesavealpha($img, true); imagegif($img); break; @@ -1597,8 +1622,6 @@ class file_storage { // the latter of which can go to 100, we need to make sure that quality here is // in a safe range or PHP WILL CRASH AND DIE. You have been warned. $quality = $quality > 9 ? (int)(max(1.0, (float)$quality / 100.0) * 9.0) : $quality; - imagealphablending($img, true); - imagesavealpha($img, true); imagepng($img, NULL, $quality, NULL); break;