|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2021 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // ビットマップ
9: //
10:
11: #include "bitmap.h"
1.1.1.5 root 12: #include "mainapp.h"
1.1 root 13: #include <cmath>
14:
15: //
16: // ビットマップ (基本クラス)
17: //
18:
19: // コンストラクタ
1.1.1.5 root 20: BitmapBase::BitmapBase(uint depth_, uint width_, uint height_, const void *buf_)
1.1.1.4 root 21: : depth(depth_)
1.1 root 22: {
1.1.1.4 root 23: assert(depth == 1 || (depth % 8) == 0);
24: Create(width_, height_, buf_);
1.1 root 25: }
26:
27: // デストラクタ
28: BitmapBase::~BitmapBase()
29: {
30: }
31:
1.1.1.4 root 32: // サイズを指定してビットマップを作成する。
33: // width_, height_ は 0 が指定される場合もある(初期状態)。
34: // buf_ が NULL ならこっちでメモリを確保。
35: void
1.1.1.5 root 36: BitmapBase::Create(uint width_, uint height_, const void *buf_)
1.1 root 37: {
1.1.1.4 root 38: width = width_;
1.1 root 39: height = height_;
40:
1.1.1.4 root 41: if (depth == 1) {
1.1 root 42: stride = (width + 7) / 8;
43: } else {
1.1.1.4 root 44: stride = width * depth / 8;
45: }
46:
1.1.1.5 root 47: owned_buf.Reset();
1.1.1.4 root 48: if (buf_) {
49: buf = static_cast<uint8 *>(const_cast<void *>(buf_));
50: } else {
51: if (width > 0 && height > 0) {
1.1.1.5 root 52: owned_buf.Reset(stride * height);
1.1.1.4 root 53: }
54: buf = owned_buf.get();
1.1 root 55: }
1.1.1.4 root 56: }
57:
1.1.1.6 root 58: // コピーコンストラクタ
59: BitmapBase::BitmapBase(const BitmapBase& src)
60: {
61: width = src.width;
62: height = src.height;
63: stride = src.stride;
64: depth = src.depth;
65:
66: owned_buf = src.owned_buf;
67: // src.buf は src.owned_buf かクラス外のどこか (か NULL) を指している。
68: if (src.buf == src.owned_buf.get()) {
69: buf = owned_buf.get();
70: } else {
71: buf = src.buf;
72: }
73: }
74:
75: // コピー代入演算子
76: BitmapBase&
77: BitmapBase::operator=(const BitmapBase& src)
78: {
79: owned_buf.Reset();
80:
81: width = src.width;
82: height = src.height;
83: stride = src.stride;
84: depth = src.depth;
85:
86: owned_buf = src.owned_buf;
87: // src.buf は src.owned_buf かクラス外のどこか (か NULL) を指している。
88: if (src.buf == src.owned_buf.get()) {
89: buf = owned_buf.get();
90: } else {
91: buf = src.buf;
92: }
93:
94: return *this;
95: }
96:
1.1.1.4 root 97: // 全域をコピーしてくる。
98: void
99: BitmapBase::CopyFrom(const BitmapBase *src)
100: {
101: assert(src);
102: assert(stride == src->stride);
103: assert(height == src->height);
1.1.1.3 root 104:
1.1.1.4 root 105: memcpy(buf, src->buf, stride * height);
1.1 root 106: }
107:
108:
109: //
110: // 1bpp ビットマップ
111: //
112:
113: // コンストラクタ
114: BitmapI1::BitmapI1()
1.1.1.4 root 115: : BitmapI1(0, 0) // 移譲
1.1 root 116: {
117: }
118:
119: // コンストラクタ
1.1.1.5 root 120: BitmapI1::BitmapI1(uint width_, uint height_)
1.1.1.4 root 121: : BitmapI1(NULL, width_, height_) // 移譲
1.1 root 122: {
123: }
124:
125: // コンストラクタ
1.1.1.5 root 126: BitmapI1::BitmapI1(const void *buf_, uint width_, uint height_)
1.1.1.4 root 127: : inherited(1, width_, height_, buf_)
1.1 root 128: {
129: }
130:
131: // デストラクタ
132: BitmapI1::~BitmapI1()
133: {
134: }
135:
1.1.1.4 root 136: // (x, y) のアドレスを返す関数だが、I1 ではサポートしていない。
137: uint8 *
138: BitmapI1::GetPtr(int x, int y) const
1.1 root 139: {
1.1.1.4 root 140: assertmsg(false, "GetPtr() not work for BitmapI1");
141: return NULL;
1.1 root 142: }
143:
1.1.1.6 root 144: // 1ラスタを読み込む/書き出すマクロ。副作用もりもり。
145: #define Load(bits, src) do { \
146: bits = 0; \
147: for (int x = 0; x < stride; x++) { \
148: bits <<= 8; \
149: bits |= *src++; \
150: } \
151: } while (0)
152: #define Store(bits, dst) do { \
153: for (int x = stride - 1; x >= 0; x--) { \
154: *dst++ = (bits >> (x * 8)) & 0xff; \
155: } \
156: } while (0)
157:
158: // このビットマップ(フォント)をボールドにした新しいビットマップを返す。
159: BitmapI1
160: BitmapI1::ConvertToBold() const
161: {
162: assert(stride <= 8);
163:
164: BitmapI1 dst(width, height);
165: const uint8 *s = buf;
166: uint8 *d = dst.buf;
167: for (int y = 0; y < height; y++) {
168: uint64 bits;
169: Load(bits, s);
170: bits |= bits >> 1;
171: Store(bits, d);
172: }
173: return dst;
174: }
175:
176: // このビットマップ(フォント)をイタリックにした新しいビットマップを返す。
177: BitmapI1
178: BitmapI1::ConvertToItalic() const
179: {
180: assert(stride <= 7);
181:
182: BitmapI1 dst(width, height);
183: int half = height / 2;
184: const uint8 *s = buf;
185: uint8 *d = dst.buf;
186: int y;
187:
188: // 上段は、右端が sticky な右シフト。
189: for (y = 0; y <= half; y++) {
190: uint64 bits;
191: Load(bits, s);
192: bits = (bits >> 1) | (bits & 1);
193: Store(bits, d);
194: }
195:
196: // 下段は、シフトしないのでコピー。
197: memcpy(d, s, stride * (height - y));
198:
199: return dst;
200: }
201:
202: #undef Load
203: #undef Store
204:
1.1 root 205:
206: //
207: // 8bpp ビットマップ
208: //
209:
210: // コンストラクタ
211: BitmapI8::BitmapI8()
1.1.1.4 root 212: : BitmapI8(0, 0) // 移譲
1.1 root 213: {
214: }
215:
216: // コンストラクタ
1.1.1.5 root 217: BitmapI8::BitmapI8(uint width_, uint height_)
1.1.1.4 root 218: : BitmapI8(NULL, width_, height_) // 移譲
1.1 root 219: {
220: }
221:
222: // コンストラクタ
1.1.1.5 root 223: BitmapI8::BitmapI8(const void *buf_, uint width_, uint height_)
1.1.1.4 root 224: : inherited(8, width_, height_, buf_)
1.1 root 225: {
226: }
227:
228: // デストラクタ
229: BitmapI8::~BitmapI8()
230: {
231: }
232:
233:
234: //
235: // 24bpp ビットマップ
236: //
237:
238: // コンストラクタ
239: BitmapRGB::BitmapRGB()
1.1.1.4 root 240: : BitmapRGB(0, 0) // 移譲
1.1 root 241: {
242: }
243:
244: // コンストラクタ
1.1.1.5 root 245: BitmapRGB::BitmapRGB(uint width_, uint height_)
1.1.1.4 root 246: : BitmapRGB(NULL, width_, height_) // 移譲
1.1 root 247: {
248: }
249:
250: // コンストラクタ
1.1.1.5 root 251: BitmapRGB::BitmapRGB(const void *buf_, uint width_, uint height_)
1.1.1.4 root 252: : inherited(24, width_, height_, buf_)
1.1 root 253: {
254: }
255:
256: // デストラクタ
257: BitmapRGB::~BitmapRGB()
258: {
259: }
260:
1.1.1.5 root 261:
262: //
263: // 32bpp (RGBX) ビットマップ
264: //
265:
266: // コンストラクタ
267: BitmapRGBX::BitmapRGBX()
268: : BitmapRGBX(0, 0) // 移譲
269: {
270: }
271:
272: // コンストラクタ
273: BitmapRGBX::BitmapRGBX(uint width_, uint height_)
274: : BitmapRGBX(NULL, width_, height_) // 移譲
275: {
276: }
277:
278: // コンストラクタ
279: BitmapRGBX::BitmapRGBX(const void *buf_, uint width_, uint height_)
280: : inherited(32, width_, height_, buf_)
281: {
282: #if defined(HAVE_AVX2)
283: enable_avx2 = gMainApp.enable_avx2;
284: #endif
285: }
286:
287: // デストラクタ
288: BitmapRGBX::~BitmapRGBX()
289: {
290: }
291:
292: // (dx, dy) にビットマップ(I1) src を描画する。
1.1 root 293: // 描画範囲をはみ出さないこと。
294: void
1.1.1.5 root 295: BitmapRGBX::DrawBitmapI1(int dx, int dy, const BitmapI1& src,
296: const Color *palette)
1.1 root 297: {
298: assert(buf);
299:
300: const uint8 *srcbuf = src.GetBuf();
301:
302: uint8 data = 0;
1.1.1.5 root 303: for (uint y = 0; y < src.GetHeight(); y++) {
304: uint32 *d = (uint32 *)GetPtr(dx, dy + y);
305: for (uint x = 0; x < src.GetWidth(); x++) {
1.1 root 306: if (__predict_false(x % 8 == 0)) {
307: data = *srcbuf++;
308: }
309: int cc;
310: if ((int8)data < 0) {
311: cc = 1;
312: } else {
313: cc = 0;
314: }
315: data <<= 1;
316:
1.1.1.5 root 317: *d++ = palette[cc].u32;
1.1 root 318: }
319: }
320: }
321:
322: // (dx, dy) にビットマップ src を scale 倍にして描画する。
323: // 描画範囲をはみ出さないこと。
324: void
1.1.1.5 root 325: BitmapRGBX::DrawBitmapI1Scale(int dx, int dy, const BitmapI1& src,
326: const Color *palette, int scale)
1.1 root 327: {
328: assert(buf);
329:
330: const uint8 *srcbuf = src.GetBuf();
331: uint8 data = 0;
1.1.1.5 root 332: uint dx0 = dx;
1.1 root 333:
1.1.1.5 root 334: for (uint y = 0; y < src.GetHeight(); y++) {
1.1 root 335: dx = dx0;
1.1.1.5 root 336: for (uint x = 0; x < src.GetWidth(); x++) {
1.1 root 337: if (__predict_false(x % 8 == 0)) {
338: data = *srcbuf++;
339: }
340: int cc;
341: if ((int8)data < 0) {
342: cc = 1;
343: } else {
344: cc = 0;
345: }
346: data <<= 1;
347:
348: FillRect(palette[cc], dx, dy, scale, scale);
349: dx += scale;
350: }
351: dy += scale;
352: }
353: }
354:
1.1.1.5 root 355: // BitmapI8 内の s8 から BitmapRGBX 内の d32 に width ピクセルをコピー (C++ 版)
356: // width は双方の画像をはみ出さないこと。
357: void
358: BitmapRGBX::CopyLineI8_gen(uint32 *d32, const uint8 *s8, uint width,
359: const Color *palette)
360: {
361: uint x = 0;
362:
363: if (__predict_true((((uintptr_t)s8) & 3) == 0)) {
364: // 4ピクセルずつ処理する
365: uint w1 = width & ~3;
366: for (; x < w1; x += 4) {
367: uint32 cc4 = *(const uint32 *)s8;
368: s8 += 4;
369:
370: // 4ピクセルをそれぞれ Color に変換
371: #if BYTE_ORDER == LITTLE_ENDIAN
372: // cc4 = $I3I2I1I0
373: Color c0 = palette[ cc4 & 0xff];
374: Color c1 = palette[(cc4 >> 8) & 0xff];
375: Color c2 = palette[(cc4 >> 16) & 0xff];
376: Color c3 = palette[ cc4 >> 24 ];
377: #else
378: // cc4 = $I0I1I2I3
379: Color c0 = palette[ cc4 >> 24 ];
380: Color c1 = palette[(cc4 >> 16) & 0xff];
381: Color c2 = palette[(cc4 >> 8) & 0xff];
382: Color c3 = palette[ cc4 & 0xff];
383: #endif
384:
385: *d32++ = c0.u32;
386: *d32++ = c1.u32;
387: *d32++ = c2.u32;
388: *d32++ = c3.u32;
389: }
390: }
391:
392: for (; x < width; x++) {
393: uint cc = *s8++;
394: Color c = palette[cc];
395: *d32++ = c.u32;
396: }
397: }
398:
399: // (dx, dy) にビットマップ(I8) src の範囲 sr を描画する。
400: // 描画範囲をはみ出さないこと。
401: void
402: BitmapRGBX::DrawBitmapI8(int dx, int dy, const BitmapI8& src,
403: const Color *palette, const Rect& sr)
404: {
405: #if defined(HAVE_AVX2)
406: if (__predict_true(enable_avx2)) {
407: for (uint y = 0; y < sr.h; y++) {
408: uint32 *d = (uint32 *)GetPtr(dx, dy + y);
409: const uint8 *s = src.GetPtr(sr.x, sr.y + y);
410: CopyLineI8_avx2(d, s, sr.w, palette);
411: }
412: } else
413: #endif
414: {
415: for (uint y = 0; y < sr.h; y++) {
416: uint32 *d = (uint32 *)GetPtr(dx, dy + y);
417: const uint8 *s = src.GetPtr(sr.x, sr.y + y);
418: CopyLineI8_gen(d, s, sr.w, palette);
419: }
420: }
421: }
422:
423: // ビットマップ(I8) src の全域を指定のラスターのみ描画する。
424: void
425: BitmapRGBX::DrawBitmapI8Raster(const BitmapI8& src, const Color *palette,
426: const uint8 *update_raster)
427: {
428: uint width = src.GetWidth();
429: uint height = src.GetHeight();
430:
431: #if defined(HAVE_AVX2)
432: if (__predict_true(enable_avx2)) {
433: for (uint y = 0; y < height; y++) {
434: if (update_raster[y] == 0) {
435: continue;
436: }
437: uint32 *d = (uint32 *)GetRowPtr(y);
438: const uint8 *s = src.GetRowPtr(y);
439: CopyLineI8_avx2(d, s, width, palette);
440: }
441: } else
442: #endif
443: {
444: for (uint y = 0; y < height; y++) {
445: if (update_raster[y] == 0) {
446: continue;
447: }
448: uint32 *d = (uint32 *)GetRowPtr(y);
449: const uint8 *s = src.GetRowPtr(y);
450: CopyLineI8_gen(d, s, width, palette);
451: }
452: }
453: }
454:
1.1.1.2 root 455: // 分数で表現可能な拡大縮小描画。
456: // dr にビットマップ src の (sxN / sxD, syN / syD) からを
457: // dst の 1 ピクセルに対して (sxS / sxD, syS / syD) ずつ動かした位置を
458: // 読み出し位置として描画する。
459: // 描画範囲をはみ出さないこと。
460: void
1.1.1.5 root 461: BitmapRGBX::DrawBitmapI8Scale(const Rect& dr, const BitmapI8& src,
462: const Color *palette,
1.1.1.2 root 463: int sxN, int sxD, int sxS, int syN, int syD, int syS)
464: {
465: assert(buf);
466: assert(sxD >= 1);
467: assert(syD >= 1);
468: assert(sxS != 0);
469: assert(syS != 0);
470:
471: int sxI = sxN / sxD;
472: int syI = syN / syD;
473:
474: sxN %= sxD;
475: syN %= syD;
476:
477: int sxI0 = sxI;
478: int sxN0 = sxN;
479:
480: int sx;
481: int sy = syI - 1;
482:
1.1.1.5 root 483: for (uint y = 0; y < dr.h; y++) {
484: uint32 *d = (uint32 *)GetPtr(dr.x, dr.y + y);
1.1.1.2 root 485:
486: if (sy != syI) {
487: sy = syI;
488:
489: sx = sxI0 - 1;
490: sxI = sxI0;
491: sxN = sxN0;
492:
493: uint8 cc = 0;
1.1.1.5 root 494: for (uint x = 0; x < dr.w; x++) {
1.1.1.2 root 495: if (sx != sxI) {
496: sx = sxI;
497: const uint8 *s = src.GetPtr(sx, sy);
498: cc = *s;
499: }
1.1.1.5 root 500: *d++ = palette[cc].u32;
1.1.1.2 root 501:
502: sxN += sxS;
503: if (sxN >= sxD) {
504: sxI += sxN / sxD;
505: sxN %= sxD;
506: }
507: }
508: } else {
509: // 前のラスタの中からコピーすればよい
1.1.1.5 root 510: memcpy(d, (uint8 *)d - stride, dr.w * 4);
1.1.1.2 root 511: }
512:
513: syN += syS;
514: if (syN >= syD) {
515: syI += syN / syD;
516: syN %= syD;
517: }
518: }
519: }
520:
1.1.1.7 ! root 521: // (dx, dy) にビットマップ(RGBX) src の範囲 sr を描画する。
1.1.1.5 root 522: // 描画範囲をはみ出さないこと。
523: void
1.1.1.7 ! root 524: BitmapRGBX::DrawBitmap(int dx, int dy, const BitmapRGBX& src, const Rect& sr)
1.1.1.5 root 525: {
526: assert(buf);
527:
1.1.1.7 ! root 528: for (uint y = 0; y < sr.h; y++) {
1.1.1.5 root 529: uint32 *d = (uint32 *)GetPtr(dx, dy + y);
1.1.1.7 ! root 530: const uint32 *s = (const uint32 *)src.GetPtr(sr.x, sr.y + y);
! 531: for (uint x = 0; x < sr.w; x++) {
1.1.1.5 root 532: *d++ = *s++;
533: }
534: }
535: }
536:
537: // src 全域が dr のサイズになるよう拡大縮小して描画。
538: // 拡大、縮小、等倍すべて可能。
539: void
540: BitmapRGBX::DrawBitmapStretch(const Rect& dr, const BitmapRGBX& src)
541: {
542: uint srcw = src.GetWidth();
543: uint srch = src.GetHeight();
544:
545: if (dr.w < srcw && dr.h < srch) {
546: uint w1 = srcw - dr.w;
547: uint h1 = srch - dr.h;
548: uint wn = dr.w / w1;
549: uint wr = dr.w % w1;
550: uint hn = dr.h / h1;
551: uint hr = dr.h % h1;
552: if (wr == 0 && hr == 0 && wn == hn && wn < 256) {
553: // (N-1)/N 倍に縮小。
554: DrawBitmapNtoNm1(dr, src, wn + 1);
555: return;
556: }
557: }
558: if (dr.w > srcw && dr.h > srch) {
559: uint w1 = dr.w - srcw;
560: uint h1 = dr.h - srch;
561: uint wn = dr.w / w1;
562: uint wr = dr.w % w1;
563: uint hn = dr.h / h1;
564: uint hr = dr.h % h1;
565: if (wr == 0 && hr == 0 && wn == hn && wn < 16) {
566: // N/(N-1) 倍に拡大。
567: DrawBitmapNm1toN(dr, src, wn);
568: return;
569: }
570: }
571: if (__predict_false(dr.w == srcw && dr.h == srch)) {
572: // 等倍。
573: DrawBitmap(dr.x, dr.y, src);
574: return;
575: }
576:
577: // 任意倍率の拡大縮小。
578: DrawBitmapMean(dr, src);
579: }
580:
581: // src 全域を (N-1)/N 倍に縮小描画。
582: // dr.w == src.Width * (N-1)/N
583: // dr.h == src.Height * (N-1)/N
584: // で呼び出すこと。
585: void
586: BitmapRGBX::DrawBitmapNtoNm1(const Rect& dr, const BitmapRGBX& src, uint N)
587: {
588: // 画像を 3/4 倍に縮小する場合 (N=4、N1=3、N2=2)。
589: //
590: // ※ この説明中では二次元座標の x, y を A(y, x) マクロの順に揃えてあり
591: // 数学で通常使う (x, y) の順ではない点に注意。
592: //
593: // dst の (0, 0) は src の (0, 0), (0, 1), (1, 0), (1, 1) の4点から、
594: // dst の (0, 1) は src の (0, 1), (0, 2), (1, 1), (1, 2) の4点から、
595: // dst の (0, 2) は src の (0, 2), (0, 3), (1, 2), (1, 3) の4点から、
596: // :
597: // 適当な割合で合成することになる。
598: // この時どの dst に対しても src が4点なことがポイント。
599: //
600: // 出力 3x3 ピクセルに対する入力の 4x4 ピクセル内部を各 2x2 に小分けに
601: // した計 8x8 の行列を以下のようにすると、この (空白で区切られた) 各 4x4
602: // ピクセル内の 4値の合計はいずれも 9。
603: //
604: // 0 0 0 0 0 0 0 0
605: // 0 9 3 6 6 3 9 0
606: //
607: // 0 3 1 2 2 1 3 0
608: // 0 6 2 4 4 2 6 0
609: //
610: // 0 6 2 4 4 2 6 0
611: // 0 3 1 2 2 1 3 0
612: //
613: // 0 9 3 6 6 3 9 0
614: // 0 0 0 0 0 0 0 0
615: //
616: // これを外周の 0 を取り除いた 6x6 行列に読み替えると、
617: // この (空白で区切られた) 各 3x3 ピクセル内の4値の合計はいずれも 16 に
618: // なっており、元の 4x4 ピクセルの成分を均等に分配できる。
619: //
620: // 9 3 6 6 3 9
621: // 3 1 2 2 1 3
622: //
623: // 6 2 4 4 2 6
624: // 6 2 4 4 2 6
625: //
626: // 3 1 2 2 1 3
627: // 9 3 6 6 3 9
628: //
629: //
630: // この計 6x6 の行列は 3x3 (N1 x N1) 行列の A[] から、オフセットを
631: // 移動させながら求めることが出来る。
632: //
633: // | 1 2 3 |
634: // A = | 2 4 6 | / 16
635: // | 3 6 9 |
636: //
637: // 具体的には dst(0, 0), dst(0, 1), dst(0, 2) の変換行列はそれぞれ
638: // 次のようになる。
639: //
640: // | 9 3 | | 6 6 | | 3 9 |
641: // a_(0,0) = | 3 1 |/16, a_(0,1) = | 2 2 |/16, a_(0,2) = | 1 3 |/16
642: //
643: // よって例えば dst(0, 2) の点の明るさは a_(0,2) を用いて
644: //
645: // dst(0, 2) = src(0, 2) * 3/16 + src(0, 3) * 9/16
646: // + src(1, 2) * 1/16 + src(1, 3) * 3/16
647: //
648: // で求められる。
649:
650: assertmsg(N >= 2, "N=%u", N);
651: uint N1 = N - 1;
652: uint N2 = N - 2;
653: std::vector<uint> a(N1 * N1);
654: #define A(y, x) a[(y) * N1 + (x)]
655:
656: // 小数部 8bit の固定小数点数で面積比変換行列を計算。
657: for (uint y = 0; y < N1; y++) {
658: for (uint x = 0; x < N1; x++) {
659: A(y, x) = (x + 1) * (y + 1) * 256 / (N * N);
660: }
661: }
662:
663: // N to (N-1) 変換なので、dst 側 N-1 ピクセルごとに
664: // src 側を N ピクセル参照すればよく、境界条件を簡略化できる。
665: const uint32 stride32 = src.GetStride() / sizeof(uint32);
666: for (uint sy = 0, dy = 0; dy < dr.h; sy += N, dy += N1) {
667: for (uint sx = 0, dx = 0; dx < dr.w; sx += N, dx += N1) {
668: const uint32 *s0 = (const uint32 *)src.GetPtr(sx, sy);
669: for (uint ty = 0; ty < N1; ty++) {
670: const uint32 *s1 = s0 + stride32;
671: uint32 *d = (uint32 *)GetPtr(dx, dy + ty);
672: for (uint tx = 0; tx < N1; tx++) {
673: uint a00 = A(N2 - ty, N2 - tx);
674: uint a01 = A(N2 - ty, tx);
675: uint a10 = A( ty, N2 - tx);
676: uint a11 = A( ty, tx);
677:
678: Color c00(s0[tx + 0]);
679: Color c01(s0[tx + 1]);
680: Color c10(s1[tx + 0]);
681: Color c11(s1[tx + 1]);
682:
683: uint r, g, b;
684: r = a00 * c00.r
685: + a01 * c01.r
686: + a10 * c10.r
687: + a11 * c11.r;
688: r >>= 8;
689:
690: g = a00 * c00.g
691: + a01 * c01.g
692: + a10 * c10.g
693: + a11 * c11.g;
694: g >>= 8;
695:
696: b = a00 * c00.b
697: + a01 * c01.b
698: + a10 * c10.b
699: + a11 * c11.b;
700: b >>= 8;
701:
702: Color c(r, g, b);
703: d[tx] = c.u32;
704: }
705: s0 += stride32;
706: }
707: }
708: }
709: #undef A
710: }
711:
712: // src 全域を N/(N-1) 倍に拡大描画。
713: // dr.w == src.Width * N/(N-1)
714: // dr.h == src.Height * N/(N-1)
715: // で呼び出すこと。
716: void
717: BitmapRGBX::DrawBitmapNm1toN(const Rect& dr, const BitmapRGBX& src, uint N)
718: {
719: assertmsg(N >= 2, "N=%u", N);
720: uint N1 = N - 1;
721: std::vector<uint> a(N * N);
722: #define A(y, x) a[(y) * N + (x)]
723:
724: // 小数部 8bit の固定小数点数で面積比変換行列を計算。
725: for (uint y = 0; y < N; y++) {
726: for (uint x = 0; x < N; x++) {
727: A(y, x) = x * y * 256 / (N1 * N1);
728: }
729: }
730:
731: const uint sstride32 = src.GetStride() / sizeof(uint32);
732: const uint dstride32 = GetStride() / sizeof(uint32);
733: // N to (N+1) 変換なので、
734: for (uint sy = 0, dy = 0; dy < dr.h; sy += N1, dy += N) {
735: for (uint sx = 0, dx = 0; dx < dr.w; sx += N1, dx += N) {
736: const uint32 *s0i = (const uint32 *)src.GetPtr(sx, sy);
737: uint32 *d = (uint32 *)GetPtr(dx, dy);
738: for (uint ty = 0; ty < N; ty++) {
739: const uint32 *s1 = (const uint32 *)(s0i + ty * sstride32);
740: const uint32 *s0 = (const uint32 *)(s1 - sstride32);
741: for (uint tx = 0; tx < N; tx++, s0++, s1++) {
742: uint a00 = A( ty, tx);
743: uint a01 = A( ty, N1 - tx);
744: uint a10 = A(N1 - ty, tx);
745: uint a11 = A(N1 - ty, N1 - tx);
746:
747: uint32 v00 = a00 ? s0[-1] : 0;
748: uint32 v01 = a01 ? s0[ 0] : 0;
749: uint32 v10 = a10 ? s1[-1] : 0;
750: uint32 v11 = a11 ? s1[ 0] : 0;
751:
752: Color c00(v00);
753: Color c01(v01);
754: Color c10(v10);
755: Color c11(v11);
756:
757: uint r, g, b;
758:
759: r = a00 * c00.r
760: + a01 * c01.r
761: + a10 * c10.r
762: + a11 * c11.r;
763: r >>= 8;
764:
765: g = a00 * c00.g
766: + a01 * c01.g
767: + a10 * c10.g
768: + a11 * c11.g;
769: g >>= 8;
770:
771: b = a00 * c00.b
772: + a01 * c01.b
773: + a10 * c10.b
774: + a11 * c11.b;
775: b >>= 8;
776:
777: Color c(r, g, b);
778: d[tx] = c.u32;
779: }
780: d += dstride32;
781: }
782: }
783: }
784: #undef A
785: }
786:
787: // src 全域が dr のサイズになるよう面積平均法で拡大縮小して描画。
788: // (等倍も無駄だけど動作はするはず)
789: void
790: BitmapRGBX::DrawBitmapMean(const Rect& dr, const BitmapRGBX& src)
791: {
792: // dst の 1 pixel が src の 何 pixel に相当するか
793: float tx = (float)src.GetWidth() / dr.w;
794: float ty = (float)src.GetHeight() / dr.h;
795:
796: // src 上を動くウィンドウ
797: RectF sr(0, 0, tx, ty);
798:
799: for (uint y = 0; y < dr.h; y++) {
800: uint32 *d = (uint32 *)GetRowPtr(y);
801: sr.x = 0;
802: for (uint x = 0; x < dr.w; x++) {
803: Color c = src.Mean(sr);
804: *d++ = c.u32;
805: sr.x += tx;
806: }
807: sr.y += ty;
808: }
809: }
810:
1.1 root 811: // sr 範囲の色の平均を返す
812: Color
1.1.1.5 root 813: BitmapRGBX::Mean(const RectF& sr) const
1.1 root 814: {
1.1.1.5 root 815: uint x1 = std::floor(sr.x);
816: uint y1 = std::floor(sr.y);
817: uint x2 = std::ceil(sr.x + sr.w);
818: uint y2 = std::ceil(sr.y + sr.h);
819: if (__predict_false(x2 > GetWidth())) {
1.1 root 820: x2 = GetWidth();
821: }
1.1.1.5 root 822: if (__predict_false(y2 > GetHeight())) {
1.1 root 823: y2 = GetHeight();
824: }
825:
1.1.1.5 root 826: float sum_r = 0;
827: float sum_g = 0;
828: float sum_b = 0;
829:
1.1 root 830: // サブピクセルの辺の長さ
1.1.1.5 root 831: float rx, ry;
832: float area = 0;
833: for (uint y = y1; y < y2; y++) {
1.1 root 834: if (y == y1) {
835: ry = 1 - (sr.y - y1);
836: } else if (y == y2 - 1) {
837: ry = 1 - (y2 - (sr.y + sr.h));
838: } else {
839: ry = 1;
840: }
841:
1.1.1.5 root 842: const uint32 *p = (const uint32 *)GetPtr(x1, y);
843: for (uint x = x1; x < x2; x++) {
844: Color c(*p++);
845:
1.1 root 846: if (x == x1) {
847: rx = 1 - (sr.x - x1);
1.1.1.5 root 848: } else if (x == x2 - 1) {
1.1 root 849: rx = 1 - (x2 - (sr.x + sr.w));
850: } else {
851: rx = 1;
852: }
853:
854: // 面積比をかけて合成
1.1.1.5 root 855: sum_r += c.r * rx * ry;
856: sum_g += c.g * rx * ry;
857: sum_b += c.b * rx * ry;
858: area += rx * ry;
1.1 root 859: }
860: }
861:
862: // 平均にする
1.1.1.5 root 863: sum_r /= area;
864: sum_g /= area;
865: sum_b /= area;
1.1 root 866:
1.1.1.5 root 867: return Color(sum_r, sum_g, sum_b);
1.1 root 868: }
869:
1.1.1.7 ! root 870: // (x, y) に点を描画する。
! 871: // 描画範囲をはみ出さないこと。
! 872: void
! 873: BitmapRGBX::DrawPoint(Color c, int x, int y)
! 874: {
! 875: uint32 *d = (uint32 *)GetPtr(x, y);
! 876: *d = c.u32;
! 877: }
! 878:
1.1 root 879: // 直線 (x1, y1) - (x2, y2) を描画する。終点は開区間。
880: // 描画範囲をはみ出さないこと。
881: void
1.1.1.5 root 882: BitmapRGBX::DrawLine(Color c, int x1, int y1, int x2, int y2)
1.1 root 883: {
884: if (y1 == y2) {
885: DrawLineH(c, x1, y1, x2);
886: } else if (x1 == x2) {
887: DrawLineV(c, x1, y1, y2);
888: } else {
889: assert(false);
890: }
891: }
892:
893: // 水平の直線 (x1, y1) - (x2, y1) を描画する。終点は開区間。
894: // 描画範囲をはみ出さないこと。
895: void
1.1.1.5 root 896: BitmapRGBX::DrawLineH(Color c, int x1, int y1, int x2)
1.1 root 897: {
898: assert(buf);
899:
900: if (__predict_false(x1 > x2)) {
901: std::swap(x1, x2);
902: x1++;
903: x2++;
904: }
905:
1.1.1.5 root 906: uint32 *d = (uint32 *)GetPtr(x1, y1);
907: for (int x = x1; x < x2; x++) {
908: *d++ = c.u32;
1.1 root 909: }
910: }
911:
912: // 垂直の直線 (x1, y1) - (x1, y2) を描画する。終点は開区間。
913: // 描画範囲をはみ出さないこと。
914: void
1.1.1.5 root 915: BitmapRGBX::DrawLineV(Color c, int x1, int y1, int y2)
1.1 root 916: {
917: assert(buf);
918:
919: if (__predict_false(y1 > y2)) {
920: std::swap(y1, y2);
921: y1++;
922: y2++;
923: }
924:
1.1.1.5 root 925: for (int y = y1; y < y2; y++) {
926: uint32 *d = (uint32 *)GetPtr(x1, y);
927: *d = c.u32;
1.1 root 928: }
929: }
930:
931: // 矩形 rect を色 c で塗りつぶす。
932: // 描画範囲をはみ出さないこと。
933: void
1.1.1.5 root 934: BitmapRGBX::FillRect(Color c, const Rect& rect)
1.1 root 935: {
936: assert(buf);
937:
1.1.1.5 root 938: DrawLineH(c, rect.x, rect.y, rect.x + rect.w);
939: CopyFromTop(rect);
940: }
1.1 root 941:
1.1.1.5 root 942: // rect の一番上のラスターを rect の残りのラスターにコピーする。
943: void
944: BitmapRGBX::CopyFromTop(const Rect& rect)
945: {
946: uint8 *s = GetPtr(rect.x, rect.y);
1.1 root 947: uint8 *d = s + stride;
1.1.1.5 root 948: const uint len = rect.w * 4;
1.1 root 949:
1.1.1.5 root 950: for (uint y = rect.y + 1; y < rect.y + rect.h; y++) {
1.1 root 951: memcpy(d, s, len);
952: d += stride;
953: }
954: }
955:
956: // 矩形 rect の辺を色 c で描画する。
957: // 描画範囲をはみ出さないこと。
958: void
1.1.1.5 root 959: BitmapRGBX::DrawRect(Color c, const Rect& rect)
1.1 root 960: {
961: assert(buf);
962:
963: int l = rect.x;
964: int t = rect.y;
965: int r = rect.GetRight();
966: int b = rect.GetBottom();
967:
968: DrawLineH(c, l, t, r); // 上辺→
969: DrawLineV(c, r, t, b); // 右辺↓
970: DrawLineH(c, r, b, l); // 下辺←
971: DrawLineV(c, l, b, t); // 左辺↑
972: }
1.1.1.4 root 973:
1.1.1.7 ! root 974: // 矩形 rect に内接する正円あるいは楕円を描画して中を塗りつぶす。
! 975: // 描画範囲をはみ出さないこと。
! 976: void
! 977: BitmapRGBX::DrawFillCircle(Color border, Color fill, const Rect& rect)
! 978: {
! 979: int D = rect.h; // 高さを基準にした直径
! 980: float r = D / 2;
! 981: float r2 = r * (r + 0.5); // 半径の二乗
! 982: float prev_d = -1;
! 983:
! 984: // Y 方向を 1 px ずつステップしながら上下両側を対称に描画する。
! 985: // 奇数個のときは真ん中を 2 回書くため、D&1 で補正。
! 986: for (int i = 0, end = r + (D & 1); i < end; i++) {
! 987: // x^2 + y^2 = r^2 を解いて、楕円の係数を掛ける。
! 988: float d = std::sqrt(r2 - r * r) * rect.w / rect.h;
! 989: r--;
! 990:
! 991: for (int j = 0; j < 2; j++) {
! 992: int y;
! 993: if (j == 0) {
! 994: y = rect.y + i;
! 995: } else {
! 996: y = rect.GetBottom() - i;
! 997: }
! 998:
! 999: int x1 = rect.x + rect.w / 2 - d;
! 1000: int x2 = rect.x + rect.w / 2 + d;
! 1001:
! 1002: DrawLineH(fill, x1, y, x2);
! 1003:
! 1004: DrawPoint(border, x1, y);
! 1005: DrawPoint(border, x2, y);
! 1006:
! 1007: int prev_x1 = rect.x + rect.w / 2 - prev_d;
! 1008: int prev_x2 = rect.x + rect.w / 2 + prev_d;
! 1009: DrawLineH(border, x1, y, prev_x1);
! 1010: DrawLineH(border, x2, y, prev_x2);
! 1011: }
! 1012:
! 1013: prev_d = d;
! 1014: }
! 1015: }
! 1016:
1.1.1.5 root 1017: // 全域を RGB に変換。
1018: void
1019: BitmapRGBX::ConvertToRGB(BitmapRGB& dst) const
1.1.1.4 root 1020: {
1.1.1.5 root 1021: assertmsg(dst.GetWidth() == GetWidth(),
1022: "dst.width=%u, src.width=%u", dst.GetWidth(), GetWidth());
1023: assertmsg(dst.GetHeight() == GetHeight(),
1024: "dst.height=%u, src.height=%u", dst.GetWidth(), GetWidth());
1025:
1026: #if defined(HAVE_AVX2)
1027: if (__predict_true(enable_avx2)) {
1028: ConvertToRGB_avx2(dst);
1029: } else
1030: #endif
1031: {
1032: ConvertToRGB_gen(dst);
1033: }
1034: }
1035:
1036: // RGBX から RGB への変換。(C++ 版)
1037: void
1038: BitmapRGBX::ConvertToRGB_gen(BitmapRGB& dst) const
1039: {
1040: const BitmapRGBX& src = *this;
1041:
1042: // この変換は一次元配列とみなしても行える。
1043: // モニタ等のパネルでも呼ばれるので何ピクセルで割り切れるかの前提は不可。
1044: uint total = GetWidth() * GetHeight();
1045: uint packed = total & ~3;
1046:
1047: const uint32 *s32 = (const uint32 *)src.GetRowPtr(0);
1048: const uint32 *s32total = s32 + total;
1049: const uint32 *s32packed = s32 + packed;
1050:
1051: uint32 *d32 = (uint32 *)dst.GetRowPtr(0);
1052: for (; s32 < s32packed; ) {
1053: // 4ピクセルずつ処理する
1054: uint32 c0 = *s32++;
1055: uint32 c1 = *s32++;
1056: uint32 c2 = *s32++;
1057: uint32 c3 = *s32++;
1058:
1059: #if BYTE_ORDER == LITTLE_ENDIAN
1060: // c0 = $X0B0G0R0
1061: // c1 = $X1B1G1R1
1062: // c2 = $X2B2G2R2
1063: // c3 = $X3B3G3R3
1064: //
1065: // d[0] = $00B0G0R0 | $R1000000 = $R1B0G0R0
1066: // d[1] = $0000B1G1 | $G2R20000 = $G2R2B1G1
1067: // d[2] = $000000B2 | $B3G3R300 = $B3G3R3B2
1068:
1069: *d32++ = (c0 ) | (c1 << 24);
1070: *d32++ = (c1 >> 8) | (c2 << 16);
1071: *d32++ = (c2 >> 16) | (c3 << 8);
1072: #else
1073: // c0 = $R0G0B0X0
1074: // c1 = $R1G1B1X1
1075: // c2 = $R2G2B2X2
1076: // c3 = $R3G3B3X3
1077: //
1078: // d[0] = $R0G0B000 | $000000R1 = $R0G0B0R1
1079: // d[1] = $G1B10000 | $0000R2G2 = $G1B1R2G2
1080: // d[2] = $B2000000 | $00R3G3B3 = $B2R3G3B3
1081:
1082: *d32++ = (c0 ) | (c1 >> 24);
1083: *d32++ = (c1 << 8) | (c2 >> 16);
1084: *d32++ = (c2 << 16) | (c3 >> 8);
1085: #endif
1086: }
1087:
1088: uint8 *d8 = (uint8 *)d32;
1089: for (; s32 < s32total; ) {
1090: // 残りを1ピクセルずつ処理する
1091: Color col(*s32++);
1092: *d8++ = col.r;
1093: *d8++ = col.g;
1094: *d8++ = col.b;
1095: }
1.1.1.4 root 1096: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.