Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ public function get_dominant_color() {
$this->image->resizeImage( 1, 1, Imagick::FILTER_LANCZOS, 1 );
$pixel = $this->image->getImagePixelColor( 0, 0 );
$color = $pixel->getColor();
$hex = dominant_color_rgb_to_hex( $color['r'], $color['g'], $color['b'] );
// Cast to int: ImagickPixel::getColor() may return floats depending on
// ImageMagick/Imagick configuration, which would break the int contract
// of dominant_color_rgb_to_hex() under strict_types.
$hex = dominant_color_rgb_to_hex( (int) $color['r'], (int) $color['g'], (int) $color['b'] );
if ( null === $hex ) {
return new WP_Error( 'image_editor_dominant_color_error', __( 'Dominant color detection failed.', 'dominant-color-images' ) );
}
Expand Down
95 changes: 65 additions & 30 deletions plugins/dominant-color-images/helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,38 @@ function dominant_color_set_image_editors( array $editors ): array {
return $editors;
}

/**
* Filters the list of image editors to include the dominant color editors.
*
* Ensures core editor classes are loaded before delegating to
* {@see dominant_color_set_image_editors()}, since the `wp_image_editors`
* filter can run early (e.g. during `wp_image_editor_supports()`).
*
* @since n.e.x.t
*
* @param string[]|mixed $editors List of image editor class names, or mixed value from previous filter.
* @return string[] Filtered list of image editor class names.
*/
function dominant_color_filter_image_editors( $editors ): array {
if ( ! is_array( $editors ) ) {
$editors = array();
}
/**
* Because plugins do bad things.
*
* @var string[] $editors
*/

if ( ! class_exists( 'WP_Image_Editor_GD' ) ) {
require_once ABSPATH . WPINC . '/class-wp-image-editor-gd.php'; // @codeCoverageIgnore
}
if ( ! class_exists( 'WP_Image_Editor_Imagick' ) ) {
require_once ABSPATH . WPINC . '/class-wp-image-editor-imagick.php'; // @codeCoverageIgnore
}

return dominant_color_set_image_editors( $editors );
}

/**
* Computes the dominant color of the given attachment image and whether it has transparency.
*
Expand Down Expand Up @@ -90,13 +122,9 @@ function dominant_color_get_dominant_color_data( int $attachment_id ) {
}

$file = dominant_color_get_attachment_file_path( $attachment_id );
if ( false === $file ) {
$file = get_attached_file( $attachment_id );
}
if ( false === $file ) {
return new WP_Error( 'no_image_found', __( 'Unable to load image.', 'dominant-color-images' ) );
}
add_filter( 'wp_image_editors', 'dominant_color_set_image_editors' );

/**
* Editor.
Expand All @@ -113,7 +141,6 @@ function dominant_color_get_dominant_color_data( int $attachment_id ) {
),
)
);
remove_filter( 'wp_image_editors', 'dominant_color_set_image_editors' );

if ( is_wp_error( $editor ) ) {
return $editor;
Expand All @@ -140,6 +167,28 @@ function dominant_color_get_dominant_color_data( int $attachment_id ) {
return $dominant_color_data;
}

/**
* Retrieves attachment metadata for an image attachment.
*
* @since n.e.x.t
*
* @param int $attachment_id Attachment ID.
* @return array<string, mixed>|null Attachment metadata array, or null if not an image or metadata unavailable.
*/
function dominant_color_get_attachment_metadata( int $attachment_id ): ?array {

if ( ! wp_attachment_is_image( $attachment_id ) ) {
return null;
}

$image_meta = wp_get_attachment_metadata( $attachment_id );
if ( ! is_array( $image_meta ) ) {
return null;
}

return $image_meta;
}

/**
* Gets file path of image based on size.
*
Expand All @@ -150,21 +199,17 @@ function dominant_color_get_dominant_color_data( int $attachment_id ) {
* @return false|string Path to an image or false if not found.
*/
function dominant_color_get_attachment_file_path( int $attachment_id, string $size = 'medium' ) {
$imagedata = wp_get_attachment_metadata( $attachment_id );
if ( ! is_array( $imagedata ) ) {
return false;
}

if ( ! isset( $imagedata['sizes'][ $size ] ) ) {
$filepath = get_attached_file( $attachment_id );
if ( false === $filepath ) {
return false;
}

$file = get_attached_file( $attachment_id );
if ( false === $file ) {
return false;
}
$image_meta = dominant_color_get_attachment_metadata( $attachment_id );

$filepath = str_replace( wp_basename( $file ), $imagedata['sizes'][ $size ]['file'], $file );
if ( isset( $image_meta['sizes'][ $size ] ) ) {
$filepath = str_replace( wp_basename( $filepath ), $image_meta['sizes'][ $size ]['file'], $filepath );
}

return $filepath;
}
Expand All @@ -175,18 +220,12 @@ function dominant_color_get_attachment_file_path( int $attachment_id, string $si
* @since 1.0.0
*
* @param int $attachment_id Attachment ID for image.
* @return string|null Hex value of dominant color or null if not set.
* @return non-empty-string|null Hex value of dominant color or null if not set.
*/
function dominant_color_get_dominant_color( int $attachment_id ): ?string {
if ( ! wp_attachment_is_image( $attachment_id ) ) {
return null;
}
$image_meta = wp_get_attachment_metadata( $attachment_id );
if ( ! is_array( $image_meta ) ) {
return null;
}
$image_meta = dominant_color_get_attachment_metadata( $attachment_id );

if ( ! isset( $image_meta['dominant_color'] ) ) {
if ( ! isset( $image_meta['dominant_color'] ) || ! is_string( $image_meta['dominant_color'] ) || '' === $image_meta['dominant_color'] ) {
return null;
}

Expand All @@ -202,19 +241,15 @@ function dominant_color_get_dominant_color( int $attachment_id ): ?string {
* @return bool|null Whether the image has transparency, or null if not set.
*/
function dominant_color_has_transparency( int $attachment_id ): ?bool {
$image_meta = wp_get_attachment_metadata( $attachment_id );
if ( ! is_array( $image_meta ) ) {
return null;
}
$image_meta = dominant_color_get_attachment_metadata( $attachment_id );

if ( ! isset( $image_meta['has_transparency'] ) ) {
return null;
}

return $image_meta['has_transparency'];
return (bool) $image_meta['has_transparency'];
}


/**
* Gets hex color from RGB.
*
Expand Down
2 changes: 2 additions & 0 deletions plugins/dominant-color-images/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,6 @@

require_once __DIR__ . '/helper.php';
require_once __DIR__ . '/hooks.php';

add_filter( 'wp_image_editors', 'dominant_color_filter_image_editors', 999, 1 );
// @codeCoverageIgnoreEnd
42 changes: 28 additions & 14 deletions plugins/dominant-color-images/tests/data/class-testcase.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,10 @@ public function provider_get_dominant_color_invalid_images(): array {
public function provider_get_dominant_color_none_images(): array {
return array(
'pdf' => array(
'files_path' => TESTS_PLUGIN_DIR . '/tests/data/images/wordpress-gsoc-flyer.pdf',
'image_path' => TESTS_PLUGIN_DIR . '/tests/data/images/wordpress-gsoc-flyer.pdf',
),
'mp4' => array(
'files_path' => TESTS_PLUGIN_DIR . '/tests/data/images/small-video.mp4',
'image_path' => TESTS_PLUGIN_DIR . '/tests/data/images/small-video.mp4',
),
);
}
Expand All @@ -182,7 +182,6 @@ public function test_get_dominant_color_valid( string $image_path, array $expect
}

$attachment_id = self::factory()->attachment->create_upload_object( $image_path );
wp_maybe_generate_attachment_metadata( get_post( $attachment_id ) );

$dominant_color_data = dominant_color_get_dominant_color_data( $attachment_id );

Expand All @@ -208,47 +207,62 @@ public function test_get_dominant_color_invalid( string $image_path ): void {
if ( ! wp_image_editor_supports( array( 'mime_type' => $mime_type ) ) ) {
$this->markTestSkipped( "Mime type $mime_type is not supported." );
}
$attachment_id = self::factory()->attachment->create_upload_object( $image_path );
wp_maybe_generate_attachment_metadata( get_post( $attachment_id ) );
$attachment_id = self::factory()->attachment->create(
array(
'post_mime_type' => $mime_type,
)
);

$dominant_color_data = dominant_color_get_dominant_color_data( $attachment_id );

$this->assertWPError( $dominant_color_data );
$this->assertStringContainsString( 'unsupported_attachment_type', $dominant_color_data->get_error_code() );
$this->assertSame( 'unsupported_attachment_type', $dominant_color_data->get_error_code() );
}

/**
* Test the function returns a WP_Error object for unsupported mime types.
* Tests dominant_color_get_dominant_color_data() returns a WP_Error when the
* dominant_color_supported_mime_types filter returns an empty array.
*
* @covers dominant_color_get_dominant_color_data
*/
public function test_get_dominant_color_data_unsupported_mime_type(): void {
add_filter( 'dominant_color_supported_mime_types', '__return_empty_array' );
$image_path = TESTS_PLUGIN_DIR . '/tests/data/images/red.jpg';

$attachment_id = self::factory()->attachment->create_upload_object( $image_path );
wp_maybe_generate_attachment_metadata( get_post( $attachment_id ) );
$attachment_id = self::factory()->attachment->create(
array(
'post_mime_type' => 'image/jpeg',
)
);

$dominant_color_data = dominant_color_get_dominant_color_data( $attachment_id );

$this->assertWPError( $dominant_color_data );
$this->assertStringContainsString( 'unsupported_attachment_type', $dominant_color_data->get_error_code() );
$this->assertSame( 'unsupported_attachment_type', $dominant_color_data->get_error_code() );
}

/**
* Test if the function returns the correct color.
* Tests dominant_color_get_dominant_color_data() returns a WP_Error for non-image file types.
*
* @covers Dominant_Color_Image_Editor_GD::get_dominant_color
* @covers Dominant_Color_Image_Editor_Imagick::get_dominant_color
*
* @dataProvider provider_get_dominant_color_none_images
*/
public function test_get_dominant_color_none_images( string $image_path ): void {
$attachment_id = self::factory()->attachment->create_upload_object( $image_path );
wp_maybe_generate_attachment_metadata( get_post( $attachment_id ) );
$mime_type = wp_check_filetype( $image_path )['type'];
if ( false === $mime_type ) {
$this->markTestSkipped( 'Mime type is not supported.' );
}

$attachment_id = self::factory()->attachment->create(
array(
'post_mime_type' => $mime_type,
)
);

$dominant_color_data = dominant_color_get_dominant_color_data( $attachment_id );

$this->assertWPError( $dominant_color_data );
$this->assertSame( 'unsupported_attachment_type', $dominant_color_data->get_error_code() );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
* Tests for Image Placeholders plugin.
*
* @package dominant-color-images
* @noinspection PhpComposerExtensionStubsInspection
*/

use Dominant_Color_Images\Tests\TestCase;

/**
* @coversDefaultClass Dominant_Color_Image_Editor_GD
*/
class Test_Dominant_Color_Image_Editor_GD extends TestCase {

/**
Expand All @@ -19,6 +23,11 @@ public function set_up(): void {
$this->markTestSkipped( 'The GD PHP extension is not loaded.' );
}

// Ensure the GD editor is registered. It doesn't seem to be by the time this runs.
require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
require_once ABSPATH . WPINC . '/class-wp-image-editor-gd.php';
require_once __DIR__ . '/../class-dominant-color-image-editor-gd.php';

add_filter(
'wp_image_editors',
static function ( array $editors ): array {
Expand All @@ -31,4 +40,93 @@ static function ( $editor ): bool {
}
);
}

/**
* @covers ::get_dominant_color
*/
public function test_invalid_image_type(): void {
$editor = new Dominant_Color_Image_Editor_GD( '/invalid/type' );
$result = $editor->get_dominant_color();
$this->assertWPError( $result );
$this->assertSame( 'image_editor_dominant_color_error_no_image', $result->get_error_code() );
}

/**
* @covers ::get_dominant_color
*/
public function test_get_dominant_color_no_image(): void {
$editor = new Dominant_Color_Image_Editor_GD( null );
$result = $editor->get_dominant_color();

$this->assertWPError( $result );
$this->assertSame( 'image_editor_dominant_color_error_no_image', $result->get_error_code() );
}

/**
* @covers ::has_transparency
*/
public function test_has_transparency_no_image(): void {
$editor = new Dominant_Color_Image_Editor_GD( null );
$result = $editor->has_transparency();

$this->assertWPError( $result );
$this->assertSame( 'image_editor_has_transparency_error_no_image', $result->get_error_code() );
}

/**
* @covers ::get_dominant_color
*/
public function test_get_dominant_color_success(): void {
$im = imagecreatetruecolor( 1, 1 );
$red = imagecolorallocate( $im, 255, 0, 0 );
imagefill( $im, 0, 0, $red );

$editor = new Dominant_Color_Image_Editor_GD( null );
$reflection = new ReflectionClass( $editor );
$property = $reflection->getProperty( 'image' );
$property->setAccessible( true );
$property->setValue( $editor, $im );

$result = $editor->get_dominant_color();

$this->assertSame( 'ff0000', $result );
}

/**
* @covers ::has_transparency
*/
public function test_has_no_transparency(): void {
$im = imagecreatetruecolor( 1, 1 );
$red = imagecolorallocate( $im, 255, 0, 0 );
imagefill( $im, 0, 0, $red );

$editor = new Dominant_Color_Image_Editor_GD( null );
$reflection = new ReflectionClass( $editor );
$property = $reflection->getProperty( 'image' );
$property->setAccessible( true );
$property->setValue( $editor, $im );

$result = $editor->has_transparency();

$this->assertFalse( $result );
}

/**
* @covers ::has_transparency
*/
public function test_has_transparency_with_transparency(): void {
$im = imagecreatetruecolor( 1, 1 );
$alpha_color = imagecolorallocatealpha( $im, 255, 0, 0, 64 );
imagefill( $im, 0, 0, $alpha_color );

$editor = new Dominant_Color_Image_Editor_GD( null );
$reflection = new ReflectionClass( $editor );
$property = $reflection->getProperty( 'image' );
$property->setAccessible( true );
$property->setValue( $editor, $im );

$result = $editor->has_transparency();

$this->assertTrue( $result );
}
}
Loading
Loading