rixmap 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +2 -2
- data/lib/rixmap/format/png/chunk.rb +12 -1
- data/lib/rixmap/format/png/imageio.rb +7 -1
- data/lib/rixmap/format/xpm.rb +3 -3
- data/lib/rixmap/image.rb +231 -0
- data/lib/rixmap/imageio.rb +125 -0
- data/lib/rixmap/version.rb +6 -6
- data/lib/rixmap.rb +3 -1
- data/src/rixmap/common.hxx +9 -2
- data/src/rixmap/converter.hxx +348 -0
- data/src/rixmap/deformer.hxx +325 -0
- data/src/rixmap/helper.hxx +347 -0
- data/src/rixmap/image.hxx +284 -39
- data/src/rixmap/interpolator.hxx +226 -0
- data/src/rixmap/palette.hxx +14 -2
- data/src/rixmap/quantizer.hxx +385 -0
- data/src/rixmap.hxx +4 -1
- data/src/rixmapcore.cxx +668 -272
- data/src/rixmapcore.hxx +8 -1
- data/src/rixmapdeformation.cxx +687 -0
- data/src/rixmapdeformation.hxx +30 -0
- data/src/rixmapio.cxx +93 -56
- data/src/rixmapmain.cxx +3 -1
- data/test/test_clone.rb +15 -0
- data/test/test_deformer.rb +46 -0
- data/test/test_quantize.rb +4 -0
- data/test/test_resize_cutter.rb +27 -0
- metadata +16 -2
@@ -0,0 +1,348 @@
|
|
1
|
+
// -*- coding: utf-8 -*-
|
2
|
+
/**
|
3
|
+
* 画像形式変換処理実装
|
4
|
+
*/
|
5
|
+
#pragma once
|
6
|
+
#include "common.hxx"
|
7
|
+
#include "mode.hxx"
|
8
|
+
#include "image.hxx"
|
9
|
+
#include "quantizer.hxx"
|
10
|
+
|
11
|
+
namespace Rixmap {
|
12
|
+
/**
|
13
|
+
* 画像形式変換ベースクラス.
|
14
|
+
*/
|
15
|
+
class Converter {
|
16
|
+
public:
|
17
|
+
virtual ~Converter() {}
|
18
|
+
|
19
|
+
public: // インターフェイスメソッド
|
20
|
+
virtual void convert(const ImageData& input, ImageData& output) = 0;
|
21
|
+
|
22
|
+
protected: // ユーティリティメソッドとか
|
23
|
+
/**
|
24
|
+
* 画像の持っている画像形式が指定された形式のどれかにマッチするかをチェックします.
|
25
|
+
*/
|
26
|
+
inline bool validateModes(const ImageData& image, int num, const Mode* modes) {
|
27
|
+
for (int i = 0; i < num; i++) {
|
28
|
+
if (image.getMode() == modes[i]) {
|
29
|
+
return true;
|
30
|
+
}
|
31
|
+
}
|
32
|
+
return false;
|
33
|
+
}
|
34
|
+
|
35
|
+
/**
|
36
|
+
* 画像形式チェック (入力画像用).
|
37
|
+
* 失敗時はRuby例外を出します.
|
38
|
+
*/
|
39
|
+
inline bool validateInput(const ImageData& image, int num, const Mode* modes) {
|
40
|
+
if (!this->validateModes(image, num, modes)) {
|
41
|
+
rb_raise(rb_eArgError, "unexpected input image mode: %s", ModeInfo::GetModeName(image.getMode()).c_str());
|
42
|
+
return false;
|
43
|
+
}
|
44
|
+
return true;
|
45
|
+
}
|
46
|
+
|
47
|
+
/**
|
48
|
+
* 画像形式チェック (出力画像用).
|
49
|
+
* 入力用との違いは、例外メッセージのテキストだけです.
|
50
|
+
*/
|
51
|
+
inline bool validateOutput(const ImageData& image, int num, const Mode* modes) {
|
52
|
+
if (!this->validateModes(image, num, modes)) {
|
53
|
+
rb_raise(rb_eArgError, "unexpected output image mode: %s", ModeInfo::GetModeName(image.getMode()).c_str());
|
54
|
+
return false;
|
55
|
+
}
|
56
|
+
return true;
|
57
|
+
}
|
58
|
+
|
59
|
+
/**
|
60
|
+
* 入力画像と出力画像のサイズが同じかを調べます.
|
61
|
+
*/
|
62
|
+
inline bool validateSize(const ImageData& input, const ImageData& output) {
|
63
|
+
if (!((input.getWidth() == output.getWidth()) && (input.getHeight() == output.getHeight()))) {
|
64
|
+
rb_raise(rb_eArgError, "input bounds does not matche to output bounds: input is [%d, %d], but output is [%d, %d]", input.getWidth(), input.getHeight(), output.getWidth(), output.getHeight());
|
65
|
+
return false;
|
66
|
+
}
|
67
|
+
return true;
|
68
|
+
}
|
69
|
+
};
|
70
|
+
|
71
|
+
/**
|
72
|
+
* RGB形式からグレースケールへの変換処理実装コンバータ
|
73
|
+
*/
|
74
|
+
class RGB2GrayScaleConverter : public Converter {
|
75
|
+
public: // 抽象メソッド実装
|
76
|
+
void convert(const ImageData& input, ImageData& output) override {
|
77
|
+
static Mode srcModes[2] = {Mode::RGB, Mode::RGBA};
|
78
|
+
static Mode dstModes[2] = {Mode::GRAYSCALE, Mode::GRAYALPHA};
|
79
|
+
|
80
|
+
// バリデーション
|
81
|
+
this->validateSize(input, output);
|
82
|
+
this->validateInput(input, 2, srcModes);
|
83
|
+
this->validateOutput(output, 2, dstModes);
|
84
|
+
|
85
|
+
// 変換処理本体
|
86
|
+
int32_t width = input.getWidth();
|
87
|
+
int32_t height = input.getHeight();
|
88
|
+
bool useAlpha = (input.getModeInfo().hasAlpha() && output.getModeInfo().hasAlpha());
|
89
|
+
|
90
|
+
// ピクセル単位で処理
|
91
|
+
for (int32_t h = 0; h < height; h++) {
|
92
|
+
for (int32_t w = 0; w < width; w++) {
|
93
|
+
uint8_t r = input.get(Channel::RED, w, h);
|
94
|
+
uint8_t g = input.get(Channel::GREEN, w, h);
|
95
|
+
uint8_t b = input.get(Channel::BLUE, w, h);
|
96
|
+
Color c(r, g, b);
|
97
|
+
output.set(Channel::LUMINANCE, w, h, c.getLuminance());
|
98
|
+
|
99
|
+
if (useAlpha) {
|
100
|
+
output.set(Channel::ALPHA, w, h, input.get(Channel::ALPHA, w, h));
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
};
|
106
|
+
|
107
|
+
/**
|
108
|
+
* RGBカラー形式からインデックスカラー形式への変換処理実装.
|
109
|
+
*/
|
110
|
+
class RGB2IndexConverter : public Converter {
|
111
|
+
public:
|
112
|
+
void convert(const ImageData& input, ImageData& output) override {
|
113
|
+
static Mode srcModes[2] = {Mode::RGB, Mode::RGBA};
|
114
|
+
static Mode dstModes[2] = {Mode::INDEXED};
|
115
|
+
|
116
|
+
// バリデーション
|
117
|
+
this->validateSize(input, output);
|
118
|
+
this->validateInput(input, 2, srcModes);
|
119
|
+
this->validateOutput(output, 1, dstModes);
|
120
|
+
|
121
|
+
// FIXME メディアンカット以外を選択可能にしたい
|
122
|
+
// FIXME パレット色数を選択可能にしたい
|
123
|
+
MedianCutQuantizer quantizer(256);
|
124
|
+
quantizer.quantize(input, output);
|
125
|
+
}
|
126
|
+
};
|
127
|
+
|
128
|
+
/**
|
129
|
+
* グレースケールからRGB形式への変換処理実装
|
130
|
+
*/
|
131
|
+
class GrayScale2RGBConverter : public Converter {
|
132
|
+
public:
|
133
|
+
void convert(const ImageData& input, ImageData& output) override {
|
134
|
+
static Mode srcModes[2] = {Mode::GRAYSCALE, Mode::GRAYALPHA};
|
135
|
+
static Mode dstModes[2] = {Mode::RGB, Mode::RGBA};
|
136
|
+
|
137
|
+
// バリデーション
|
138
|
+
this->validateSize(input, output);
|
139
|
+
this->validateInput(input, 2, srcModes);
|
140
|
+
this->validateOutput(output, 2, dstModes);
|
141
|
+
|
142
|
+
// 変換処理本体
|
143
|
+
int32_t width = input.getWidth();
|
144
|
+
int32_t height = input.getHeight();
|
145
|
+
bool useAlpha = (input.getModeInfo().hasAlpha() && output.getModeInfo().hasAlpha());
|
146
|
+
|
147
|
+
// ピクセル単位で処理
|
148
|
+
for (int32_t h = 0; h < height; h++) {
|
149
|
+
for (int32_t w = 0; w < width; w++) {
|
150
|
+
uint8_t l = input.get(Channel::LUMINANCE, w, h);
|
151
|
+
output.set(Channel::RED, w, h, l);
|
152
|
+
output.set(Channel::GREEN, w, h, l);
|
153
|
+
output.set(Channel::BLUE, w, h, l);
|
154
|
+
|
155
|
+
if (useAlpha) {
|
156
|
+
output.set(Channel::ALPHA, w, h, input.get(Channel::ALPHA, w, h));
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
}
|
161
|
+
};
|
162
|
+
|
163
|
+
/**
|
164
|
+
* グレースケール形式からインデックスカラー形式への変換処理実装
|
165
|
+
* 透明度は引き継げないことに注意.
|
166
|
+
*/
|
167
|
+
class GrayScale2IndexConverter : public Converter {
|
168
|
+
public:
|
169
|
+
void convert(const ImageData& input, ImageData& output) override {
|
170
|
+
static Mode srcModes[2] = {Mode::GRAYSCALE, Mode::GRAYALPHA};
|
171
|
+
static Mode dstModes[1] = {Mode::INDEXED};
|
172
|
+
|
173
|
+
// バリデーション
|
174
|
+
this->validateSize(input, output);
|
175
|
+
this->validateInput(input, 2, srcModes);
|
176
|
+
this->validateOutput(output, 1, dstModes);
|
177
|
+
|
178
|
+
// グレースケールパレットを作成
|
179
|
+
// FIXME 256階調決め打ちやめたい
|
180
|
+
PaletteData* palette = output.getPaletteData();
|
181
|
+
if (palette == NULL) {
|
182
|
+
rb_raise(rb_eRuntimeError, "illegal state: indexed image without palette detected.");
|
183
|
+
}
|
184
|
+
palette->resize(256);
|
185
|
+
for (size_t i = 0; i < 256; i++) {
|
186
|
+
uint8_t l = static_cast<uint8_t>(i);
|
187
|
+
Color c(l, l, l);
|
188
|
+
palette->set(i, c);
|
189
|
+
}
|
190
|
+
|
191
|
+
// ピクセルを変更
|
192
|
+
int32_t width = input.getWidth();
|
193
|
+
int32_t height = input.getHeight();
|
194
|
+
for (int32_t h = 0; h < height; h++) {
|
195
|
+
for (int32_t w = 0; w < width; w++) {
|
196
|
+
uint8_t l = input.get(Channel::LUMINANCE, w, h);
|
197
|
+
output.set(Channel::PALETTE, w, h, l);
|
198
|
+
}
|
199
|
+
}
|
200
|
+
}
|
201
|
+
};
|
202
|
+
|
203
|
+
/**
|
204
|
+
* インデックスカラー形式からRGBカラー形式への変換処理実装.
|
205
|
+
*/
|
206
|
+
class Index2RGBConverter : public Converter {
|
207
|
+
public:
|
208
|
+
void convert(const ImageData& input, ImageData& output) override {
|
209
|
+
static Mode srcModes[1] = {Mode::INDEXED};
|
210
|
+
static Mode dstModes[2] = {Mode::RGB, Mode::RGBA};
|
211
|
+
|
212
|
+
// バリデーション
|
213
|
+
this->validateSize(input, output);
|
214
|
+
this->validateInput(input, 1, srcModes);
|
215
|
+
this->validateOutput(output, 2, dstModes);
|
216
|
+
|
217
|
+
// パレット
|
218
|
+
const PaletteData* palette = input.getPaletteData();
|
219
|
+
if (palette == NULL) {
|
220
|
+
rb_raise(rb_eRuntimeError, "illegal state: indexed image without palette detected.");
|
221
|
+
}
|
222
|
+
|
223
|
+
// 変換処理
|
224
|
+
int32_t width = input.getWidth();
|
225
|
+
int32_t height = input.getHeight();
|
226
|
+
bool useAlpha = output.getModeInfo().hasAlpha();
|
227
|
+
|
228
|
+
for (int32_t h = 0; h < height; h++) {
|
229
|
+
for (int32_t w = 0; w < width; w++) {
|
230
|
+
size_t off = input.get(Channel::PALETTE, w, h);
|
231
|
+
const Color& col = palette->get(off);
|
232
|
+
|
233
|
+
output.set(Channel::RED, w, h, col.getRed());
|
234
|
+
output.set(Channel::GREEN, w, h, col.getGreen());
|
235
|
+
output.set(Channel::BLUE, w, h, col.getBlue());
|
236
|
+
if (useAlpha) {
|
237
|
+
output.set(Channel::ALPHA, w, h, col.getAlpha());
|
238
|
+
}
|
239
|
+
}
|
240
|
+
}
|
241
|
+
}
|
242
|
+
};
|
243
|
+
|
244
|
+
/**
|
245
|
+
* インデックスカラー形式からグレースケール形式への変換処理実装.
|
246
|
+
*/
|
247
|
+
class Index2GrayScaleConverter : public Converter {
|
248
|
+
public:
|
249
|
+
void convert(const ImageData& input, ImageData& output) override {
|
250
|
+
static Mode srcModes[1] = {Mode::INDEXED};
|
251
|
+
static Mode dstModes[2] = {Mode::GRAYSCALE, Mode::GRAYALPHA};
|
252
|
+
|
253
|
+
// バリデーション
|
254
|
+
this->validateSize(input, output);
|
255
|
+
this->validateInput(input, 1, srcModes);
|
256
|
+
this->validateOutput(output, 2, dstModes);
|
257
|
+
|
258
|
+
// パレット
|
259
|
+
const PaletteData* palette = input.getPaletteData();
|
260
|
+
if (palette == NULL) {
|
261
|
+
rb_raise(rb_eRuntimeError, "illegal state: indexed image without palette detected.");
|
262
|
+
}
|
263
|
+
|
264
|
+
// 変換処理
|
265
|
+
int32_t width = input.getWidth();
|
266
|
+
int32_t height = input.getHeight();
|
267
|
+
bool useAlpha = output.getModeInfo().hasAlpha();
|
268
|
+
|
269
|
+
for (int32_t h = 0; h < height; h++) {
|
270
|
+
for (int32_t w = 0; w < width; w++) {
|
271
|
+
size_t off = input.get(Channel::PALETTE, w, h);
|
272
|
+
const Color& col = palette->get(off);
|
273
|
+
|
274
|
+
output.set(Channel::LUMINANCE, w, h, col.getLuminance());
|
275
|
+
if (useAlpha) {
|
276
|
+
output.set(Channel::ALPHA, w, h, col.getAlpha());
|
277
|
+
}
|
278
|
+
}
|
279
|
+
}
|
280
|
+
}
|
281
|
+
};
|
282
|
+
|
283
|
+
class CopyConverter : public Converter {
|
284
|
+
public:
|
285
|
+
virtual ~CopyConverter() {}
|
286
|
+
|
287
|
+
public:
|
288
|
+
virtual void convert(const ImageData& input, ImageData& output) override {
|
289
|
+
this->validateSize(input, output);
|
290
|
+
|
291
|
+
// 変換処理
|
292
|
+
int32_t width = input.getWidth();
|
293
|
+
int32_t height = input.getHeight();
|
294
|
+
const ModeInfo& outMode = output.getModeInfo();
|
295
|
+
//bool useAlpha = outMode.hasAlpha();
|
296
|
+
const ChannelArray& channels = input.getModeInfo().getChannels();
|
297
|
+
for (auto it = channels.begin(); it != channels.end(); it++) {
|
298
|
+
Channel channel = *it;
|
299
|
+
if (!outMode.has(channel)) {
|
300
|
+
continue;
|
301
|
+
}
|
302
|
+
for (int32_t h = 0; h < height; h++) {
|
303
|
+
for (int32_t w = 0; w < width; w++) {
|
304
|
+
output.set(channel, w, h, input.get(channel, w, h));
|
305
|
+
}
|
306
|
+
}
|
307
|
+
}
|
308
|
+
}
|
309
|
+
};
|
310
|
+
|
311
|
+
class RGB2RGBConverter : public CopyConverter {
|
312
|
+
public:
|
313
|
+
virtual void convert(const ImageData& input, ImageData& output) override {
|
314
|
+
static Mode srcModes[2] = {Mode::RGB, Mode::RGBA};
|
315
|
+
static Mode dstModes[2] = {Mode::RGB, Mode::RGBA};
|
316
|
+
this->validateInput(input, 2, srcModes);
|
317
|
+
this->validateOutput(output, 2, dstModes);
|
318
|
+
CopyConverter::convert(input, output);
|
319
|
+
}
|
320
|
+
};
|
321
|
+
|
322
|
+
class GrayScale2GrayScaleConverter : public CopyConverter {
|
323
|
+
public:
|
324
|
+
virtual void convert(const ImageData& input, ImageData& output) override {
|
325
|
+
static Mode srcModes[2] = {Mode::GRAYSCALE, Mode::GRAYALPHA};
|
326
|
+
static Mode dstModes[2] = {Mode::GRAYSCALE, Mode::GRAYALPHA};
|
327
|
+
this->validateInput(input, 2, srcModes);
|
328
|
+
this->validateOutput(output, 2, dstModes);
|
329
|
+
CopyConverter::convert(input, output);
|
330
|
+
}
|
331
|
+
};
|
332
|
+
|
333
|
+
class Index2IndexConverter : public CopyConverter {
|
334
|
+
public:
|
335
|
+
virtual void convert(const ImageData& input, ImageData& output) override {
|
336
|
+
static Mode srcModes[1] = {Mode::INDEXED};
|
337
|
+
static Mode dstModes[1] = {Mode::INDEXED};
|
338
|
+
this->validateInput(input, 1, srcModes);
|
339
|
+
this->validateOutput(output, 1, dstModes);
|
340
|
+
CopyConverter::convert(input, output);
|
341
|
+
}
|
342
|
+
};
|
343
|
+
}
|
344
|
+
|
345
|
+
|
346
|
+
//============================================================================//
|
347
|
+
// $Id: converter.hxx,v 753dbf70cab3 2014/05/16 16:13:38 chikuchikugonzalez $
|
348
|
+
// vim: set sts=4 ts=4 sw=4 expandtab foldmethod=marker:
|
@@ -0,0 +1,325 @@
|
|
1
|
+
// -*- coding: utf-8 -*-
|
2
|
+
/**
|
3
|
+
* 画像変形処理
|
4
|
+
*/
|
5
|
+
#pragma once
|
6
|
+
#include "common.hxx"
|
7
|
+
#include "image.hxx"
|
8
|
+
#include "interpolator.hxx"
|
9
|
+
|
10
|
+
namespace Rixmap {
|
11
|
+
|
12
|
+
/**
|
13
|
+
* 座標っぽい構造体
|
14
|
+
*/
|
15
|
+
struct Point {
|
16
|
+
public:
|
17
|
+
double x;
|
18
|
+
double y;
|
19
|
+
};
|
20
|
+
|
21
|
+
/**
|
22
|
+
* 剪断変形フラグ.
|
23
|
+
*/
|
24
|
+
enum class SkewDeformation : int {
|
25
|
+
NONE = 0x00,
|
26
|
+
HORIZONTAL = 0x01,
|
27
|
+
VERTICAL = 0x02,
|
28
|
+
BOTH = 0x03
|
29
|
+
};
|
30
|
+
|
31
|
+
/**
|
32
|
+
* アフィン変換パラメータ.
|
33
|
+
*/
|
34
|
+
class AffineMatrix {
|
35
|
+
protected: // 非公開メンバ
|
36
|
+
Point _center; // 変換中心点
|
37
|
+
Point _scale; // 拡大率
|
38
|
+
Point _translation; // 平行移動量
|
39
|
+
|
40
|
+
double _angle; // 回転角度 (ラジアン)
|
41
|
+
|
42
|
+
SkewDeformation _skew; // 剪断変形方向
|
43
|
+
|
44
|
+
double _regularMatrix[3][3]; // 3x3 正変換用行列
|
45
|
+
double _inverseMatrix[3][3]; // 3x3 逆変換用行列
|
46
|
+
|
47
|
+
public: // コンストラクタ
|
48
|
+
AffineMatrix() {
|
49
|
+
this->reset();
|
50
|
+
}
|
51
|
+
|
52
|
+
AffineMatrix(const AffineMatrix& matrix) {
|
53
|
+
*this = matrix;
|
54
|
+
this->reset();
|
55
|
+
}
|
56
|
+
|
57
|
+
virtual ~AffineMatrix() {}
|
58
|
+
|
59
|
+
private: // 非公開クラスメンバ関数
|
60
|
+
/**
|
61
|
+
* 3x3行列同士の乗算を行います.
|
62
|
+
*/
|
63
|
+
static inline void _3x3matrixMultiply(const double left[][3], const double right[][3], double dest[][3]) {
|
64
|
+
for (int row = 0; row < 3; row++) {
|
65
|
+
for (int column = 0; column < 3; column++) {
|
66
|
+
dest[row][column] = 0.0; // リセット
|
67
|
+
for (int i = 0; i < 3; i++) {
|
68
|
+
dest[row][column] += (left[row][i] * right[i][column]);
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
public: // 公開プロパティメンバ関数
|
75
|
+
inline const Point& getCenter() const { return this->_center; }
|
76
|
+
inline Point& getCenter() { return this->_center; }
|
77
|
+
inline void setCenter(const Point& point) { this->setCenter(point.x, point.y); }
|
78
|
+
inline void setCenter(double x, double y) {
|
79
|
+
this->_center.x = x;
|
80
|
+
this->_center.y = y;
|
81
|
+
}
|
82
|
+
|
83
|
+
inline const Point& getScale() const { return this->_scale; }
|
84
|
+
inline Point& getScale() { return this->_scale; }
|
85
|
+
inline void setScale(const Point& point) { this->setScale(point.x, point.y); }
|
86
|
+
inline void setScale(double x, double y) {
|
87
|
+
this->_scale.x = x;
|
88
|
+
this->_scale.y = y;
|
89
|
+
}
|
90
|
+
|
91
|
+
inline const Point& getTranslation() const { return this->_translation; }
|
92
|
+
inline Point& getTranslation() { return this->_translation; }
|
93
|
+
inline void setTranslation(const Point& point) { this->setTranslation(point.x, point.y); }
|
94
|
+
inline void setTranslation(double x, double y) {
|
95
|
+
this->_translation.x = x;
|
96
|
+
this->_translation.y = y;
|
97
|
+
}
|
98
|
+
|
99
|
+
inline double getAngle() const { return this->_angle; }
|
100
|
+
inline void setAngle(double angle) { this->_angle = angle; }
|
101
|
+
|
102
|
+
inline SkewDeformation getSkew() const { return this->_skew; }
|
103
|
+
inline void setSkew(SkewDeformation skew) { this->_skew = skew; }
|
104
|
+
|
105
|
+
public: // 公開メンバ関数
|
106
|
+
/**
|
107
|
+
* 内部パラメータから変換用行列を初期化します.
|
108
|
+
*/
|
109
|
+
virtual void matrixize() {
|
110
|
+
double scaleMatrix[3][3] = {
|
111
|
+
{0.0, 0.0, 0.0},
|
112
|
+
{0.0, 0.0, 0.0},
|
113
|
+
{0.0, 0.0, 1.0}
|
114
|
+
};
|
115
|
+
double transMatrix[3][3] = {
|
116
|
+
{0.0, 0.0, 0.0},
|
117
|
+
{0.0, 0.0, 0.0},
|
118
|
+
{0.0, 0.0, 1.0}
|
119
|
+
};
|
120
|
+
double angleMatrix[3][3] = {
|
121
|
+
{0.0, 0.0, 0.0},
|
122
|
+
{0.0, 0.0, 0.0},
|
123
|
+
{0.0, 0.0, 1.0}
|
124
|
+
};
|
125
|
+
this->matrixizeScale(scaleMatrix);
|
126
|
+
this->matrixizeTranslation(transMatrix);
|
127
|
+
this->matrixizeAngle(angleMatrix);
|
128
|
+
|
129
|
+
// 正変換行列
|
130
|
+
{
|
131
|
+
double matrix0[3][3]; // 一時計算
|
132
|
+
AffineMatrix::_3x3matrixMultiply(scaleMatrix, transMatrix, matrix0);
|
133
|
+
AffineMatrix::_3x3matrixMultiply(matrix0, angleMatrix, this->_regularMatrix);
|
134
|
+
}
|
135
|
+
|
136
|
+
// 逆変換用行列
|
137
|
+
{
|
138
|
+
double delta0 = this->_regularMatrix[0][0] * (this->_regularMatrix[1][1] * this->_regularMatrix[2][2] - this->_regularMatrix[2][1] * this->_regularMatrix[1][2]);
|
139
|
+
double delta1 = this->_regularMatrix[1][0] * (this->_regularMatrix[0][1] * this->_regularMatrix[2][2] - this->_regularMatrix[2][1] * this->_regularMatrix[0][2]);
|
140
|
+
double delta2 = this->_regularMatrix[2][0] * (this->_regularMatrix[0][1] * this->_regularMatrix[1][2] - this->_regularMatrix[1][1] * this->_regularMatrix[0][2]);
|
141
|
+
double delta = delta0 - delta1 + delta2;
|
142
|
+
|
143
|
+
this->_inverseMatrix[0][0] = +(this->_regularMatrix[1][1] * this->_regularMatrix[2][2] - this->_regularMatrix[2][1] * this->_regularMatrix[1][2]) / delta;
|
144
|
+
this->_inverseMatrix[0][1] = -(this->_regularMatrix[0][1] * this->_regularMatrix[2][2] - this->_regularMatrix[2][1] * this->_regularMatrix[0][2]) / delta;
|
145
|
+
this->_inverseMatrix[0][2] = +(this->_regularMatrix[0][1] * this->_regularMatrix[1][2] - this->_regularMatrix[1][1] * this->_regularMatrix[0][2]) / delta;
|
146
|
+
this->_inverseMatrix[1][0] = -(this->_regularMatrix[1][0] * this->_regularMatrix[2][2] - this->_regularMatrix[2][0] * this->_regularMatrix[1][2]) / delta;
|
147
|
+
this->_inverseMatrix[1][1] = +(this->_regularMatrix[0][0] * this->_regularMatrix[2][2] - this->_regularMatrix[2][0] * this->_regularMatrix[0][2]) / delta;
|
148
|
+
this->_inverseMatrix[1][2] = -(this->_regularMatrix[0][0] * this->_regularMatrix[1][2] - this->_regularMatrix[1][0] * this->_regularMatrix[0][2]) / delta;
|
149
|
+
this->_inverseMatrix[2][0] = +(this->_regularMatrix[1][0] * this->_regularMatrix[2][1] - this->_regularMatrix[2][0] * this->_regularMatrix[1][1]) / delta;
|
150
|
+
this->_inverseMatrix[2][1] = -(this->_regularMatrix[0][0] * this->_regularMatrix[2][1] - this->_regularMatrix[2][0] * this->_regularMatrix[0][1]) / delta;
|
151
|
+
this->_inverseMatrix[2][2] = +(this->_regularMatrix[0][0] * this->_regularMatrix[1][1] - this->_regularMatrix[1][0] * this->_regularMatrix[0][1]) / delta;
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
/**
|
156
|
+
* 内部パラメータを初期化します.
|
157
|
+
*/
|
158
|
+
virtual void reset() {
|
159
|
+
this->_center.x = 0.0;
|
160
|
+
this->_center.y = 0.0;
|
161
|
+
this->_scale.x = 1.0;
|
162
|
+
this->_scale.y = 1.0;
|
163
|
+
this->_translation.x = 0.0;
|
164
|
+
this->_translation.y = 0.0;
|
165
|
+
this->_angle = 0.0;
|
166
|
+
this->_skew = SkewDeformation::NONE;
|
167
|
+
this->matrixize();
|
168
|
+
}
|
169
|
+
|
170
|
+
/**
|
171
|
+
* パラメータに従いsrcからdstに変換を行います.
|
172
|
+
*/
|
173
|
+
virtual void deform(const Point& src, Point& dst) {
|
174
|
+
this->apply(this->_regularMatrix, src, dst);
|
175
|
+
}
|
176
|
+
|
177
|
+
/**
|
178
|
+
* パラメータに従い逆変換を実行します.
|
179
|
+
*/
|
180
|
+
virtual void inverse(const Point& src, Point& dst) {
|
181
|
+
this->apply(this->_inverseMatrix, src, dst);
|
182
|
+
}
|
183
|
+
|
184
|
+
public: // 演算子オーバーロード
|
185
|
+
AffineMatrix& operator=(const AffineMatrix& base) {
|
186
|
+
this->_center.x = base._center.x;
|
187
|
+
this->_center.y = base._center.y;
|
188
|
+
this->_scale.x = base._scale.x;
|
189
|
+
this->_scale.y = base._scale.y;
|
190
|
+
this->_translation.x = base._translation.x;
|
191
|
+
this->_translation.y = base._translation.y;
|
192
|
+
this->_angle = base._angle;
|
193
|
+
this->_skew = base._skew;
|
194
|
+
return *this;
|
195
|
+
}
|
196
|
+
|
197
|
+
protected:
|
198
|
+
/**
|
199
|
+
* 変換処理の実体
|
200
|
+
*/
|
201
|
+
virtual void apply(const double matrix[][3], const Point& src, Point& dst) {
|
202
|
+
// 中心補正をした基準値を計算
|
203
|
+
double sx = src.x - this->_center.x;
|
204
|
+
double sy = src.y - this->_center.y;
|
205
|
+
|
206
|
+
// 似非行列計算を実行
|
207
|
+
double dx = (matrix[0][0] * sx) + (matrix[0][1] * sy) + matrix[0][2];
|
208
|
+
double dy = (matrix[1][0] * sx) + (matrix[1][1] * sy) + matrix[1][2];
|
209
|
+
|
210
|
+
// 中止補正分を除外
|
211
|
+
dst.x = dx + this->_center.x;
|
212
|
+
dst.y = dy + this->_center.y;
|
213
|
+
}
|
214
|
+
|
215
|
+
/**
|
216
|
+
* 拡縮関係の行列を初期化
|
217
|
+
*/
|
218
|
+
virtual void matrixizeScale(double matrix[][3]) {
|
219
|
+
matrix[0][0] = this->_scale.x;
|
220
|
+
matrix[0][1] = 0.0;
|
221
|
+
matrix[0][2] = 0.0;
|
222
|
+
matrix[1][0] = 0.0;
|
223
|
+
matrix[1][1] = this->_scale.y;
|
224
|
+
matrix[1][2] = 0.0;
|
225
|
+
matrix[2][0] = 0.0;
|
226
|
+
matrix[2][1] = 0.0;
|
227
|
+
matrix[2][2] = 1.0;
|
228
|
+
}
|
229
|
+
|
230
|
+
/**
|
231
|
+
* 平行移動関係の行列を初期化する.
|
232
|
+
*/
|
233
|
+
virtual void matrixizeTranslation(double matrix[][3]) {
|
234
|
+
matrix[0][0] = 1.0;
|
235
|
+
matrix[0][1] = 0.0;
|
236
|
+
matrix[0][2] = this->_translation.x;
|
237
|
+
matrix[1][0] = 0.0;
|
238
|
+
matrix[1][1] = 1.0;
|
239
|
+
matrix[1][2] = this->_translation.y;
|
240
|
+
matrix[2][0] = 0.0;
|
241
|
+
matrix[2][1] = 0.0;
|
242
|
+
matrix[2][2] = 1.0;
|
243
|
+
}
|
244
|
+
|
245
|
+
/**
|
246
|
+
* 角度関係の行列を初期化します.
|
247
|
+
*/
|
248
|
+
virtual void matrixizeAngle(double matrix[][3]) {
|
249
|
+
double cosTheta = std::cos(this->_angle);
|
250
|
+
double sinTheta = std::sin(this->_angle);
|
251
|
+
double tanTheta = std::tan(this->_angle);
|
252
|
+
matrix[0][0] = 0.0;
|
253
|
+
matrix[0][1] = 0.0;
|
254
|
+
matrix[0][2] = 0.0;
|
255
|
+
matrix[1][0] = 0.0;
|
256
|
+
matrix[1][1] = 0.0;
|
257
|
+
matrix[1][2] = 0.0;
|
258
|
+
matrix[2][0] = 0.0;
|
259
|
+
matrix[2][1] = 0.0;
|
260
|
+
matrix[2][2] = 1.0;
|
261
|
+
|
262
|
+
switch (this->_skew){
|
263
|
+
case SkewDeformation::BOTH:
|
264
|
+
matrix[0][0] = 1.0;
|
265
|
+
matrix[0][1] = tanTheta;
|
266
|
+
matrix[1][0] = tanTheta;
|
267
|
+
matrix[1][1] = 1.0;
|
268
|
+
break;
|
269
|
+
|
270
|
+
case SkewDeformation::HORIZONTAL:
|
271
|
+
matrix[0][0] = 1.0;
|
272
|
+
matrix[0][1] = tanTheta;
|
273
|
+
matrix[1][1] = 1.0;
|
274
|
+
break;
|
275
|
+
|
276
|
+
case SkewDeformation::VERTICAL:
|
277
|
+
matrix[0][0] = 1.0;
|
278
|
+
matrix[1][0] = tanTheta;
|
279
|
+
matrix[1][1] = 1.0;
|
280
|
+
break;
|
281
|
+
|
282
|
+
default:
|
283
|
+
matrix[0][0] = +(cosTheta);
|
284
|
+
matrix[0][1] = -(sinTheta);
|
285
|
+
matrix[1][0] = +(sinTheta);
|
286
|
+
matrix[1][1] = +(cosTheta);
|
287
|
+
break;
|
288
|
+
}
|
289
|
+
}
|
290
|
+
};
|
291
|
+
|
292
|
+
/**
|
293
|
+
* Ruby用アフィン変換パラメータクラスデータ.
|
294
|
+
*/
|
295
|
+
class AffineMatrixData : public AffineMatrix {};
|
296
|
+
|
297
|
+
/**
|
298
|
+
* Ruby用変形処理クラスデータ.
|
299
|
+
*/
|
300
|
+
class DeformerData {
|
301
|
+
private:
|
302
|
+
Interpolation _ipo; // 補間処理方法
|
303
|
+
|
304
|
+
public: // コンストラクタ
|
305
|
+
DeformerData(Interpolation ipo = Interpolation::NONE) : _ipo(ipo) {}
|
306
|
+
virtual ~DeformerData() {}
|
307
|
+
|
308
|
+
public: // メンバ関数
|
309
|
+
inline Interpolation getInterpolation() const { return this->_ipo; }
|
310
|
+
inline void setInterpolation(Interpolation ipo) { this->_ipo = ipo; }
|
311
|
+
};
|
312
|
+
|
313
|
+
/**
|
314
|
+
* Ruby用アフィン変換処理クラスデータ.
|
315
|
+
*/
|
316
|
+
class AffineDeformerData : public DeformerData {
|
317
|
+
public:
|
318
|
+
AffineDeformerData(Interpolation ipo = Interpolation::NONE) : DeformerData(ipo) {}
|
319
|
+
};
|
320
|
+
}
|
321
|
+
|
322
|
+
|
323
|
+
//============================================================================//
|
324
|
+
// $Id: deformer.hxx,v 753dbf70cab3 2014/05/16 16:13:38 chikuchikugonzalez $
|
325
|
+
// vim: set sts=4 ts=4 sw=4 expandtab foldmethod=marker:
|