lib/chunky_png/canvas/operations.rb in chunky_png-1.0.0.beta2 vs lib/chunky_png/canvas/operations.rb in chunky_png-1.0.0.rc1
- old
+ new
@@ -9,52 +9,97 @@
# a new canvas and leave the original intact.
#
# @see ChunkyPNG::Canvas
module Operations
- # Composes another image onto this image using alpha blending.
+ # Composes another image onto this image using alpha blending. This will modify
+ # the current canvas.
#
# If you simply want to replace pixels or when the other image does not have
- # transparency, it is faster to use {#replace}.
+ # transparency, it is faster to use {#replace!}.
#
# @param [ChunkyPNG::Canvas] other The foreground canvas to compose on the
# current canvas, using alpha compositing.
# @param [Integer] offset_x The x-offset to apply the new forgeround on.
# @param [Integer] offset_y The y-offset to apply the new forgeround on.
# @return [ChunkyPNG::Canvas] Returns itself, but with the other canvas composed onto it.
# @raise [ChunkyPNG::OutOfBounds] when the other canvas doesn't fit on this one,
- # given the offset and size of the other canavs.
- # @see #replace
- def compose(other, offset_x = 0, offset_y = 0)
+ # given the offset and size of the other canvas.
+ # @see #replace!
+ # @see #compose
+ def compose!(other, offset_x = 0, offset_y = 0)
check_size_constraints!(other, offset_x, offset_y)
for y in 0...other.height do
for x in 0...other.width do
set_pixel(x + offset_x, y + offset_y, ChunkyPNG::Color.compose(other.get_pixel(x, y), get_pixel(x + offset_x, y + offset_y)))
end
end
self
end
-
+
+ # Composes another image onto this image using alpha blending. This will return
+ # a new canvas and leave the original intact.
+ #
+ # If you simply want to replace pixels or when the other image does not have
+ # transparency, it is faster to use {#replace}.
+ #
+ # @param (see #compose!)
+ # @return [ChunkyPNG::Canvas] Returns the new canvas, composed of the other 2.
+ # @raise [ChunkyPNG::OutOfBounds] when the other canvas doesn't fit on this one,
+ # given the offset and size of the other canvas.
+ #
+ # @note API changed since 1.0 - This method now no longer is in place, but returns
+ # a new canvas and leaves the original intact. Use {#compose!} if you want to
+ # compose on the canvas in place.
+ # @see #replace
+ def compose(other, offset_x = 0, offset_y = 0)
+ dup.compose!(other, offset_x, offset_y)
+ end
+
# Replaces pixels on this image by pixels from another pixels, on a given offset.
+ # This method will modify the current canvas.
#
# This will completely replace the pixels of the background image. If you want to blend
- # them with semi-transparent pixels from the foreground image, see {#compose}.
+ # them with semi-transparent pixels from the foreground image, see {#compose!}.
#
- # @return [ChunkyPNG::Canvas] Returns itself, but with the other canvas composed onto it.
+ # @param [ChunkyPNG::Canvas] other The foreground canvas to get the pixels from.
+ # @param [Integer] offset_x The x-offset to apply the new forgeround on.
+ # @param [Integer] offset_y The y-offset to apply the new forgeround on.
+ # @return [ChunkyPNG::Canvas] Returns itself, but with the other canvas placed onto it.
# @raise [ChunkyPNG::OutOfBounds] when the other canvas doesn't fit on this one,
- # given the offset and size of the other canavs.
- # @see #compose
- def replace(other, offset_x = 0, offset_y = 0)
+ # given the offset and size of the other canvas.
+ # @see #compose!
+ # @see #replace
+ def replace!(other, offset_x = 0, offset_y = 0)
check_size_constraints!(other, offset_x, offset_y)
for y in 0...other.height do
pixels[(y + offset_y) * width + offset_x, other.width] = other.pixels[y * other.width, other.width]
end
self
end
+ # Replaces pixels on this image by pixels from another pixels, on a given offset.
+ # This method will modify the current canvas.
+ #
+ # This will completely replace the pixels of the background image. If you want to blend
+ # them with semi-transparent pixels from the foreground image, see {#compose!}.
+ #
+ # @param (see #replace!)
+ # @return [ChunkyPNG::Canvas] Returns a new, combined canvas.
+ # @raise [ChunkyPNG::OutOfBounds] when the other canvas doesn't fit on this one,
+ # given the offset and size of the other canvas.
+ #
+ # @note API changed since 1.0 - This method now no longer is in place, but returns
+ # a new canvas and leaves the original intact. Use {#replace!} if you want to
+ # replace pixels on the canvas in place.
+ # @see #compose
+ def replace(other, offset_x = 0, offset_y = 0)
+ dup.replace!(other, offset_x, offset_y)
+ end
+
# Crops an image, given the coordinates and size of the image that needs to be cut out.
# This will leave the original image intact and return a new, cropped image with pixels
# copied from the original image.
#
# @param [Integer] x The x-coordinate of the top left corner of the image to be cropped.
@@ -74,154 +119,138 @@
new_pixels += pixels.slice((cy + y) * width + x, crop_width)
end
ChunkyPNG::Canvas.new(crop_width, crop_height, new_pixels)
end
- # Creates a new image, based on the current image but with a new theme color.
+ # Crops an image, given the coordinates and size of the image that needs to be cut out.
#
- # This method will replace one color in an image with another image. This is done by
- # first extracting the pixels with a color close to the original theme color as a mask
- # image, changing the color of this mask image and then apply it on the original image.
+ # This will change the size and content of the current canvas. Use {#crop} if you want to
+ # have a new canvas returned instead, leaving the current canvas intact.
#
- # Mask extraction works best when the theme colored pixels are clearly distinguishable
- # from a background color (preferably white). You can set a tolerance level to influence
- # the extraction process.
- #
- # @param [Integer] old_theme_color The original theme color in this image.
- # @param [Integer] new_theme_color The color to replace the old theme color with.
- # @param [Integer] The backrgound color opn which the theme colored pixels are placed.
- # @param [Integer] tolerance The tolerance level to use when extracting the mask image. Five is
- # the default; increase this if the masked image does not extract all the required pixels,
- # decrease it if too many pixels get extracted.
- # @return [ChunkyPNG::Canvas] Returns itself, but with the theme colored pixels changed.
- # @see #change_theme_color!
- # @see #change_mask_color!
- def change_theme_color!(old_theme_color, new_theme_color, bg_color = ChunkyPNG::Color::WHITE, tolerance = 5)
- base, mask = extract_mask(old_theme_color, bg_color, tolerance)
- mask.change_mask_color!(new_theme_color)
- self.replace(base.compose(mask))
- end
-
- # Creates a base image and a mask image from an original image that has a particular theme color.
- # This can be used to easily change a theme color in an image.
- #
- # It will extract all the pixels that look like the theme color (with a tolerance level) and put
- # these in a mask image. All the other pixels will be stored in a base image. Both images will be
- # of the exact same size as the original image. The original image will be left untouched.
- #
- # The color of the mask image can be changed with {#change_mask_color!}. This new mask image can
- # then be composed upon the base image to create an image with a new theme color. A call to
- # {#change_theme_color!} will perform this in one go.
- #
- # @param [Integer] mask_color The current theme color.
- # @param [Integer] bg_color The background color on which the theme colored pxiels are applied.
- # @param [Integer] tolerance The tolerance level to use when extracting the mask image. Five is
- # the default; increase this if the masked image does not extract all the required pixels,
- # decrease it if too many pixels get extracted.
- # @return [Array<ChunkyPNG::Canvas, ChunkyPNG::Canvas>] An array with the base canvas and the mask
- # canvas as elements.
- # @see #change_theme_color!
- # @see #change_mask_color!
- def extract_mask(mask_color, bg_color = ChunkyPNG::Color::WHITE, tolerance = 5)
- base_pixels = []
- mask_pixels = []
-
- pixels.each do |pixel|
- if ChunkyPNG::Color.alpha_decomposable?(pixel, mask_color, bg_color, tolerance)
- mask_pixels << ChunkyPNG::Color.decompose_color(pixel, mask_color, bg_color, tolerance)
- base_pixels << bg_color
- else
- mask_pixels << (mask_color & 0xffffff00)
- base_pixels << pixel
- end
- end
+ # @param [Integer] x The x-coordinate of the top left corner of the image to be cropped.
+ # @param [Integer] y The y-coordinate of the top left corner of the image to be cropped.
+ # @param [Integer] crop_width The width of the image to be cropped.
+ # @param [Integer] crop_height The height of the image to be cropped.
+ # @return [ChunkyPNG::Canvas] Returns itself, but cropped.
+ # @raise [ChunkyPNG::OutOfBounds] when the crop dimensions plus the given coordinates
+ # are bigger then the original image.
+ def crop!(x, y, crop_width, crop_height)
- [ self.class.new(width, height, base_pixels), self.class.new(width, height, mask_pixels) ]
+ raise ChunkyPNG::OutOfBounds, "Image width is too small!" if crop_width + x > width
+ raise ChunkyPNG::OutOfBounds, "Image width is too small!" if crop_height + y > height
+
+ new_pixels = []
+ for cy in 0...crop_height do
+ new_pixels += pixels.slice((cy + y) * width + x, crop_width)
+ end
+ replace_canvas!(crop_width, crop_height, new_pixels)
end
- # Changes the color of a mask image.
+ # Flips the image horizontally, leaving the original intact.
#
- # This method works on acanavs extracte out of another image using the {#extract_mask} method.
- # It can then be applied on the extracted base image. See {#change_theme_color!} to perform
- # these operations in one go.
+ # This will flip the image on its horizontal axis, e.g. pixels on the top will now
+ # be pixels on the bottom. Chaining this method twice will return the original canvas.
+ # This method will leave the original object intact and return a new canvas.
#
- # @param [Integer] new_color The color to replace the original mask color with.
- # @raise [ChunkyPNG::ExpectationFailed] when this canvas is not a mask image, i.e. its palette
- # has more than once color, disregarding transparency.
- # @see #change_theme_color!
- # @see #extract_mask
- def change_mask_color!(new_color)
- raise ChunkyPNG::ExpectationFailed, "This is not a mask image!" if palette.opaque_palette.size != 1
- pixels.map! { |pixel| (new_color & 0xffffff00) | ChunkyPNG::Color.a(pixel) }
- self
+ # @return [ChunkyPNG::Canvas] The flipped image
+ # @see #flip_horizontally!
+ def flip_horizontally
+ dup.flip_horizontally!
end
- # Flips the image horizontally.
+ # Flips the image horizontally in place.
#
# This will flip the image on its horizontal axis, e.g. pixels on the top will now
# be pixels on the bottom. Chaining this method twice will return the original canvas.
# This method will leave the original object intact and return a new canvas.
#
- # @return [ChunkyPNG::Canvas] The flipped image
- def flip_horizontally
- self.class.new(width, height).tap do |flipped|
- for y in 0...height do
- flipped.replace_row!(height - (y + 1), row(y))
- end
+ # @return [ChunkyPNG::Canvas] Itself, but flipped
+ # @see #flip_horizontally
+ def flip_horizontally!
+ for y in 0..((height - 1) >> 1) do
+ other_y = height - (y + 1)
+ other_row = row(other_y)
+ replace_row!(other_y, row(y))
+ replace_row!(y, other_row)
end
+ return self
end
- # Flips the image horizontally.
+ alias_method :flip!, :flip_horizontally!
+ alias_method :flip, :flip_horizontally
+
+ # Flips the image vertically, leaving the orginial intact.
#
# This will flip the image on its vertical axis, e.g. pixels on the left will now
# be pixels on the right. Chaining this method twice will return the original canvas.
# This method will leave the original object intact and return a new canvas.
#
# @return [ChunkyPNG::Canvas] The flipped image
+ # @see #flip_vertically!
def flip_vertically
- self.class.new(width, height).tap do |flipped|
- for x in 0...width do
- flipped.replace_column!(width - (x + 1), column(x))
- end
+ dup.flip_vertically!
+ end
+
+ # Flips the image vertically in place.
+ #
+ # This will flip the image on its vertical axis, e.g. pixels on the left will now
+ # be pixels on the right. Chaining this method twice will return the original canvas.
+ # This method will leave the original object intact and return a new canvas.
+ #
+ # @return [ChunkyPNG::Canvas] Itself, but flipped
+ # @see #flip_vertically
+ def flip_vertically!
+ for y in 0...height do
+ replace_row!(y, row(y).reverse)
end
+ return self
end
+
+ alias_method :mirror!, :flip_vertically!
+ alias_method :mirror, :flip_vertically
# Rotates the image 90 degrees clockwise.
# This method will leave the original object intact and return a new canvas.
#
# @return [ChunkyPNG::Canvas] The rotated image
def rotate_right
- self.class.new(height, width).tap do |rotated|
- for i in 0...width do
- rotated.replace_row!(i, column(i).reverse)
- end
+ rotated = self.class.new(height, width)
+ for i in 0...width do
+ rotated.replace_row!(i, column(i).reverse)
end
+ return rotated
end
# Rotates the image 90 degrees counter-clockwise.
# This method will leave the original object intact and return a new canvas.
#
# @return [ChunkyPNG::Canvas] The rotated image.
def rotate_left
- self.class.new(height, width).tap do |rotated|
- for i in 0...width do
- rotated.replace_row!(width - (i + 1), column(i))
- end
+ rotated = self.class.new(height, width)
+ for i in 0...width do
+ rotated.replace_row!(width - (i + 1), column(i))
end
+ return rotated
end
-
+
# Rotates the image 180 degrees.
# This method will leave the original object intact and return a new canvas.
#
# @return [ChunkyPNG::Canvas] The rotated image.
+ # @see #rotate_180!
def rotate_180
- self.class.new(width, height).tap do |flipped|
- for y in 0...height do
- flipped.replace_row!(height - (y + 1), row(y).reverse)
- end
- end
+ dup.rotate_180!
end
-
+
+ # Rotates the image 180 degrees in place.
+ #
+ # @return [ChunkyPNG::Canvas] Itself, but rotated 180 degrees.
+ # @see #rotate_180
+ def rotate_180!
+ pixels.reverse!
+ return self
+ end
+
protected
# Checks whether another image has the correct dimension to be used for an operation
# on the current image, given an offset coordinate to work with.
# @param [ChunkyPNG::Canvas] other The other canvas