Class: Plugin::Thumbnailer::Service

Inherits:
Object
  • Object
show all
Extended by:
Stats
Includes:
ClassLogging
Defined in:
lib/httpthumbnailer/plugin/thumbnailer/service.rb,
lib/httpthumbnailer/plugin/thumbnailer/service/images.rb,
lib/httpthumbnailer/plugin/thumbnailer/service/built_in_plugins.rb

Defined Under Namespace

Modules: MimeType Classes: InputImage, Thumbnail

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Service

Returns a new instance of Service.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/httpthumbnailer/plugin/thumbnailer/service.rb', line 52

def initialize(options = {})
	InputImage.logger = logger_for(InputImage)
	Thumbnail.logger = logger_for(Thumbnail)
	Magick::Image.logger = logger_for(Magick::Image)

	@thumbnailing_methods = {}
	@edits = {}
	@options = options
	@images_loaded = 0

	log.info "initializing thumbnailer: #{self.class.rmagick_version} #{self.class.magick_version}"

	set_limit(:area, options[:limit_area]) if options.member?(:limit_area)
	set_limit(:memory, options[:limit_memory]) if options.member?(:limit_memory)
	set_limit(:map, options[:limit_map]) if options.member?(:limit_map)
	set_limit(:disk, options[:limit_disk]) if options.member?(:limit_disk)

	Magick.trace_proc = lambda do |which, description, id, method|
		case which
		when :c
			Service.stats.incr_images_loaded
			@images_loaded += 1
			Service.stats.max_images_loaded = Service.stats.images_loaded if Service.stats.images_loaded > Service.stats.max_images_loaded
			Service.stats.max_images_loaded_worker = @images_loaded if @images_loaded > Service.stats.max_images_loaded_worker
			Service.stats.incr_total_images_created
			case method
			when :from_blob
				Service.stats.incr_total_images_created_from_blob
			when :initialize
				Service.stats.incr_total_images_created_initialize
			when :initialize_copy
				Service.stats.incr_total_images_created_initialize_copy
			when :resize
				Service.stats.incr_total_images_created_resize
			when :resize!
				Service.stats.incr_total_images_created_resize
			when :crop
				Service.stats.incr_total_images_created_crop
			when :crop!
				Service.stats.incr_total_images_created_crop
			when :sample
				Service.stats.incr_total_images_created_sample
			when :blur_image
				Service.stats.incr_total_images_created_blur_image
			when :composite
				Service.stats.incr_total_images_created_composite
			when :rotate
				Service.stats.incr_total_images_created_rotate
			else
				log.warn "uncounted image creation method: #{method}"
			end
		when :d
			Service.stats.decr_images_loaded
			@images_loaded -= 1
			Service.stats.incr_total_images_destroyed
		end
		log.debug{"image event: #{which}, #{description}, #{id}, #{method}: loaded images: #{Service.stats.images_loaded}"}
	end
end

Class Method Details

.built_in_pluginObject



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/httpthumbnailer/plugin/thumbnailer/service/built_in_plugins.rb', line 4

def self.built_in_plugin
	PluginContext.new do
		thumbnailing_method('crop') do |image, width, height, options|
			image.resize_to_fill(width, height, float!('float-x', options['float-x'], 0.5), float!('float-y', options['float-y'], 0.5)) if image.width != width or image.height != height
		end

		thumbnailing_method('fit') do |image, width, height, options|
			image.resize_to_fit(width, height) if image.width != width or image.height != height
		end

		thumbnailing_method('pad') do |image, width, height, options|
			image.resize_to_fit(width, height).get do |resize|
				resize.render_on_background(options['background-color'], width, height, float!('float-x', options['float-x'], 0.5), float!('float-y', options['float-y'], 0.5))
			end if image.width != width or image.height != height
		end

		thumbnailing_method('limit') do |image, width, height, options|
			image.resize_to_fit(width, height) if image.width > width or image.height > height
		end

		edit('resize_crop') do |image, width, height, options, thumbnail_spec|
			width = float!('width', width)
			height = float!('height', height)

			image.resize_to_fill(width, height, ufloat!('float-x', options['float-x'], 0.5), ufloat!('float-y', options['float-y'], 0.5)) if image.width != width or image.height != height
		end

		edit('resize_fit') do |image, width, height, options, thumbnail_spec|
			width = float!('width', width)
			height = float!('height', height)

			image.resize_to_fit(width, height) if image.width != width or image.height != height
		end

		edit('resize_limit') do |image, width, height, options, thumbnail_spec|
			width = float!('width', width)
			height = float!('height', height)

			image.resize_to_fit(width, height) if image.width > width or image.height > height
		end

		edit('rotate') do |image, angle, options, thumbnail_spec|
			angle = float!('angle', angle)
			next image if angle % 360 == 0
			image.with_background_color(options['background-color'] || thumbnail_spec.options['background-color']) do
				image.rotate(angle)
			end
		end

		edit('crop') do |image, x, y, width, height, options, thumbnail_spec|
			x, y, width, height = normalize_region(
				float!('x', x),
				float!('y', y),
				float!('width', width),
				float!('height', height)
			)

			next image if [x, y, width, height] == [0.0, 0.0, 1.0, 1.0]

			image.crop(
				*image.rel_to_px_box(x, y, width, height),
				true
			)
		end

		edit('pixelate') do |image, box_x, box_y, box_width, box_height, options, thumbnail_spec|
			x, y, width, height = normalize_region(
				float!('box_x', box_x),
				float!('box_y', box_y),
				float!('box_width', box_width),
				float!('box_height', box_height)
			)
			size = ufloat!('size', options['size'], 0.01)

			image.pixelate_region(
				*image.rel_to_px_box(x, y, width, height),
				image.rel_to_diagonal(size)
			)
		end

		edit('blur') do |image, box_x, box_y, box_width, box_height, options, thumbnail_spec|
			x, y, width, height = normalize_region(
				float!('box_x', box_x),
				float!('box_y', box_y),
				float!('box_width', box_width),
				float!('box_height', box_height)
			)

			radius = ufloat!('radius', options['radius'], 0.0) # auto
			sigma = ufloat!('sigma', options['sigma'], 0.01)

			radius = image.rel_to_diagonal(radius)
			sigma = image.rel_to_diagonal(sigma)

			if radius > 50
				log.warn "limiting effective radius from #{radius} down to 50"
				radius = 50
			end

			if sigma > 50
				log.warn "limiting effective sigma from #{sigma} down to 50"
				sigma = 50
			end

			image.blur_region(
				*image.rel_to_px_box(x, y, width, height),
				radius, sigma
			)
		end

		edit('rectangle') do |image, box_x, box_y, box_width, box_height, options, thumbnail_spec|
			x, y, width, height = normalize_region(
				float!('box_x', box_x),
				float!('box_y', box_y),
				float!('box_width', box_width),
				float!('box_height', box_height)
			)

			color = options['color'] || 'black'

			image.render_rectangle(
				*image.rel_to_px_box(x, y, width, height),
				color
			)
		end
	end
end

.input_formatsObject



32
33
34
35
36
# File 'lib/httpthumbnailer/plugin/thumbnailer/service.rb', line 32

def self.input_formats
	Magick.formats.select do |name, mode|
		mode.include? 'r'
	end.keys.map(&:downcase)
end

.magick_versionObject



48
49
50
# File 'lib/httpthumbnailer/plugin/thumbnailer/service.rb', line 48

def self.magick_version
	Magick::Magick_version
end

.output_formatsObject



38
39
40
41
42
# File 'lib/httpthumbnailer/plugin/thumbnailer/service.rb', line 38

def self.output_formats
	Magick.formats.select do |name, mode|
		mode.include? 'w'
	end.keys.map(&:downcase)
end

.rmagick_versionObject



44
45
46
# File 'lib/httpthumbnailer/plugin/thumbnailer/service.rb', line 44

def self.rmagick_version
	Magick::Version
end

Instance Method Details

#edit(name, &impl) ⇒ Object



135
136
137
138
# File 'lib/httpthumbnailer/plugin/thumbnailer/service.rb', line 135

def edit(name, &impl)
	log.info "adding edit: #{name}(#{impl.parameters.drop(1).reverse.drop(2).reverse.map{|p| p.last.to_s}.join(', ')})"
	@edits[name] = impl
end

#load(io, options = {}, &block) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/httpthumbnailer/plugin/thumbnailer/service.rb', line 112

def load(io, options = {}, &block)
	blob = io.read

	old_memory_limit = nil
	borrowed_memory_limit = nil
	if options.member?(:limit_memory)
		borrowed_memory_limit = options[:limit_memory].borrow(options[:limit_memory].limit, 'image magick')
		old_memory_limit = set_limit(:memory, borrowed_memory_limit)
	end

	InputImage.from_blob(blob, @thumbnailing_methods, @edits, options, &block)
ensure
	if old_memory_limit
		set_limit(:memory, old_memory_limit)
		options[:limit_memory].return(borrowed_memory_limit, 'image magick')
	end
end

#load_plugin(plugin_context) ⇒ Object



146
147
148
149
150
151
152
153
154
# File 'lib/httpthumbnailer/plugin/thumbnailer/service.rb', line 146

def load_plugin(plugin_context)
	plugin_context.thumbnailing_methods.each do |name, block|
		thumbnailing_method(name, &block)
	end

	plugin_context.edits.each do |name, block|
		edit(name, &block)
	end
end

#set_limit(limit, value) ⇒ Object



140
141
142
143
144
# File 'lib/httpthumbnailer/plugin/thumbnailer/service.rb', line 140

def set_limit(limit, value)
	old = Magick.limit_resource(limit, value)
	log.info "changed #{limit} limit from #{old} to #{value} bytes"
	old
end

#setup_built_in_pluginsObject



156
157
158
159
# File 'lib/httpthumbnailer/plugin/thumbnailer/service.rb', line 156

def setup_built_in_plugins
	log.info("loading built in plugins")
	load_plugin(self.class.built_in_plugin)
end

#thumbnailing_method(method, &impl) ⇒ Object



130
131
132
133
# File 'lib/httpthumbnailer/plugin/thumbnailer/service.rb', line 130

def thumbnailing_method(method, &impl)
	log.info "adding thumbnailing method: #{method}"
	@thumbnailing_methods[method] = impl
end