__('Thumbnail (400x300)', 'roi-theme'), 'roi-medium' => __('Medium (800x600)', 'roi-theme'), 'roi-large' => __('Large (1200x900)', 'roi-theme'), 'roi-featured-large' => __('Featured Large (1200x600)', 'roi-theme'), 'roi-featured-medium' => __('Featured Medium (800x400)', 'roi-theme'), 'roi-hero' => __('Hero (1920x800)', 'roi-theme'), 'roi-card' => __('Card (600x400)', 'roi-theme'), )); } add_filter('image_size_names_choose', 'roi_custom_image_sizes'); /** * Generate srcset and sizes attributes for responsive images * * @param int $attachment_id Image attachment ID * @param string $size Image size * @param array $additional_sizes Additional sizes to include in srcset * @return array Array with 'srcset' and 'sizes' attributes */ function roi_get_responsive_image_attrs($attachment_id, $size = 'full', $additional_sizes = array()) { if (empty($attachment_id)) { return array('srcset' => '', 'sizes' => ''); } // Get default WordPress srcset $srcset = wp_get_attachment_image_srcset($attachment_id, $size); // Get default WordPress sizes attribute $sizes = wp_get_attachment_image_sizes($attachment_id, $size); // Add additional sizes if specified if (!empty($additional_sizes)) { $srcset_array = array(); foreach ($additional_sizes as $additional_size) { $image = wp_get_attachment_image_src($attachment_id, $additional_size); if ($image) { $srcset_array[] = $image[0] . ' ' . $image[1] . 'w'; } } if (!empty($srcset_array)) { $srcset .= ', ' . implode(', ', $srcset_array); } } return array( 'srcset' => $srcset, 'sizes' => $sizes, ); } /** * Output responsive image with lazy loading * * @param int $attachment_id Image attachment ID * @param string $size Image size * @param array $attr Additional image attributes * @param bool $lazy Enable lazy loading (default: true) * @return string Image HTML */ function roi_get_responsive_image($attachment_id, $size = 'full', $attr = array(), $lazy = true) { if (empty($attachment_id)) { return ''; } // Get responsive attributes $responsive_attrs = roi_get_responsive_image_attrs($attachment_id, $size); // Merge default attributes with custom ones $default_attr = array( 'loading' => $lazy ? 'lazy' : 'eager', 'decoding' => 'async', ); // Add srcset and sizes if available if (!empty($responsive_attrs['srcset'])) { $default_attr['srcset'] = $responsive_attrs['srcset']; } if (!empty($responsive_attrs['sizes'])) { $default_attr['sizes'] = $responsive_attrs['sizes']; } $attr = array_merge($default_attr, $attr); return wp_get_attachment_image($attachment_id, $size, false, $attr); } /** * Enable lazy loading by default for all images */ function roi_add_lazy_loading_to_images($attr, $attachment, $size) { // Don't add lazy loading if explicitly disabled if (isset($attr['loading']) && $attr['loading'] === 'eager') { return $attr; } // Add lazy loading by default if (!isset($attr['loading'])) { $attr['loading'] = 'lazy'; } // Add async decoding for better performance if (!isset($attr['decoding'])) { $attr['decoding'] = 'async'; } return $attr; } add_filter('wp_get_attachment_image_attributes', 'roi_add_lazy_loading_to_images', 10, 3); /** * Add lazy loading to content images */ function roi_add_lazy_loading_to_content($content) { // Don't process if empty if (empty($content)) { return $content; } // Add loading="lazy" to images that don't have it $content = preg_replace('/]*loading=)/', 'ID) { $attr['fetchpriority'] = 'high'; $attr['loading'] = 'eager'; // Don't lazy load LCP images } return $attr; } add_filter('wp_get_attachment_image_attributes', 'roi_add_fetchpriority_to_featured_image', 20, 3); /** * Optimize image quality for uploads */ function roi_optimize_image_quality($quality, $mime_type) { // Set quality to 85% for better file size without visible quality loss if ($mime_type === 'image/jpeg') { return 85; } return $quality; } add_filter('jpeg_quality', 'roi_optimize_image_quality', 10, 2); add_filter('wp_editor_set_quality', 'roi_optimize_image_quality', 10, 2); /** * Add picture element support for WebP/AVIF with fallbacks * * @param int $attachment_id Image attachment ID * @param string $size Image size * @param array $attr Additional attributes * @return string Picture element HTML */ function roi_get_picture_element($attachment_id, $size = 'full', $attr = array()) { if (empty($attachment_id)) { return ''; } $image_src = wp_get_attachment_image_src($attachment_id, $size); if (!$image_src) { return ''; } $srcset = wp_get_attachment_image_srcset($attachment_id, $size); $sizes = wp_get_attachment_image_sizes($attachment_id, $size); $alt = get_post_meta($attachment_id, '_wp_attachment_image_alt', true); // Default attributes $default_attr = array( 'loading' => 'lazy', 'decoding' => 'async', 'alt' => $alt, ); $attr = array_merge($default_attr, $attr); // Build picture element $picture = ''; // Add AVIF source if available $avif_url = str_replace(array('.jpg', '.jpeg', '.png', '.webp'), '.avif', $image_src[0]); if ($avif_url !== $image_src[0]) { $picture .= sprintf( '', esc_attr($avif_url), esc_attr($sizes) ); } // Add WebP source if available $webp_url = str_replace(array('.jpg', '.jpeg', '.png'), '.webp', $image_src[0]); if ($webp_url !== $image_src[0]) { $picture .= sprintf( '', esc_attr($webp_url), esc_attr($sizes) ); } // Fallback img tag $picture .= wp_get_attachment_image($attachment_id, $size, false, $attr); $picture .= ''; return $picture; } /** * Configurar threshold de escala de imágenes grandes * WordPress 5.3+ escala imágenes mayores a 2560px por defecto * Mantenemos 2560px como límite para balance entre calidad y rendimiento */ function roi_big_image_size_threshold($threshold) { // Mantener 2560px como threshold (no desactivar completamente) return 2560; } add_filter('big_image_size_threshold', 'roi_big_image_size_threshold'); /** * Set maximum srcset image width */ function roi_max_srcset_image_width($max_width, $size_array) { // Limit srcset to images up to 2560px wide return 2560; } add_filter('max_srcset_image_width', 'roi_max_srcset_image_width', 10, 2); /** * Habilitar generación automática de WebP en WordPress * WordPress 5.8+ soporta WebP nativamente si el servidor tiene soporte */ function roi_enable_webp_generation($editors) { // Verificar que GD o Imagick tengan soporte WebP if (extension_loaded('gd')) { $gd_info = gd_info(); if (!empty($gd_info['WebP Support'])) { // WebP está soportado en GD return $editors; } } if (extension_loaded('imagick')) { $imagick = new Imagick(); $formats = $imagick->queryFormats('WEBP'); if (count($formats) > 0) { // WebP está soportado en Imagick return $editors; } } return $editors; } add_filter('wp_image_editors', 'roi_enable_webp_generation'); /** * Configurar tipos MIME adicionales para WebP y AVIF */ function roi_additional_mime_types($mimes) { // WebP ya está soportado en WordPress 5.8+, pero lo agregamos por compatibilidad if (!isset($mimes['webp'])) { $mimes['webp'] = 'image/webp'; } // AVIF soportado desde WordPress 6.5+ if (!isset($mimes['avif'])) { $mimes['avif'] = 'image/avif'; } return $mimes; } add_filter('mime_types', 'roi_additional_mime_types'); /** * Remover tamaños de imagen no utilizados para ahorrar espacio */ function roi_remove_unused_image_sizes($sizes) { // Remover tamaños de WordPress que no usamos unset($sizes['medium_large']); // 768px - no necesario con nuestros tamaños custom unset($sizes['1536x1536']); // 2x medium_large - no necesario unset($sizes['2048x2048']); // 2x large - no necesario return $sizes; } add_filter('intermediate_image_sizes_advanced', 'roi_remove_unused_image_sizes'); /** * Agregar sizes attribute personalizado según contexto * Mejora la selección del tamaño correcto de imagen por el navegador */ function roi_responsive_image_sizes_attr($sizes, $size, $image_src, $image_meta, $attachment_id) { // Para imágenes destacadas grandes (single posts) if ($size === 'roi-featured-large' || $size === 'roi-large') { $sizes = '(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px'; } // Para imágenes destacadas medianas (archives) elseif ($size === 'roi-featured-medium' || $size === 'roi-medium') { $sizes = '(max-width: 768px) 100vw, (max-width: 992px) 50vw, 800px'; } // Para thumbnails (widgets, related posts) elseif ($size === 'roi-thumbnail') { $sizes = '(max-width: 576px) 100vw, 400px'; } // Para hero images elseif ($size === 'roi-hero') { $sizes = '100vw'; } return $sizes; } add_filter('wp_calculate_image_sizes', 'roi_responsive_image_sizes_attr', 10, 5); /** * Forzar regeneración de metadatos de imagen para incluir WebP/AVIF * Solo se ejecuta cuando sea necesario */ function roi_maybe_regenerate_image_metadata($metadata, $attachment_id) { // Verificar si ya tiene formatos modernos generados if (!empty($metadata) && is_array($metadata)) { // WordPress maneja automáticamente la generación de sub-formatos return $metadata; } return $metadata; } add_filter('wp_generate_attachment_metadata', 'roi_maybe_regenerate_image_metadata', 10, 2); /** * Excluir lazy loading en la primera imagen del contenido (posible LCP) */ function roi_skip_lazy_loading_first_image($content) { // Solo en posts/páginas singulares if (!is_singular()) { return $content; } // Contar si es la primera imagen static $first_image = true; if ($first_image) { // Cambiar loading="lazy" a loading="eager" en la primera imagen $content = preg_replace( '/]+?)loading=["\']lazy["\']/', '