Created
June 29, 2011 13:32
-
-
Save jspillers/1053837 to your computer and use it in GitHub Desktop.
Solution for codebrawl PNG pixelation challenge
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rubygems' | |
require 'chunky_png' | |
# Pixelator will pixelate your pngs | |
# defaults to a 10 pixel block, but will gladly accept | |
# larger or smaller blocks | |
# also includes a pointelize method which does color averaged dots | |
# instead of blocks | |
class Pixelator | |
include ChunkyPNG::Color | |
def initialize(image_path) | |
@image = ChunkyPNG::Image.from_file(image_path) | |
end | |
# anytime we set the block size the number of block rows | |
# and block columns needs to be updated as well | |
def set_block_size(blck_size) | |
@block_size = blck_size | |
@col_blocks = (@image.width.to_f / @block_size.to_f) | |
@row_blocks = (@image.height.to_f / @block_size.to_f) | |
end | |
def pixelate(blck_size=10) | |
set_block_size(blck_size) | |
# iterate through each "block" as defined by the block size | |
each_block do |x, y| | |
# setting current block size handles partial blocks at the ends of rows, columns, or both | |
@curr_x_block_size = x + 1 > @col_blocks ? @image.width - (@col_blocks.to_i * @block_size) : @block_size | |
@curr_y_block_size = y + 1 > @row_blocks ? @image.height - (@row_blocks.to_i * @block_size) : @block_size | |
# gets an array containing the averages for each color channel | |
averages = average_color_for_block(x, y, @curr_x_block_size, @curr_y_block_size) | |
# set every pixel from the current block to the averaged color value | |
each_pixel_from_block(@curr_x_block_size, @curr_y_block_size) do |xp, yp| | |
@image[(x * @block_size) + xp, (y * @block_size) + yp] = rgba(averages[0], averages[1], averages[2], averages[3]) | |
end | |
end | |
@image | |
end | |
def pointelize(blck_size=10, padding=2) | |
set_block_size(blck_size) | |
each_block do |x, y| | |
# setting current block size handles partial blocks at the ends of rows, columns, or both | |
@curr_x_block_size = x + 1 > @col_blocks ? @image.width - (@col_blocks.to_i * @block_size) : @block_size | |
@curr_y_block_size = y + 1 > @row_blocks ? @image.height - (@row_blocks.to_i * @block_size) : @block_size | |
averages = average_color_for_block(x, y, @curr_x_block_size, @curr_y_block_size) | |
averaged_color = rgba(averages[0], averages[1], averages[2], averages[3]) | |
# paint the block all white | |
each_pixel_from_block(@curr_x_block_size, @curr_y_block_size) do |xp, yp| | |
@image[(x * @block_size) + xp, (y * @block_size) + yp] = rgba(0, 0, 0, averages[3]) | |
end | |
# draw the circle | |
@image.circle( | |
x * @block_size + (@block_size / 2), | |
y * @block_size + (@block_size / 2), | |
(@block_size - padding) / 2, | |
averaged_color, | |
averaged_color | |
) | |
end | |
@image | |
end | |
private | |
def average_color_for_block(x, y, x_block_size, y_block_size) | |
red = [] | |
green = [] | |
blue = [] | |
alpha = [] | |
# iterate through the pixels in the current block and pull out all the color info | |
each_pixel_from_block(x_block_size, y_block_size) do |xp, yp| | |
pixel = @image[(x * @block_size) + xp, (y * @block_size) + yp] | |
red << r(pixel) | |
green << g(pixel) | |
blue << b(pixel) | |
alpha << a(pixel) | |
end | |
averages = [] | |
# average each color channel | |
[red, green, blue, alpha].each do |color| | |
averages << color.inject{ |sum,i| sum + i } / color.size | |
end | |
averages | |
end | |
def each_block | |
@row_blocks.ceil.times do |y| | |
@col_blocks.ceil.times do |x| | |
yield(x,y) | |
end | |
end | |
end | |
def each_pixel_from_block(x_block_size, y_block_size) | |
(0..x_block_size - 1).each do |xp| | |
(0..y_block_size - 1).each do |yp| | |
yield(xp, yp) | |
end | |
end | |
end | |
end | |
# demo usage of each method | |
p = Pixelator.new('input.png') | |
i = p.pointelize(20, 5) | |
i.save("pointelized.png") | |
p = Pixelator.new('input.png') | |
i = p.pixelate | |
i.save("pixelated.png") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment