rixmap 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +38 -0
- data/README.markdown +74 -0
- data/Rakefile +156 -0
- data/lib/rixmap/format/bmp.rb +326 -0
- data/lib/rixmap/format/pcx.rb +438 -0
- data/lib/rixmap/format/png/chunk.rb +239 -0
- data/lib/rixmap/format/png/imageio.rb +288 -0
- data/lib/rixmap/format/png.rb +78 -0
- data/lib/rixmap/format/xpm.rb +446 -0
- data/lib/rixmap/format.rb +10 -0
- data/lib/rixmap/version.rb +26 -0
- data/lib/rixmap.rb +24 -0
- data/spec/binary_spec.rb +12 -0
- data/spec/color_spec.rb +76 -0
- data/spec/image_spec.rb +297 -0
- data/spec/mode_spec.rb +71 -0
- data/spec/palette_spec.rb +77 -0
- data/src/chollas/LICENSE.txt +38 -0
- data/src/chollas/README.md +23 -0
- data/src/chollas/alloc.hxx +62 -0
- data/src/chollas/endian.hxx +42 -0
- data/src/chollas/raser.hxx +113 -0
- data/src/chollas/utilities.hxx +58 -0
- data/src/extconf.rb +47 -0
- data/src/rixmap/binary.hxx +19 -0
- data/src/rixmap/channel.hxx +42 -0
- data/src/rixmap/color.hxx +222 -0
- data/src/rixmap/common.hxx +58 -0
- data/src/rixmap/image.hxx +371 -0
- data/src/rixmap/mode.hxx +351 -0
- data/src/rixmap/palette.hxx +220 -0
- data/src/rixmap.hxx +19 -0
- data/src/rixmapcore.cxx +3209 -0
- data/src/rixmapcore.hxx +27 -0
- data/src/rixmapio.cxx +652 -0
- data/src/rixmapio.hxx +25 -0
- data/src/rixmapmain.cxx +24 -0
- data/test/test_bmp.rb +139 -0
- data/test/test_pcx.rb +172 -0
- data/test/test_png.rb +136 -0
- data/test/test_xpm.rb +73 -0
- metadata +128 -0
@@ -0,0 +1,288 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'zlib'
|
3
|
+
require_relative 'chunk'
|
4
|
+
|
5
|
+
#
|
6
|
+
module Rixmap
|
7
|
+
module Format
|
8
|
+
module PNG
|
9
|
+
|
10
|
+
# スキャンライン用フィルタ実装.
|
11
|
+
#
|
12
|
+
# @see http://www.milk-island.net/document/png/#9Filters
|
13
|
+
class AdaptiveFilter
|
14
|
+
# デフォルトフィルタ実装を初期化します.
|
15
|
+
#
|
16
|
+
# @param [Rixmap::Mode] mode 画像形式
|
17
|
+
def initialize(mode)
|
18
|
+
@prev = nil
|
19
|
+
@mode = mode
|
20
|
+
end
|
21
|
+
|
22
|
+
# フィルタ関数
|
23
|
+
#
|
24
|
+
# @param [Array] line 対象スキャンライン
|
25
|
+
# @param [Integer] method フィルタメソッド
|
26
|
+
# @return [Array] フィルタ処理後スキャンライン
|
27
|
+
def filt(line, method = ADAPTIVEFILTER_NONE)
|
28
|
+
base_line = Rixmap::Binary.new(line)
|
29
|
+
filt_line = Rixmap::Binary.new([method])
|
30
|
+
case method
|
31
|
+
when ADAPTIVEFILTER_NONE
|
32
|
+
filt_line += base_line
|
33
|
+
else
|
34
|
+
raise NotImplementedError.new("Unsupported Filter Method: #{method}")
|
35
|
+
end
|
36
|
+
|
37
|
+
@prev = base_line
|
38
|
+
return filt_line
|
39
|
+
end
|
40
|
+
|
41
|
+
# 復元関数
|
42
|
+
#
|
43
|
+
# @param [Array] line 対象スキャンライン
|
44
|
+
def recon(line)
|
45
|
+
base_line = Rixmap::Binary.new(line)
|
46
|
+
recon_line = Rixmap::Binary.new
|
47
|
+
method = base_line.shift
|
48
|
+
|
49
|
+
case method
|
50
|
+
when ADAPTIVEFILTER_NONE
|
51
|
+
recon_line += base_line
|
52
|
+
else
|
53
|
+
raise NotImplementedError.new("Unsupported Filter Method: #{method}")
|
54
|
+
end
|
55
|
+
|
56
|
+
@prev = base_line
|
57
|
+
return recon_line
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# PNG入出力実装クラス.
|
62
|
+
#
|
63
|
+
# さすがにPNGフル実装はきついですな(´・ω・`)
|
64
|
+
class PNGImageIO < Rixmap::ImageIO::BaseImageIO
|
65
|
+
# 指定マジックデータがPNG画像のものかを判定します.
|
66
|
+
#
|
67
|
+
# @param [String] magic 画像先頭データ
|
68
|
+
# @return [Boolean] PNG形式のシグネチャならtrue
|
69
|
+
def self.readable?(magic)
|
70
|
+
return (magic[0, FILE_SIGNATURE_SIZE] == FILE_SIGNATURE)
|
71
|
+
end
|
72
|
+
|
73
|
+
# 指定画像をPNGとしてエンコードできるかを返します.
|
74
|
+
#
|
75
|
+
# @param [Rixmap::Image] image 対象画像
|
76
|
+
# @return [Boolean] PNGとしてエンコードできる場合はtrue
|
77
|
+
def self.writable?(image)
|
78
|
+
return true
|
79
|
+
end
|
80
|
+
|
81
|
+
# 画像をPNG形式でエンコードします.
|
82
|
+
#
|
83
|
+
# @param [Rixmap::Image] image 対象画像
|
84
|
+
# @param [Hash] options オプションパラメータ
|
85
|
+
# @return [String] エンコードされた画像データ
|
86
|
+
def encode(image, options={})
|
87
|
+
# イメージヘッダ
|
88
|
+
ihdr = Chunk::IHDRChunk.new
|
89
|
+
ihdr.width = image.width
|
90
|
+
ihdr.height = image.height
|
91
|
+
ihdr.depth = 8 #image.mode.depth
|
92
|
+
|
93
|
+
# 必要チャンク
|
94
|
+
plte = nil
|
95
|
+
|
96
|
+
# ピクセルデータ
|
97
|
+
pixels = ''.force_encoding(Encoding::BINARY)
|
98
|
+
filter = AdaptiveFilter.new(image.mode)
|
99
|
+
|
100
|
+
# 画像種別による処理
|
101
|
+
case image.mode
|
102
|
+
when Rixmap::INDEXED
|
103
|
+
ihdr.colortype = COLORTYPE_INDEXED
|
104
|
+
|
105
|
+
# パレットを設定
|
106
|
+
plte = Chunk::PLTEChunk.new
|
107
|
+
image.palette.each_with_index do |color, i|
|
108
|
+
plte[i] = [color.red, color.green, color.blue]
|
109
|
+
end
|
110
|
+
|
111
|
+
# ピクセルを作成
|
112
|
+
image.each_line do |line|
|
113
|
+
# ByteArrayはto_bytesできる
|
114
|
+
pixels.concat(filter.filt(line).to_str)
|
115
|
+
end
|
116
|
+
when Rixmap::GRAYSCALE
|
117
|
+
ihdr.colortype = COLORTYPE_GRAYSCALE
|
118
|
+
image.each_line do |line|
|
119
|
+
pixels.concat(filter.filt(line).to_str)
|
120
|
+
end
|
121
|
+
when Rixmap::GRAYALPHA
|
122
|
+
ihdr.colortype = COLORTYPE_GRAYSCALE_WITH_ALPHA
|
123
|
+
image.each_line do |line|
|
124
|
+
pixels.concat(filter.filt(line.to_s('LA')).to_str)
|
125
|
+
end
|
126
|
+
when Rixmap::RGB
|
127
|
+
ihdr.colortype = COLORTYPE_TRUECOLOR
|
128
|
+
image.each_line do |line|
|
129
|
+
pixels.concat(filter.filt(line.to_s('RGB')).to_str)
|
130
|
+
end
|
131
|
+
when Rixmap::RGBA
|
132
|
+
ihdr.colortype = COLORTYPE_TRUECOLOR_WITH_ALPHA
|
133
|
+
image.each_line do |line|
|
134
|
+
pixels.concat(filter.filt(line.to_s('RGBA')).to_str)
|
135
|
+
end
|
136
|
+
else
|
137
|
+
raise ArgumentError.new("Unknown image mode: #{image.mode}")
|
138
|
+
end
|
139
|
+
|
140
|
+
# ピクセルを圧縮
|
141
|
+
deflated_pixels = Zlib.deflate(pixels, Zlib::BEST_COMPRESSION)
|
142
|
+
|
143
|
+
# IDATチャンクを生成
|
144
|
+
idat = Chunk::IDATChunk.new
|
145
|
+
idat.data = deflated_pixels
|
146
|
+
|
147
|
+
# チャンクリスト
|
148
|
+
chunks = []
|
149
|
+
chunks << ihdr
|
150
|
+
chunks << plte unless plte.nil?
|
151
|
+
chunks << idat
|
152
|
+
chunks << Chunk::IENDChunk.new
|
153
|
+
|
154
|
+
# PNGストリームを構築
|
155
|
+
png_data = ''
|
156
|
+
png_data.concat(FILE_SIGNATURE)
|
157
|
+
png_data.force_encoding(Encoding::BINARY)
|
158
|
+
chunks.each do |chunk|
|
159
|
+
chunk_data = chunk.data
|
160
|
+
chunk_size = chunk_data.bytesize
|
161
|
+
chunk_fulldata = chunk.type + chunk_data
|
162
|
+
chunk_crc = Zlib.crc32(chunk_fulldata)
|
163
|
+
#chunk_crc = CRC.crc(chunk_fulldata) # 自前実装
|
164
|
+
png_data.concat([chunk_size].pack('N'))
|
165
|
+
png_data.concat(chunk_fulldata)
|
166
|
+
png_data.concat([chunk_crc].pack('N'))
|
167
|
+
end
|
168
|
+
|
169
|
+
return png_data
|
170
|
+
end
|
171
|
+
|
172
|
+
# バイトデータをPNG画像として復元します.
|
173
|
+
#
|
174
|
+
# @param [String] data 入力データ
|
175
|
+
# @param [Hash] options オプションパラメータ
|
176
|
+
# @return [Rixmap::Image] 復元された画像
|
177
|
+
def decode(data, options={})
|
178
|
+
if data.byteslice(0, FILE_SIGNATURE_SIZE) != FILE_SIGNATURE
|
179
|
+
raise ArgumentError.new("InvalidSignature detected. Input is not PNG Image Data.")
|
180
|
+
end
|
181
|
+
|
182
|
+
# チャンクを分割
|
183
|
+
offset = FILE_SIGNATURE_SIZE
|
184
|
+
length = data.bytesize
|
185
|
+
chunks = []
|
186
|
+
while offset < length
|
187
|
+
chunk_size = data.byteslice(offset, offset + 4).unpack('N')[0]; offset += 4
|
188
|
+
chunk_data = nil
|
189
|
+
chunk_type = nil
|
190
|
+
chunk_crc = nil
|
191
|
+
if chunk_size > 0
|
192
|
+
chunk_type, chunk_data, chunk_crc = data.byteslice(offset, offset + (chunk_size + 8)).unpack("a4a#{chunk_size}N")
|
193
|
+
elsif chunk_size == 0
|
194
|
+
chunk_type, chunk_crc = data.byteslice(offset, offset + 8).unpack("a4N")
|
195
|
+
else
|
196
|
+
raise RuntimeError.new("Invalid Chunk Size: #{chunk_size}")
|
197
|
+
end
|
198
|
+
offset += (chunk_size + 8)
|
199
|
+
|
200
|
+
chunk = Chunk.get(chunk_type).new
|
201
|
+
chunk.data = chunk_data
|
202
|
+
|
203
|
+
chunks << chunk
|
204
|
+
break if chunk.type == Chunk::IENDChunk::TYPE
|
205
|
+
end
|
206
|
+
|
207
|
+
# IHDRチャンクを処理
|
208
|
+
ihdr = chunks.shift
|
209
|
+
unless ihdr.type == Chunk::IHDRChunk::TYPE
|
210
|
+
raise ArgumentError.new("Illegal Chunk Order. First chunk must to be IHDR chunk.")
|
211
|
+
end
|
212
|
+
|
213
|
+
image_width = ihdr.width
|
214
|
+
image_height = ihdr.height
|
215
|
+
image_depth = ihdr.depth
|
216
|
+
colortype = ihdr.colortype
|
217
|
+
|
218
|
+
# 残りチャンクを処理
|
219
|
+
image_data = ''
|
220
|
+
palette = nil
|
221
|
+
chunks.each do |chunk|
|
222
|
+
case chunk
|
223
|
+
when Chunk::IDATChunk
|
224
|
+
image_data.concat(chunk.data)
|
225
|
+
when Chunk::PLTEChunk
|
226
|
+
palette = Rixmap::Palette.new(2 ** image_depth)
|
227
|
+
palette.size.times do |i|
|
228
|
+
palette[i] = chunk[i]
|
229
|
+
end
|
230
|
+
when Chunk::IENDChunk
|
231
|
+
break
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# ピクセルを復元
|
236
|
+
pixels = Zlib.inflate(image_data).each_byte
|
237
|
+
|
238
|
+
# ベース画像を作成
|
239
|
+
image = nil
|
240
|
+
case colortype
|
241
|
+
when COLORTYPE_INDEXED
|
242
|
+
image = Rixmap::Image.new(Rixmap::INDEXED, image_width, image_height, :palette => palette)
|
243
|
+
filter = AdaptiveFilter.new(image.mode)
|
244
|
+
pixels.each_slice(image_width + 1).each_with_index do |bytes, i|
|
245
|
+
image[i] = filter.recon(bytes)
|
246
|
+
end
|
247
|
+
when COLORTYPE_GRAYSCALE
|
248
|
+
image = Rixmap::Image.new(Rixmap::GRAYSCALE, image_width, image_height)
|
249
|
+
filter = AdaptiveFilter.new(image.mode)
|
250
|
+
pixels.each_slice(image_width + 1).each_with_index do |bytes, i|
|
251
|
+
image[i] = filter.recon(bytes)
|
252
|
+
end
|
253
|
+
when COLORTYPE_GRAYSCALE_WITH_ALPHA
|
254
|
+
image = Rixmap::Image.new(Rixmap::GRAYALPHA, image_width, image_height)
|
255
|
+
filter = AdaptiveFilter.new(image.mode)
|
256
|
+
pixels.each_slice((image_width * 2) + 1).each_with_index do |bytes, i|
|
257
|
+
image[i] = filter.recon(bytes)
|
258
|
+
end
|
259
|
+
when COLORTYPE_TRUECOLOR
|
260
|
+
image = Rixmap::Image.new(Rixmap::RGB, image_width, image_height)
|
261
|
+
filter = AdaptiveFilter.new(image.mode)
|
262
|
+
pixels.each_slice((image_width * 3) + 1).each_with_index do |bytes, i|
|
263
|
+
image[i] = filter.recon(bytes)
|
264
|
+
end
|
265
|
+
when COLORTYPE_TRUECOLOR_WITH_ALPHA
|
266
|
+
image = Rixmap::Image.new(Rixmap::RGBA, image_width, image_height)
|
267
|
+
filter = AdaptiveFilter.new(image.mode)
|
268
|
+
pixels.each_slice((image_width * 4) + 1).each_with_index do |bytes, i|
|
269
|
+
image[i] = filter.recon(bytes)
|
270
|
+
end
|
271
|
+
else
|
272
|
+
raise RuntimeError.new("Unsupported Colo-Type: #{colortype}")
|
273
|
+
end
|
274
|
+
|
275
|
+
return image
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
Rixmap::ImageIO.register(:PNG, PNGImageIO, [".png"])
|
280
|
+
end
|
281
|
+
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
|
286
|
+
#==============================================================================#
|
287
|
+
# $Id: imageio.rb,v 57b1fb2cd6a6 2014/04/20 12:21:27 chikuchikugonzalez $
|
288
|
+
# vim: set sts=2 ts=2 sw=2 expandtab:
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
#
|
4
|
+
module Rixmap
|
5
|
+
module Format
|
6
|
+
|
7
|
+
# PNGフォーマット対応実装モジュール.
|
8
|
+
module PNG
|
9
|
+
|
10
|
+
# PNGシグネチャ
|
11
|
+
FILE_SIGNATURE = "\x89PNG\r\n\x1A\n".force_encoding(Encoding::BINARY)
|
12
|
+
|
13
|
+
# PNGシグネチャサイズ
|
14
|
+
FILE_SIGNATURE_SIZE = 8
|
15
|
+
|
16
|
+
# グレースケール形式
|
17
|
+
#
|
18
|
+
# ビット幅は 1, 2, 4, 8, 16
|
19
|
+
COLORTYPE_GRAYSCALE = 0x00
|
20
|
+
|
21
|
+
# RGBトゥルーカラー形式
|
22
|
+
#
|
23
|
+
# ビット幅は 8, 16
|
24
|
+
COLORTYPE_TRUECOLOR = 0x02
|
25
|
+
|
26
|
+
# インデックスカラー形式
|
27
|
+
#
|
28
|
+
# ビット幅は 1, 2, 4, 8
|
29
|
+
COLORTYPE_INDEXED = 0x03
|
30
|
+
|
31
|
+
# グレースケール形式 with 透明度
|
32
|
+
#
|
33
|
+
# ビット幅は 8, 16
|
34
|
+
COLORTYPE_GRAYSCALE_WITH_ALPHA = 0x04
|
35
|
+
|
36
|
+
# RGBトゥルーカラー形式 with 透明度
|
37
|
+
#
|
38
|
+
# ビット幅は 8, 16
|
39
|
+
COLORTYPE_TRUECOLOR_WITH_ALPHA = 0x06
|
40
|
+
|
41
|
+
# Deflate圧縮メソッド
|
42
|
+
COMPRESSION_DEFLATE = 0x00
|
43
|
+
|
44
|
+
# 基本のフィルタタイプ
|
45
|
+
FILTER_ADAPTIVE = 0x00
|
46
|
+
|
47
|
+
# インターレースなし
|
48
|
+
INTERLACE_NONE = 0x00
|
49
|
+
|
50
|
+
# Adam7インターレース
|
51
|
+
INTERLACE_ADAM7 = 0x01
|
52
|
+
|
53
|
+
# なにもしない基本フィルタ
|
54
|
+
ADAPTIVEFILTER_NONE = 0x00
|
55
|
+
|
56
|
+
# 左側差分基本フィルタ
|
57
|
+
ADAPTIVEFILTER_SUB = 0x01
|
58
|
+
|
59
|
+
# 上側差分基本フィルタ
|
60
|
+
ADAPTIVEFILTER_UP = 0x02
|
61
|
+
|
62
|
+
# 平均基本フィルタ
|
63
|
+
ADAPTIVEFILTER_AVERAGE = 0x03
|
64
|
+
|
65
|
+
# 周辺から近い色をとって、その差分をとるようなの
|
66
|
+
ADAPTIVEFILTER_PEATH = 0x04
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
require_relative 'png/chunk'
|
73
|
+
require_relative 'png/imageio'
|
74
|
+
|
75
|
+
|
76
|
+
#==============================================================================#
|
77
|
+
# $Id: png.rb,v 57b1fb2cd6a6 2014/04/20 12:21:27 chikuchikugonzalez $
|
78
|
+
# vim: set sts=2 ts=2 sw=2 expandtab:
|