Créer des traitements personnalisés pour CarrierWave
Si vous avez besoin de traiter des images sur une application rails, je ne peux que vous conseiller CarrierWave.
Cette librairie vous permettra aisément de gérer de l'upload de fichier. Mais également du post traitement automatisé sur les images, tel que le redimensionnement de celles-ci.
J'utilise CarrierWave sur ce blog, pour une refonte graphique qui devrait être publiée dans deux à trois semaines.
Chaque catégorie et chaque article as ainsi une image. Cette image possède différentes versions qui sont chacune plus ou moins grande.
Pour l'une de ces versions, je souhaite obtenir des bords arrondis.
Le résultat désiré est l'image à gauche ici.
Voyons en détails comment j'en suis arrivé au résultat ci-contre.
Post processing CarrierWave
Imaginons le modèle d'upload suivant :
class CategoryUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
version :thumb do
process :resize_to_fill => [248, 163]
end
end
Nous avons ici un uploader de catégorie qui créera une seconde image "thumb". Cette image sera de taille 248 * 163 pixels.
Vous pouvez trouver le processeur "resize_to_fill", qui redimensionnera l'image à lib/carrierwave/processing/rmagick.rb.
Afin de créer des bords arrondis, nous allons devoir créer notre propre processeur. Je l'ai placé dans lib/dmathieu/carrier_wave/round.rb.
unless defined? Magick
begin
require 'rmagick'
rescue LoadError
require 'RMagick'
rescue LoadError
puts "WARNING: Failed to require rmagick, image processing may fail!"
end
end
module Dmathieu
module CarrierWave
module Round
module ClassMethods
def rounded_corner(radius = 10)
process :rounded_corner => [radius]
end
end
##
# Makes the image's corners round
#
#
# === Parameters
#
# [radius (#to_s)] the corner radius
#
# === Yields
#
# [Magick::Image] additional manipulations to perform
#
def rounded_corner(radius = 10)
#
# Voir plus loin pour le code métier
#
end
end
end
Puis, dans votre uploader, vous devez inclure ce processeur nouvellement créé.
class CategoryUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
include Dmathieu::CarrierWave::Round
version :thumb do
process :resize_to_fill => [248, 163]
process :rounded_corner
end
end
Vous pouvez constater que, dans notre module, nous créons une méthode rounded_corner, qui fera l'action d'ajouter des coins arrondis à notre image. Cette action sera appelée automatiquement par CarrierWave lors de la génération de cette version de l'image.
Le code métier
Afin de manipuler notre image, nous utilisons rmagick.
Pour créer notre image, nous allons faire la chose suivante :
- Créer une nouvelle image de la même taille que la notre, qui contiendra un rectangle aux bords arrondis.
- Placer ce rectangle au dessus de notre image.
Plutôt simple non ? ;-)
def rounded_corner(radius = 10)
manipulate! do |img|
masq = ::Magick::Image.new(img.columns, img.rows).matte_floodfill(1, 1)
::Magick::Draw.new.
fill('transparent').
stroke('black').
stroke_width(1).
roundrectangle(0, 0, img.columns - 1, img.rows - 1, radius, radius).
draw(masq)
img.composite!(masq, 0, 0, Magick::LightenCompositeOp)
img = yield(img) if block_given?
img
end
end
Nous venons ici de définir la méthode rounded_corner, qui se situe dans le module expliqué plus haut.
La méthode manipulate! se situe dans le processeur rmagick.rb et nous permet d'obtenir l'image courante, de la modifier et de la transmettre au processeur suivant (via le yield).
masq = ::Magick::Image.new(img.columns, img.rows).matte_floodfill(1, 1)
Nous créons ici une nouvelle image de la même taille que la précédente mais avec intégralement transparente.
::Magick::Draw.new.
fill('transparent').
stroke('black').
stroke_width(1).
roundrectangle(0, 0, img.columns - 1, img.rows - 1, radius, radius).
draw(masq)
Nous dessinons maintenant dans l'image précédemment créée.
fill('transparent').
Le fonds du rectangle que nous dessinons sera transparent.
stroke('black').
stroke_width(1).
Notre rectangle aura une bordure noire de 1 pixel.
roundrectangle(0, 0, img.columns - 1, img.rows - 1, radius, radius).
draw(masq)
Nous créons ici le rectangle et l'ajoutons à l'image créée juste auparavant.
img.composite!(masq, 0, 0, Magick::LightenCompositeOp)
Enfin nous superposons cette image nouvellement créée à celle qui existe déjà, rendant l'effet escompté.
img = yield(img) if block_given?
img
CarrierWave fonctionne de manière imbriquée. Chaque processeur est exécuté l'un après l'autre, transmettant ensuite la main au suivant et retournant l'image modifiée. C'est ce que nous faisons ici avec yield.
Et enfin nous retournons l'image qui pourra alors être sauvegardée.
Conclusion
Comme vous pouvez le constater, CarrierWave permet de créer des processeurs de manière fortement générique afin de rendre vos images telles que vous en avez besoin.
Je suis d'ailleurs assez étonné qu'il n'y ait pas déjà des processeurs open source qui se baladent.
