rixmap 0.1.1 → 0.2.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 +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
data/src/rixmap/image.hxx
CHANGED
@@ -9,10 +9,110 @@
|
|
9
9
|
#include "palette.hxx"
|
10
10
|
|
11
11
|
namespace Rixmap {
|
12
|
+
|
13
|
+
// ピクセルデータ用コンテナ
|
14
|
+
typedef std::map<Channel, uint8_t**> Pixels;
|
15
|
+
|
16
|
+
// 反転方向定数
|
17
|
+
enum class FlipDirection : int {
|
18
|
+
HORIZONTAL = 0x01,
|
19
|
+
VERTICAL = 0x02,
|
20
|
+
DIAGONAL = 0x03
|
21
|
+
};
|
22
|
+
|
12
23
|
/**
|
13
24
|
* Ruby用画像情報クラスデータ
|
14
25
|
*/
|
15
26
|
class ImageData {
|
27
|
+
private: // 非公開クラスメンバ関数
|
28
|
+
/**
|
29
|
+
* ピクセルデータを指定された情報に従って解放します.
|
30
|
+
*
|
31
|
+
* @param [Pixels&] pixels ピクセルデータ.
|
32
|
+
* @param [int32_t] width 画像の横幅
|
33
|
+
* @param [int32_t] height 画像の高さ
|
34
|
+
*/
|
35
|
+
static void DeallocatePixels(Pixels& pixels, int32_t width, int32_t height) {
|
36
|
+
for (auto it = pixels.begin(); it != pixels.end(); it++) {
|
37
|
+
uint8_t** plane = it->second;
|
38
|
+
if (plane != NULL) {
|
39
|
+
for (int32_t h = 0; h < height; h++) {
|
40
|
+
if (plane[h] != NULL) {
|
41
|
+
delete[] plane[h];
|
42
|
+
}
|
43
|
+
}
|
44
|
+
delete[] plane;
|
45
|
+
it->second = NULL;
|
46
|
+
}
|
47
|
+
}
|
48
|
+
pixels.clear();
|
49
|
+
}
|
50
|
+
|
51
|
+
/**
|
52
|
+
* ピクセルデータリストを指定チャンネル分だけ確保します.
|
53
|
+
*
|
54
|
+
* 注意点として、pixelsは未初期化 (確保したて) を想定しています.
|
55
|
+
* 中身が二重ポインタのせいで正確に解放できないためです.
|
56
|
+
*
|
57
|
+
* @param [Pixels&] pixels データ格納先.
|
58
|
+
* @param [ChannelArray&] channels 確保するチャンネルリスト
|
59
|
+
* @param [int32_t] width 画像の横幅
|
60
|
+
* @param [int32_t] height 画像の高さ
|
61
|
+
* @param [bool] cleared 初期化するかのフラグ (default=true)
|
62
|
+
*/
|
63
|
+
static void AllocatePixels(Pixels& pixels, const ChannelArray& channels, int32_t width, int32_t height, bool cleared = true) {
|
64
|
+
// メモリ不足時に投げる例外オブジェクト
|
65
|
+
static std::bad_alloc excNoMemory;
|
66
|
+
|
67
|
+
// 確保処理
|
68
|
+
try {
|
69
|
+
for (auto it = channels.begin(); it != channels.end(); it++) {
|
70
|
+
Channel channel = *it;
|
71
|
+
|
72
|
+
// NULLポインタで初期化
|
73
|
+
// TODO ところでこれ、nullptrのほうがいいんですかね
|
74
|
+
pixels[channel] = NULL;
|
75
|
+
|
76
|
+
// ピクセルデータを確保
|
77
|
+
uint8_t** plane = new uint8_t*[height];
|
78
|
+
if (plane == NULL) {
|
79
|
+
// 確保できてない
|
80
|
+
throw excNoMemory;
|
81
|
+
}
|
82
|
+
|
83
|
+
// 確保できたらpixelsに入れる
|
84
|
+
pixels[channel] = plane;
|
85
|
+
|
86
|
+
// 中身を確保
|
87
|
+
for (int32_t h = 0; h < height; h++) {
|
88
|
+
uint8_t* line = new uint8_t[width];
|
89
|
+
if (line == NULL) {
|
90
|
+
// 無理でした
|
91
|
+
throw excNoMemory;
|
92
|
+
}
|
93
|
+
plane[h] = line;
|
94
|
+
|
95
|
+
// 初期化する
|
96
|
+
if (cleared) {
|
97
|
+
if (channel == Channel::ALPHA) {
|
98
|
+
std::memset(&(line[0]), 255, sizeof(uint8_t) * width);
|
99
|
+
} else {
|
100
|
+
std::memset(&(line[0]), 0, sizeof(uint8_t) * width);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
} catch (const std::bad_alloc& e) {
|
106
|
+
// メモリねぇぞ(#゚Д゚)
|
107
|
+
|
108
|
+
// 今まで確保した分を解放
|
109
|
+
ImageData::DeallocatePixels(pixels, width, height);
|
110
|
+
|
111
|
+
// raise NoMemoryError
|
112
|
+
rb_memerror();
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
16
116
|
private: // 非公開メンバ
|
17
117
|
ModeInfo _mode; // 画像形式
|
18
118
|
int32_t _width; // 横幅
|
@@ -20,7 +120,7 @@ namespace Rixmap {
|
|
20
120
|
bool _ready; // 準備完了しているか
|
21
121
|
|
22
122
|
// ピクセルデータ系
|
23
|
-
|
123
|
+
Pixels _data;
|
24
124
|
VALUE _palette;
|
25
125
|
|
26
126
|
public: // コンストラクタ
|
@@ -36,25 +136,42 @@ namespace Rixmap {
|
|
36
136
|
this->init(mode, width, height);
|
37
137
|
}
|
38
138
|
|
139
|
+
/**
|
140
|
+
* コピーコンストラクタ
|
141
|
+
*/
|
142
|
+
ImageData(const ImageData& image) : _mode(Mode::NONE), _width(0), _height(0), _ready(false), _palette(Qnil) {
|
143
|
+
this->init(image.getMode(), image._width, image._height);
|
144
|
+
this->_palette = image._palette;
|
145
|
+
|
146
|
+
// ピクセルの複製
|
147
|
+
const ChannelArray& channels = image._mode.getChannels();
|
148
|
+
for (auto it = channels.begin(); it != channels.end(); it++) {
|
149
|
+
Channel channel = *it;
|
150
|
+
uint8_t** dstPlane = this->_data.at(channel);
|
151
|
+
uint8_t** srcPlane = image._data.at(channel);
|
152
|
+
for (int32_t h = 0; h < image._height; h++) {
|
153
|
+
for (int32_t w = 0; w < image._width; w++) {
|
154
|
+
dstPlane[h][w] = srcPlane[h][w];
|
155
|
+
}
|
156
|
+
}
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
/**
|
161
|
+
* ベース画像ののサイズを変更した新しい画像を作成する、部分コピーコンストラクタ.
|
162
|
+
* ピクセルデータは複製されません.
|
163
|
+
*/
|
164
|
+
ImageData(const ImageData& image, int32_t width, int32_t height) : _mode(Mode::NONE), _width(0), _height(0), _ready(false), _palette(Qnil) {
|
165
|
+
this->init(image.getMode(), width, height);
|
166
|
+
this->_palette = image._palette;
|
167
|
+
}
|
168
|
+
|
39
169
|
/**
|
40
170
|
* デストラクタ
|
41
171
|
*/
|
42
172
|
~ImageData() {
|
43
|
-
// TODO ピクセルデータの解放処理
|
44
173
|
if (this->_ready) {
|
45
|
-
|
46
|
-
uint8_t** plane = it->second;
|
47
|
-
if (plane != NULL) {
|
48
|
-
for (int32_t h = 0; h < this->_height; h++) {
|
49
|
-
if (plane[h] != NULL) {
|
50
|
-
ruby_xfree(plane[h]);
|
51
|
-
}
|
52
|
-
}
|
53
|
-
ruby_xfree(plane);
|
54
|
-
it->second = NULL;
|
55
|
-
}
|
56
|
-
}
|
57
|
-
this->_data.clear();
|
174
|
+
ImageData::DeallocatePixels(this->_data, this->_width, this->_height);
|
58
175
|
}
|
59
176
|
}
|
60
177
|
|
@@ -67,8 +184,8 @@ namespace Rixmap {
|
|
67
184
|
/**
|
68
185
|
* 画像形式情報を取得します.
|
69
186
|
*/
|
70
|
-
//inline ModeInfo& getModeInfo() { return this->_mode; }
|
71
187
|
inline const ModeInfo& getModeInfo() const { return this->_mode; }
|
188
|
+
inline ModeInfo& getModeInfo() { return this->_mode; }
|
72
189
|
|
73
190
|
/**
|
74
191
|
* 横幅を取得します.
|
@@ -94,7 +211,7 @@ namespace Rixmap {
|
|
94
211
|
* パレットオブジェクトの内部データポインタを返します.
|
95
212
|
* パレットがnilの場合はNULLが返されます.
|
96
213
|
*/
|
97
|
-
inline
|
214
|
+
inline PaletteData* getPaletteData() const {
|
98
215
|
if (NIL_P(this->_palette)) {
|
99
216
|
return NULL;
|
100
217
|
} else {
|
@@ -130,10 +247,10 @@ namespace Rixmap {
|
|
130
247
|
* 指定チャンネルデータを格納するデータポインタから取得します.
|
131
248
|
* 対象チャンネルを持っていない場合はNULLになります.
|
132
249
|
*/
|
133
|
-
inline uint8_t** get(Channel channel) {
|
250
|
+
inline uint8_t** get(Channel channel) const {
|
134
251
|
auto found = this->_data.find(channel);
|
135
252
|
if (found != this->_data.end()) {
|
136
|
-
return this->_data
|
253
|
+
return this->_data.at(channel);
|
137
254
|
} else {
|
138
255
|
return NULL;
|
139
256
|
}
|
@@ -143,7 +260,7 @@ namespace Rixmap {
|
|
143
260
|
* 指定チャンネルの指定行番号に対応するバイト列へのポインタから取得します.
|
144
261
|
* 行番号が範囲外の場合か、非対応のチャンネルの場合はNULLになります.
|
145
262
|
*/
|
146
|
-
inline uint8_t* get(Channel channel, int32_t lineno) {
|
263
|
+
inline uint8_t* get(Channel channel, int32_t lineno) const {
|
147
264
|
if (0 <= lineno && lineno < this->_height) {
|
148
265
|
uint8_t** plane = this->get(channel);
|
149
266
|
if (plane != NULL) {
|
@@ -159,7 +276,7 @@ namespace Rixmap {
|
|
159
276
|
* Notice: 範囲外だったりチャンネル持ってないときは0になるので、
|
160
277
|
* 実はバイトデータが0の時と判別できないんですよコレ
|
161
278
|
*/
|
162
|
-
inline uint8_t get(Channel channel, int32_t x, int32_t y) {
|
279
|
+
inline uint8_t get(Channel channel, int32_t x, int32_t y) const {
|
163
280
|
if (0 <= x && x < this->_width && 0 <= y && y < this->_height) {
|
164
281
|
uint8_t** plane = this->get(channel);
|
165
282
|
if (plane != NULL) {
|
@@ -183,6 +300,51 @@ namespace Rixmap {
|
|
183
300
|
return false;
|
184
301
|
}
|
185
302
|
|
303
|
+
/**
|
304
|
+
* 指定した座標の色データを取得します.
|
305
|
+
*/
|
306
|
+
inline Color fetch(int32_t x, int32_t y, bool strict = false) const {
|
307
|
+
if (0 <= x && x < this->_width && 0 <= y && y < this->_height) {
|
308
|
+
switch (this->_mode.getMode()) {
|
309
|
+
case Mode::INDEXED:
|
310
|
+
{
|
311
|
+
uint8_t off = this->get(Channel::PALETTE, x, y);
|
312
|
+
return this->getPaletteData()->get(off);
|
313
|
+
}
|
314
|
+
break;
|
315
|
+
|
316
|
+
case Mode::GRAYSCALE:
|
317
|
+
case Mode::GRAYALPHA:
|
318
|
+
{
|
319
|
+
uint8_t l = this->get(Channel::LUMINANCE, x, y);
|
320
|
+
return Color(l, l, l);
|
321
|
+
}
|
322
|
+
break;
|
323
|
+
|
324
|
+
case Mode::RGB:
|
325
|
+
case Mode::RGBA:
|
326
|
+
{
|
327
|
+
uint8_t r = this->get(Channel::RED, x, y);
|
328
|
+
uint8_t g = this->get(Channel::GREEN, x, y);
|
329
|
+
uint8_t b = this->get(Channel::BLUE, x, y);
|
330
|
+
return Color(r, g, b);
|
331
|
+
}
|
332
|
+
break;
|
333
|
+
|
334
|
+
default:
|
335
|
+
throw std::invalid_argument("unexpected image mode");
|
336
|
+
}
|
337
|
+
} else {
|
338
|
+
if (strict) {
|
339
|
+
std::ostringstream err;
|
340
|
+
err << "point [" << x << ", " << y << "] is out of image";
|
341
|
+
throw std::out_of_range(err.str());
|
342
|
+
} else {
|
343
|
+
return Color(0, 0, 0);
|
344
|
+
}
|
345
|
+
}
|
346
|
+
}
|
347
|
+
|
186
348
|
/**
|
187
349
|
* 画像データを初期化します.
|
188
350
|
*/
|
@@ -203,25 +365,11 @@ namespace Rixmap {
|
|
203
365
|
// 解析用にモードを設置
|
204
366
|
ModeInfo info(mode);
|
205
367
|
const ChannelArray& channels = info.getChannels();
|
206
|
-
for (auto it = channels.begin(); it != channels.end(); it++) {
|
207
|
-
Channel channel = *it;
|
208
|
-
this->_data[channel] = NULL;
|
209
|
-
|
210
|
-
// 領域を確保
|
211
|
-
uint8_t** plane = ALLOC_N(uint8_t*, height);
|
212
|
-
std::memset(plane, 0, sizeof(uint8_t*) * height);
|
213
|
-
for (int32_t h = 0; h < height; h++) {
|
214
|
-
plane[h] = ALLOC_N(uint8_t, width);
|
215
|
-
if (channel == Channel::ALPHA) {
|
216
|
-
std::memset(&(plane[h][0]), 255, sizeof(uint8_t) * width);
|
217
|
-
} else {
|
218
|
-
std::memset(&(plane[h][0]), 0, sizeof(uint8_t) * width);
|
219
|
-
}
|
220
|
-
}
|
221
368
|
|
222
|
-
|
223
|
-
|
369
|
+
// ピクセルデータを確保
|
370
|
+
ImageData::AllocatePixels(this->_data, channels, width, height, true);
|
224
371
|
|
372
|
+
// その他プロパティを初期化
|
225
373
|
this->_mode = info;
|
226
374
|
this->_width = width;
|
227
375
|
this->_height = height;
|
@@ -236,6 +384,62 @@ namespace Rixmap {
|
|
236
384
|
rb_gc_mark_maybe(this->_palette);
|
237
385
|
}
|
238
386
|
|
387
|
+
/**
|
388
|
+
* 必須ではないオブジェクトのハッシュを返します.
|
389
|
+
*/
|
390
|
+
inline VALUE getMetadata() const {
|
391
|
+
VALUE metadata = rb_hash_new();
|
392
|
+
rb_hash_aset(metadata, ID2SYM(rb_intern("palette")), this->_palette);
|
393
|
+
return metadata;
|
394
|
+
}
|
395
|
+
|
396
|
+
/**
|
397
|
+
* 画像の反転処理を行います.
|
398
|
+
*/
|
399
|
+
void flip(FlipDirection direction) {
|
400
|
+
const ChannelArray& channels = this->_mode.getChannels();
|
401
|
+
|
402
|
+
// まずは横方向に反転
|
403
|
+
switch (direction) {
|
404
|
+
case FlipDirection::HORIZONTAL:
|
405
|
+
case FlipDirection::DIAGONAL:
|
406
|
+
for (auto it = channels.begin(); it != channels.end(); it++) {
|
407
|
+
uint8_t** plane = this->get(*it);
|
408
|
+
if (plane == NULL) {
|
409
|
+
continue;
|
410
|
+
}
|
411
|
+
for (int32_t h = 0; h < this->_height; h++) {
|
412
|
+
std::reverse(&(plane[h][0]), &(plane[h][this->_width]));
|
413
|
+
}
|
414
|
+
}
|
415
|
+
break;
|
416
|
+
|
417
|
+
default:
|
418
|
+
// DO NOTHING
|
419
|
+
// (警告抑制)
|
420
|
+
break;
|
421
|
+
}
|
422
|
+
|
423
|
+
// 次に縦方向に反転
|
424
|
+
switch (direction) {
|
425
|
+
case FlipDirection::VERTICAL:
|
426
|
+
case FlipDirection::DIAGONAL:
|
427
|
+
for (auto it = channels.begin(); it != channels.end(); it++) {
|
428
|
+
uint8_t** plane = this->get(*it);
|
429
|
+
if (plane == NULL) {
|
430
|
+
continue;
|
431
|
+
}
|
432
|
+
std::reverse(&(plane[0]), &(plane[this->_height]));
|
433
|
+
}
|
434
|
+
break;
|
435
|
+
|
436
|
+
default:
|
437
|
+
// DO NOTHING
|
438
|
+
// (警告抑制)
|
439
|
+
break;
|
440
|
+
}
|
441
|
+
}
|
442
|
+
|
239
443
|
public: // 演算子オーバーロード
|
240
444
|
bool operator==(const ImageData& data) const {
|
241
445
|
if (this->_ready && data._ready && (this->_mode == data._mode) && (this->_width == data._width) && (this->_height == data._height)) {
|
@@ -267,6 +471,47 @@ namespace Rixmap {
|
|
267
471
|
}
|
268
472
|
}
|
269
473
|
bool operator!=(const ImageData& data) const { return !(*this == data); }
|
474
|
+
|
475
|
+
/**
|
476
|
+
* 代入演算子実装
|
477
|
+
*/
|
478
|
+
ImageData& operator=(const ImageData& base) {
|
479
|
+
// チャンネルリスト
|
480
|
+
const ChannelArray& channels = base._mode.getChannels();
|
481
|
+
|
482
|
+
// ピクセルデータは新しく確保 (破壊防止)
|
483
|
+
std::map<Channel, uint8_t**> pixels;
|
484
|
+
ImageData::AllocatePixels(pixels, channels, base._width, base._height, false);
|
485
|
+
|
486
|
+
// ピクセルデータを複製
|
487
|
+
for (auto it = channels.begin(); it != channels.end(); it++) {
|
488
|
+
Channel channel = *it;
|
489
|
+
uint8_t** dstPlane = pixels.at(channel);
|
490
|
+
uint8_t** srcPlane = base._data.at(channel);
|
491
|
+
for (int32_t h = 0; h < base._height; h++) {
|
492
|
+
std::memcpy(&(dstPlane[h][0]), &(srcPlane[h][0]), sizeof(uint8_t) * base._width);
|
493
|
+
}
|
494
|
+
}
|
495
|
+
|
496
|
+
// ピクセルを移動
|
497
|
+
// TODO swapとか使ってるけど危ないなら複製する方向にしよう
|
498
|
+
ImageData::DeallocatePixels(this->_data, this->_width, this->_height);
|
499
|
+
this->_data.swap(pixels);
|
500
|
+
|
501
|
+
// 基本情報を複製
|
502
|
+
this->_mode = base._mode;
|
503
|
+
this->_width = base._width;
|
504
|
+
this->_height = base._height;
|
505
|
+
|
506
|
+
// パレットは参照
|
507
|
+
this->_palette = base._palette;
|
508
|
+
|
509
|
+
// 準備完了
|
510
|
+
this->_ready = true;
|
511
|
+
|
512
|
+
// 戻る
|
513
|
+
return *this;
|
514
|
+
}
|
270
515
|
};
|
271
516
|
|
272
517
|
/**
|
@@ -367,5 +612,5 @@ namespace Rixmap {
|
|
367
612
|
|
368
613
|
|
369
614
|
//============================================================================//
|
370
|
-
// $Id: image.hxx,v
|
615
|
+
// $Id: image.hxx,v 753dbf70cab3 2014/05/16 16:13:38 chikuchikugonzalez $
|
371
616
|
// vim: set sts=4 ts=4 sw=4 expandtab foldmethod=marker:
|
@@ -0,0 +1,226 @@
|
|
1
|
+
// -*- coding: utf-8 -*-
|
2
|
+
/**
|
3
|
+
* 変形処理用画素補間処理実装
|
4
|
+
*/
|
5
|
+
#pragma once
|
6
|
+
#include <array>
|
7
|
+
#include "common.hxx"
|
8
|
+
#include "image.hxx"
|
9
|
+
|
10
|
+
namespace Rixmap {
|
11
|
+
/**
|
12
|
+
* 補間処理方法.
|
13
|
+
*/
|
14
|
+
enum class Interpolation : int {
|
15
|
+
NONE = 0x00,
|
16
|
+
//NEAREST = 0x01, // 最近傍 (Nearest-Neighbor)
|
17
|
+
//BILINEAR = 0x02, // バイリニア (双一次)
|
18
|
+
BICUBIC = 0x03, // バイキュービック (双三次)
|
19
|
+
};
|
20
|
+
|
21
|
+
/**
|
22
|
+
* 画素補間処理ベースクラス.
|
23
|
+
* 補間処理自体はピクセルベースで実行されます.
|
24
|
+
*/
|
25
|
+
class Interpolator {
|
26
|
+
protected:
|
27
|
+
ImageData* _base; // 元画像
|
28
|
+
ModeInfo* _mode; // 元画像形式
|
29
|
+
|
30
|
+
// 補間処理キャッシュ
|
31
|
+
std::map<Channel, uint8_t> _pixel;
|
32
|
+
|
33
|
+
public:
|
34
|
+
Interpolator(ImageData* base) : _base(base), _mode(&(_base->getModeInfo())) {
|
35
|
+
this->reset();
|
36
|
+
}
|
37
|
+
|
38
|
+
virtual ~Interpolator() {}
|
39
|
+
|
40
|
+
public: // 補間処理用メソッド
|
41
|
+
/**
|
42
|
+
* 変換後に必要になる画像形式
|
43
|
+
*/
|
44
|
+
virtual Mode getPreferredMode() { return this->_mode->getMode(); }
|
45
|
+
|
46
|
+
/**
|
47
|
+
* 補間処理を実行します.
|
48
|
+
*/
|
49
|
+
virtual void calculate(double x, double y) = 0;
|
50
|
+
|
51
|
+
/**
|
52
|
+
* 計算済みピクセルデータを取得します.
|
53
|
+
*/
|
54
|
+
virtual uint8_t get(Channel channel) { return this->_pixel.at(channel); }
|
55
|
+
|
56
|
+
protected: // 非公開メンバ
|
57
|
+
virtual void reset() {
|
58
|
+
const ChannelArray& channels = this->_mode->getChannels();
|
59
|
+
for (auto it = channels.begin(); it != channels.end(); it++) {
|
60
|
+
Channel channel = *it;
|
61
|
+
if (channel == Channel::ALPHA) {
|
62
|
+
this->_pixel[channel] = 255;
|
63
|
+
} else {
|
64
|
+
this->_pixel[channel] = 0;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
};
|
69
|
+
|
70
|
+
/**
|
71
|
+
* なにもしない補間処理実装
|
72
|
+
*/
|
73
|
+
class NOPInterpolator : public Interpolator {
|
74
|
+
public:
|
75
|
+
NOPInterpolator(ImageData* base) : Interpolator(base) {}
|
76
|
+
|
77
|
+
public:
|
78
|
+
virtual void calculate(double x, double y) override {
|
79
|
+
this->reset();
|
80
|
+
int32_t dx = static_cast<int32_t>(x);
|
81
|
+
int32_t dy = static_cast<int32_t>(y);
|
82
|
+
if (this->_base->isInside(dx, dy)) {
|
83
|
+
const ChannelArray& channels = this->_mode->getChannels();
|
84
|
+
for (auto it = channels.begin(); it != channels.end(); it++) {
|
85
|
+
Channel channel = *it;
|
86
|
+
this->_pixel[channel] = this->_base->get(channel, dx, dy);
|
87
|
+
}
|
88
|
+
}
|
89
|
+
}
|
90
|
+
};
|
91
|
+
|
92
|
+
/**
|
93
|
+
* バイキュービック法による補間処理実装
|
94
|
+
*/
|
95
|
+
class BicubicInterpolator : public Interpolator {
|
96
|
+
protected:
|
97
|
+
double _factor; // sinc関数パラメータ
|
98
|
+
ModeInfo _preferredMode; // 推奨画像形式
|
99
|
+
|
100
|
+
public:
|
101
|
+
BicubicInterpolator(ImageData* base, double factor = -1) : Interpolator(base), _factor(factor) {
|
102
|
+
switch (this->_mode->getMode()) {
|
103
|
+
case Mode::RGB:
|
104
|
+
case Mode::RGBA:
|
105
|
+
this->_preferredMode = ModeInfo(this->_mode->getMode());
|
106
|
+
break;
|
107
|
+
|
108
|
+
default:
|
109
|
+
this->_preferredMode = ModeInfo(Mode::RGB);
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
protected: // 非公開メンバ関数
|
114
|
+
/**
|
115
|
+
* sinc関数 (http://ja.wikipedia.org/wiki/Sinc%E9%96%A2%E6%95%B0) の近似式
|
116
|
+
*/
|
117
|
+
static inline double sinc(double t, double f = -1) {
|
118
|
+
using namespace std; // powがmingw x86_64版rubyだとstd名前空間付きで使えないため
|
119
|
+
double t0 = abs(t);
|
120
|
+
if (t0 > 2) {
|
121
|
+
return 0.0;
|
122
|
+
} else if (t0 <= 1.0) {
|
123
|
+
double n0 = (f + 2) * pow(t0, 3.0);
|
124
|
+
double n1 = (f + 3) * pow(t0, 2.0);
|
125
|
+
return n0 - n1 + 1;
|
126
|
+
} else {
|
127
|
+
double n0 = f * pow(t0, 3.0);
|
128
|
+
double n1 = (5 * f) * pow(t0, 2.0);
|
129
|
+
double n2 = (8 * f) * t0;
|
130
|
+
double n3 = 4 * f;
|
131
|
+
return n0 - n1 + n2 - n3;
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
public:
|
136
|
+
virtual Mode getPreferredMode() override {
|
137
|
+
return this->_preferredMode.getMode();
|
138
|
+
}
|
139
|
+
|
140
|
+
virtual void calculate(double x, double y) override {
|
141
|
+
static int32_t sx[4]; // 周辺座標点 (16点)
|
142
|
+
static int32_t sy[4];
|
143
|
+
static double dx[4]; // 周辺座標の比率
|
144
|
+
static double dy[4];
|
145
|
+
static double hx[4]; // sinc関数キャッシュ
|
146
|
+
static double hy[4];
|
147
|
+
static std::map<Channel, std::array<std::array<uint8_t, 4>, 4>> pixels;
|
148
|
+
|
149
|
+
// ピクセルデータを初期化
|
150
|
+
this->reset();
|
151
|
+
|
152
|
+
// 周辺座標設定
|
153
|
+
sx[1] = lround(std::floor(x)); // [x]
|
154
|
+
sx[0] = sx[1] - 1; // [x] - 1
|
155
|
+
sx[2] = sx[1] + 1; // [x] + 1
|
156
|
+
sx[3] = sx[1] + 2; // [x] + 2
|
157
|
+
sy[1] = lround(std::floor(y)); // [y]
|
158
|
+
sy[0] = sy[1] - 1; // [y] - 1
|
159
|
+
sy[2] = sy[1] + 1; // [y] + 1
|
160
|
+
sy[3] = sy[1] + 2; // [y] + 2
|
161
|
+
|
162
|
+
// 距離を計算
|
163
|
+
dx[0] = 1 + x - sx[1];
|
164
|
+
dx[1] = x - sx[1];
|
165
|
+
dx[2] = sx[1] + 1 - x;
|
166
|
+
dx[3] = sx[1] + 2 - x;
|
167
|
+
dy[0] = 1 + y - sy[1];
|
168
|
+
dy[1] = y - sy[1];
|
169
|
+
dy[2] = sy[1] + 1 - y;
|
170
|
+
dy[3] = sy[1] + 2 - y;
|
171
|
+
|
172
|
+
// sinc関数による重み計算
|
173
|
+
for (int i = 0; i < 4; i++) {
|
174
|
+
hx[i] = BicubicInterpolator::sinc(dx[i], this->_factor);
|
175
|
+
hy[i] = BicubicInterpolator::sinc(dy[i], this->_factor);
|
176
|
+
}
|
177
|
+
|
178
|
+
// 周辺ピクセルデータ
|
179
|
+
for (int32_t h = 0; h < 4; h++) {
|
180
|
+
for (int32_t w = 0; w < 4; w++) {
|
181
|
+
Color col = this->_base->fetch(sx[w], sy[h]);
|
182
|
+
pixels[Channel::RED ][h][w] = col.getRed();
|
183
|
+
pixels[Channel::GREEN][h][w] = col.getGreen();
|
184
|
+
pixels[Channel::BLUE ][h][w] = col.getBlue();
|
185
|
+
pixels[Channel::ALPHA][h][w] = col.getAlpha();
|
186
|
+
}
|
187
|
+
}
|
188
|
+
|
189
|
+
// 画素値を計算
|
190
|
+
double matrix[4];
|
191
|
+
const ChannelArray& channels = this->_preferredMode.getChannels();
|
192
|
+
for (auto it = channels.begin(); it != channels.end(); it++) {
|
193
|
+
Channel channel = *it;
|
194
|
+
for (int m = 0; m < 4; m++) {
|
195
|
+
matrix[m] = 0.0;
|
196
|
+
for (int h = 0; h < 4; h++) {
|
197
|
+
matrix[m] += hy[h] * pixels[channel][h][m];
|
198
|
+
}
|
199
|
+
}
|
200
|
+
double v = 0.0;
|
201
|
+
for (int w = 0; w < 4; w++) {
|
202
|
+
v += matrix[w] * hx[w];
|
203
|
+
}
|
204
|
+
this->_pixel[channel] = ROUND2BYTE(lround(v));
|
205
|
+
}
|
206
|
+
}
|
207
|
+
|
208
|
+
protected: // 非公開メンバ
|
209
|
+
virtual void reset() {
|
210
|
+
const ChannelArray& channels = this->_preferredMode.getChannels();
|
211
|
+
for (auto it = channels.begin(); it != channels.end(); it++) {
|
212
|
+
Channel channel = *it;
|
213
|
+
if (channel == Channel::ALPHA) {
|
214
|
+
this->_pixel[channel] = 255;
|
215
|
+
} else {
|
216
|
+
this->_pixel[channel] = 0;
|
217
|
+
}
|
218
|
+
}
|
219
|
+
}
|
220
|
+
};
|
221
|
+
}
|
222
|
+
|
223
|
+
|
224
|
+
//============================================================================//
|
225
|
+
// $Id: interpolator.hxx,v 753dbf70cab3 2014/05/16 16:13:38 chikuchikugonzalez $
|
226
|
+
// vim: set sts=4 ts=4 sw=4 expandtab foldmethod=marker:
|
data/src/rixmap/palette.hxx
CHANGED
@@ -113,7 +113,19 @@ namespace Rixmap {
|
|
113
113
|
throw std::out_of_range(msg.str());
|
114
114
|
}
|
115
115
|
}
|
116
|
-
|
116
|
+
|
117
|
+
/**
|
118
|
+
* const用実装.
|
119
|
+
*/
|
120
|
+
inline const Color& get(size_t offset) const {
|
121
|
+
if (/* 0 <= offset && */ offset <= this->_colors.size()) {
|
122
|
+
return this->_colors[offset];
|
123
|
+
} else {
|
124
|
+
std::ostringstream msg;
|
125
|
+
msg << "offsetis out of range: " << offset << " for 0.." << this->_colors.size();
|
126
|
+
throw std::out_of_range(msg.str());
|
127
|
+
}
|
128
|
+
}
|
117
129
|
|
118
130
|
/**
|
119
131
|
* 指定したオフセットのカラーデータを変更します.
|
@@ -216,5 +228,5 @@ namespace Rixmap {
|
|
216
228
|
|
217
229
|
|
218
230
|
//============================================================================//
|
219
|
-
// $Id: palette.hxx,v
|
231
|
+
// $Id: palette.hxx,v 753dbf70cab3 2014/05/16 16:13:38 chikuchikugonzalez $
|
220
232
|
// vim: set sts=4 ts=4 sw=4 expandtab foldmethod=marker:
|