-
Notifications
You must be signed in to change notification settings - Fork 0
/
png.cr
82 lines (71 loc) · 2.5 KB
/
png.cr
1
2
3
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
require "png"
# Provides methods to read from and write to PNG. Requires `libspng` to function.
#
# ```
# image = File.open("image.png") { |file| CrImage::RGBAImage.from_png(file) }
# File.open("other_image.png") { |file| image.to_png(file) }
# ```
# Alternatively, you can use the convenience methods in the `Open` and `Save` modules
# to acheive the same thing:
# ```
# image = CrImage::RGBAImage.open("image.png")
# image.save("other_image.png")
# ```
module CrImage::Format::PNG
{% CrImage::Format::SUPPORTED_FORMATS << {extension: ".png", method: "png"} %}
macro included
# Read `image_data` and PNG encoded bytes
def self.from_png(image_data : Bytes) : self
from_png(IO::Memory.new(image_data))
end
# Construct an Image by reading bytes from `io`
def self.from_png(io : IO) : self
png = ::PNG.read(io)
width = png.width.to_i32
height = png.height.to_i32
red = Array.new(width * height) { ChannelType::Red.default }
green = Array.new(width * height) { ChannelType::Green.default }
blue = Array.new(width * height) { ChannelType::Blue.default }
alpha = Array.new(width * height) { ChannelType::Alpha.default }
red_offset = 0
green_offset = 1
blue_offset = 2
alpha_offset = 3
jump = png.color_type.channels
case jump
when 1
red_offset = green_offset = blue_offset = 0
alpha_offset = -1
when 2
red_offset = green_offset = blue_offset = 0
alpha_offset = 1
when 3
alpha_offset = -1
end
(width * height).times do |index|
png.data[index * jump]
red.unsafe_put(index, png.data[index * jump + red_offset])
green.unsafe_put(index, png.data[index * jump + green_offset])
blue.unsafe_put(index, png.data[index * jump + blue_offset])
if alpha_offset > -1
alpha.unsafe_put(index, png.data[index * jump + alpha_offset])
end
end
new(red, green, blue, alpha, width, height)
end
end
# Output the image as PNG to `io`
def to_png(io : IO) : Nil
bytes = Bytes.new(size * 4)
idx = 0
(size * 4).times.step(4).each do |index|
bytes.unsafe_put(index, red[idx])
bytes.unsafe_put(index + 1, green[idx])
bytes.unsafe_put(index + 2, blue[idx])
bytes.unsafe_put(index + 3, alpha[idx])
idx += 1
end
canvas = ::PNG::Canvas.new(::PNG::Header.new(width.to_u32, height.to_u32, color_type: ::PNG::ColorType::TrueColorAlpha), bytes)
::PNG.write(io, canvas)
end
end