Class: ZPNG::Image
Constant Summary collapse
- PNG_HDR =
"\x89PNG\x0d\x0a\x1a\x0a"
Instance Attribute Summary collapse
-
#chunks ⇒ Object
Returns the value of attribute chunks.
-
#data ⇒ Object
Returns the value of attribute data.
-
#header ⇒ Object
(also: #hdr)
Returns the value of attribute header.
-
#imagedata ⇒ Object
Returns the value of attribute imagedata.
-
#scanlines ⇒ Object
Returns the value of attribute scanlines.
Class Method Summary collapse
-
.load(fname) ⇒ Object
(also: load_file, from_file)
load image from file.
Instance Method Summary collapse
- #==(other_image) ⇒ Object
- #[](x, y) ⇒ Object
- #[]=(x, y, newpixel) ⇒ Object
-
#_alpha_color(color) ⇒ Object
internal helper method for color types 0 (grayscale) and 2 (truecolor).
- #adam7 ⇒ Object
- #alpha_used? ⇒ Boolean
-
#bpp ⇒ Object
image attributes.
-
#crop(params) ⇒ Object
returns new image.
-
#crop!(params) ⇒ Object
modifies this image.
-
#decode_all_scanlines ⇒ Object
we must decode all scanlines before doing any modifications or scanlines decoded AFTER modification of UPPER ones will be decoded wrong.
-
#deinterlace ⇒ Object
returns new deinterlaced image if deinterlaced OR returns self if no need to deinterlace.
- #each_block(bw, bh, &block) ⇒ Object
- #each_pixel(&block) ⇒ Object
- #export ⇒ Object
- #extract_block(x, y = nil, w = nil, h = nil) ⇒ Object
- #grayscale? ⇒ Boolean
- #height ⇒ Object
-
#initialize(x) ⇒ Image
constructor
A new instance of Image.
- #interlaced? ⇒ Boolean
- #metadata ⇒ Object
-
#new_image? ⇒ Boolean
(also: #new?)
flag that image is just created, and NOT loaded from file as in Rails’ ActiveRecord::Base#new_record?.
- #pixels ⇒ Object
- #plte ⇒ Object (also: #palette)
-
#save(fname) ⇒ Object
save image to file.
- #to_ascii(*args) ⇒ Object
-
#trns ⇒ Object
chunks access.
- #width ⇒ Object
Methods included from DeepCopyable
Constructor Details
#initialize(x) ⇒ Image
Returns a new instance of Image.
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/zpng/image.rb', line 10 def initialize x @chunks = [] case x when IO _from_string x.read when String _from_string x when Hash _from_hash x else raise "unsupported input data type #{x.class}" end if palette && hdr && hdr.depth palette.max_colors = 2**hdr.depth end end |
Instance Attribute Details
#chunks ⇒ Object
Returns the value of attribute chunks.
3 4 5 |
# File 'lib/zpng/image.rb', line 3 def chunks @chunks end |
#data ⇒ Object
Returns the value of attribute data.
3 4 5 |
# File 'lib/zpng/image.rb', line 3 def data @data end |
#header ⇒ Object Also known as: hdr
Returns the value of attribute header.
3 4 5 |
# File 'lib/zpng/image.rb', line 3 def header @header end |
#imagedata ⇒ Object
Returns the value of attribute imagedata.
3 4 5 |
# File 'lib/zpng/image.rb', line 3 def imagedata @imagedata end |
#scanlines ⇒ Object
Returns the value of attribute scanlines.
3 4 5 |
# File 'lib/zpng/image.rb', line 3 def scanlines @scanlines end |
Class Method Details
.load(fname) ⇒ Object Also known as: load_file, from_file
load image from file
33 34 35 36 37 |
# File 'lib/zpng/image.rb', line 33 def load fname open(fname,"rb") do |f| self.new(f) end end |
Instance Method Details
#==(other_image) ⇒ Object
323 324 325 326 327 |
# File 'lib/zpng/image.rb', line 323 def == other_image width == other_image.width && height == other_image.height && pixels == other_image.pixels end |
#[](x, y) ⇒ Object
202 203 204 205 |
# File 'lib/zpng/image.rb', line 202 def [] x, y x,y = adam7.convert_coords(x,y) if interlaced? scanlines[y][x] end |
#[]=(x, y, newpixel) ⇒ Object
207 208 209 210 211 |
# File 'lib/zpng/image.rb', line 207 def []= x, y, newpixel decode_all_scanlines x,y = adam7.convert_coords(x,y) if interlaced? scanlines[y][x] = newpixel end |
#_alpha_color(color) ⇒ Object
internal helper method for color types 0 (grayscale) and 2 (truecolor)
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/zpng/image.rb', line 115 def _alpha_color color return nil unless trns # For color type 0 (grayscale), the tRNS chunk contains a single gray level value, stored in the format: # # Gray: 2 bytes, range 0 .. (2^bitdepth)-1 # # For color type 2 (truecolor), the tRNS chunk contains a single RGB color value, stored in the format: # # Red: 2 bytes, range 0 .. (2^bitdepth)-1 # Green: 2 bytes, range 0 .. (2^bitdepth)-1 # Blue: 2 bytes, range 0 .. (2^bitdepth)-1 # # (If the image bit depth is less than 16, the least significant bits are used and the others are 0) # Pixels of the specified gray level are to be treated as transparent (equivalent to alpha value 0); # all other pixels are to be treated as fully opaque ( alpha = (2^bitdepth)-1 ) @alpha_color ||= case hdr.color when COLOR_GRAYSCALE v = trns.data.unpack('n')[0] & (2**hdr.depth-1) Color.from_grayscale(v, :depth => hdr.depth) when COLOR_RGB a = trns.data.unpack('n3').map{ |v| v & (2**hdr.depth-1) } Color.new(*a, :depth => hdr.depth) else raise "color2alpha only intended for GRAYSCALE & RGB color modes" end color == @alpha_color ? 0 : (2**hdr.depth-1) end |
#adam7 ⇒ Object
27 28 29 |
# File 'lib/zpng/image.rb', line 27 def adam7 @adam7 ||= Adam7Decoder.new(self) end |
#alpha_used? ⇒ Boolean
185 186 187 |
# File 'lib/zpng/image.rb', line 185 def alpha_used? @header && @header.alpha_used? end |
#bpp ⇒ Object
image attributes
165 166 167 |
# File 'lib/zpng/image.rb', line 165 def bpp @header && @header.bpp end |
#crop(params) ⇒ Object
returns new image
313 314 315 316 317 |
# File 'lib/zpng/image.rb', line 313 def crop params decode_all_scanlines # deep copy first, then crop! deep_copy.crop!(params) end |
#crop!(params) ⇒ Object
modifies this image
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
# File 'lib/zpng/image.rb', line 285 def crop! params decode_all_scanlines x,y,h,w = (params[:x]||0), (params[:y]||0), params[:height], params[:width] raise "negative params not allowed" if [x,y,h,w].any?{ |x| x < 0 } # adjust crop sizes if they greater than image sizes h = self.height-y if (y+h) > self.height w = self.width-x if (x+w) > self.width raise "negative params not allowed (p2)" if [x,y,h,w].any?{ |x| x < 0 } # delete excess scanlines at tail scanlines[(y+h)..-1] = [] if (y+h) < scanlines.size # delete excess scanlines at head scanlines[0,y] = [] if y > 0 # crop remaining scanlines scanlines.each{ |l| l.crop!(x,w) } # modify header hdr.height, hdr.width = h, w # return self self end |
#decode_all_scanlines ⇒ Object
we must decode all scanlines before doing any modifications or scanlines decoded AFTER modification of UPPER ones will be decoded wrong
215 216 217 218 219 |
# File 'lib/zpng/image.rb', line 215 def decode_all_scanlines return if @all_scanlines_decoded || new_image? @all_scanlines_decoded = true scanlines.each(&:decode!) end |
#deinterlace ⇒ Object
returns new deinterlaced image if deinterlaced OR returns self if no need to deinterlace
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 |
# File 'lib/zpng/image.rb', line 339 def deinterlace return self unless interlaced? # copy all but 'interlace' header params h = Hash[*%w'width height depth color compression filter'.map{ |k| [k.to_sym, hdr.send(k)] }.flatten] # don't auto-add palette chunk h[:palette] = nil # create new img new_img = self.class.new h # copy all but hdr/imagedata/end chunks chunks.each do |chunk| next if chunk.is_a?(Chunk::IHDR) next if chunk.is_a?(Chunk::IDAT) next if chunk.is_a?(Chunk::IEND) new_img.chunks << chunk.deep_copy end # pixel-by-pixel copy each_pixel do |c,x,y| new_img[x,y] = c end new_img end |
#each_block(bw, bh, &block) ⇒ Object
254 255 256 257 258 259 260 261 |
# File 'lib/zpng/image.rb', line 254 def each_block bw,bh, &block 0.upto(height/bh-1) do |by| 0.upto(width/bw-1) do |bx| b = extract_block(bx*bw, by*bh, bw, bh) yield b end end end |
#each_pixel(&block) ⇒ Object
329 330 331 332 333 334 335 |
# File 'lib/zpng/image.rb', line 329 def each_pixel &block height.times do |y| width.times do |x| yield(self[x,y], x, y) end end end |
#export ⇒ Object
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/zpng/image.rb', line 263 def export # fill @imagedata, if not already filled imagedata unless new_image? # delete old IDAT chunks @chunks.delete_if{ |c| c.is_a?(Chunk::IDAT) } # fill first_idat @data with compressed imagedata @chunks << Chunk::IDAT.new( :data => Zlib::Deflate.deflate(scanlines.map(&:export).join, 9) ) # delete IEND chunk(s) b/c we just added a new chunk and IEND must be the last one @chunks.delete_if{ |c| c.is_a?(Chunk::IEND) } # add fresh new IEND @chunks << Chunk::IEND.new PNG_HDR + @chunks.map(&:export).join end |
#extract_block(x, y = nil, w = nil, h = nil) ⇒ Object
246 247 248 249 250 251 252 |
# File 'lib/zpng/image.rb', line 246 def extract_block x,y=nil,w=nil,h=nil if x.is_a?(Hash) Block.new(self,x[:x], x[:y], x[:width], x[:height]) else Block.new(self,x,y,w,h) end end |
#grayscale? ⇒ Boolean
177 178 179 |
# File 'lib/zpng/image.rb', line 177 def grayscale? @header && @header.grayscale? end |
#height ⇒ Object
173 174 175 |
# File 'lib/zpng/image.rb', line 173 def height @header && @header.height end |
#interlaced? ⇒ Boolean
181 182 183 |
# File 'lib/zpng/image.rb', line 181 def interlaced? @header && @header.interlace != 0 end |
#metadata ⇒ Object
198 199 200 |
# File 'lib/zpng/image.rb', line 198 def @metadata ||= Metadata.new(self) end |
#new_image? ⇒ Boolean Also known as: new?
flag that image is just created, and NOT loaded from file as in Rails’ ActiveRecord::Base#new_record?
49 50 51 |
# File 'lib/zpng/image.rb', line 49 def new_image? @new_image end |
#plte ⇒ Object Also known as: palette
157 158 159 |
# File 'lib/zpng/image.rb', line 157 def plte @plte ||= @chunks.find{ |c| c.is_a?(Chunk::PLTE) } end |
#save(fname) ⇒ Object
save image to file
43 44 45 |
# File 'lib/zpng/image.rb', line 43 def save fname File.open(fname,"wb"){ |f| f << export } end |
#to_ascii(*args) ⇒ Object
234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/zpng/image.rb', line 234 def to_ascii *args if scanlines.any? if interlaced? height.times.map{ |y| width.times.map{ |x| self[x,y].to_ascii(*args) }.join }.join("\n") else scanlines.map{ |l| l.to_ascii(*args) }.join("\n") end else super() end end |
#trns ⇒ Object
chunks access
152 153 154 155 |
# File 'lib/zpng/image.rb', line 152 def trns # not used "@trns ||= ..." here b/c it will call find() each time of there's no TRNS chunk defined?(@trns) ? @trns : (@trns=@chunks.find{ |c| c.is_a?(Chunk::TRNS) }) end |
#width ⇒ Object
169 170 171 |
# File 'lib/zpng/image.rb', line 169 def width @header && @header.width end |