|
|
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"
12: #include <cmath>
13:
14: //
15: // ビットマップ (基本クラス)
16: //
17:
18: // コンストラクタ
19: BitmapBase::BitmapBase()
20: {
21: }
22:
23: // コンストラクタ
24: BitmapBase::BitmapBase(int width_, int height_, int depth_)
25: {
26: Create(width_, height_, depth_);
27: }
28:
29: // デストラクタ
30: BitmapBase::~BitmapBase()
31: {
32: }
33:
1.1.1.3 ! root 34: // 作成。width_ * height_ が面積を持っていれば true を返す。
! 35: bool
1.1 root 36: BitmapBase::Create(int width_, int height_, int depth_)
37: {
38: assert(depth_ == 1 || depth_ == 8 || depth_ == 24);
39:
40: width = width_;
41: height = height_;
42:
43: if (depth_ == 1) {
44: stride = (width + 7) / 8;
45: } else {
46: stride = width * depth_ / 8;
47: }
1.1.1.3 ! root 48:
! 49: return (width > 0 && height > 0);
1.1 root 50: }
51:
52:
53: //
54: // 1bpp ビットマップ
55: //
56:
57: // コンストラクタ
58: BitmapI1::BitmapI1()
59: {
60: }
61:
62: // コンストラクタ
63: BitmapI1::BitmapI1(int width_, int height_)
64: {
65: Create(width_, height_);
66: }
67:
68: // コンストラクタ
69: BitmapI1::BitmapI1(const void *buf_, int width_, int height_)
70: : inherited(width_, height_, 1)
71: {
72: buf = static_cast<uint8 *>(const_cast<void *>(buf_));
73: }
74:
75: // デストラクタ
76: BitmapI1::~BitmapI1()
77: {
78: }
79:
80: // 作成
81: void
82: BitmapI1::Create(int width_, int height_)
83: {
1.1.1.3 ! root 84: if (inherited::Create(width_, height_, 1)) {
! 85: owned_buf.reset(new uint8[stride * height]);
! 86: buf = owned_buf.get();
! 87: } else {
! 88: owned_buf.reset();
! 89: buf = NULL;
! 90: }
1.1 root 91: }
92:
93: // カラーコード cc で全体を塗りつぶす。
94: // 未使用ビットも変更される。
95: void
96: BitmapI1::Fill(int cc)
97: {
98: assert(buf);
99:
100: memset(buf, (cc ? 0xff : 0), stride * height);
101: }
102:
103:
104: //
105: // 8bpp ビットマップ
106: //
107:
108: // コンストラクタ
109: BitmapI8::BitmapI8()
110: {
111: }
112:
113: // コンストラクタ
114: BitmapI8::BitmapI8(int width_, int height_)
115: {
116: Create(width_, height_);
117: }
118:
119: // コンストラクタ
120: BitmapI8::BitmapI8(const void *buf_, int width_, int height_)
121: {
1.1.1.2 root 122: Create(buf_, width_, height_);
1.1 root 123: }
124:
125: // デストラクタ
126: BitmapI8::~BitmapI8()
127: {
128: }
129:
130: // 作成
131: void
132: BitmapI8::Create(int width_, int height_)
133: {
1.1.1.3 ! root 134: if (inherited::Create(width_, height_, 8)) {
! 135: owned_buf.reset(new uint8[stride * height]);
! 136: buf = owned_buf.get();
! 137: } else {
! 138: owned_buf.reset();
! 139: buf = NULL;
! 140: }
1.1 root 141: }
142:
1.1.1.2 root 143: // 元データを指定して作成
144: void
145: BitmapI8::Create(const void *buf_, int width_, int height_)
146: {
1.1.1.3 ! root 147: if (inherited::Create(width_, height_, 8)) {
! 148: buf = static_cast<uint8 *>(const_cast<void *>(buf_));
! 149: } else {
! 150: buf = NULL;
! 151: }
1.1.1.2 root 152: }
153:
1.1 root 154: // カラーコード cc で全体を塗りつぶす。
155: // 未使用ビットも変更される。
156: void
157: BitmapI8::Fill(int cc)
158: {
159: assert(buf);
160:
161: memset(buf, cc, stride * height);
162: }
163:
164:
165: //
166: // 24bpp ビットマップ
167: //
168:
169: // コンストラクタ
170: BitmapRGB::BitmapRGB()
171: {
172: }
173:
174: // コンストラクタ
175: BitmapRGB::BitmapRGB(int width_, int height_)
176: {
177: Create(width_, height_);
178: }
179:
180: // コンストラクタ
181: BitmapRGB::BitmapRGB(const void *buf_, int width_, int height_)
182: : inherited(width_, height_, 24)
183: {
184: buf = static_cast<uint8 *>(const_cast<void *>(buf_));
185: }
186:
187: // デストラクタ
188: BitmapRGB::~BitmapRGB()
189: {
190: }
191:
192: // 作成
193: void
194: BitmapRGB::Create(int width_, int height_)
195: {
1.1.1.3 ! root 196: if (inherited::Create(width_, height_, 24)) {
! 197: owned_buf.reset(new uint8[stride * height]);
! 198: buf = owned_buf.get();
! 199: } else {
! 200: owned_buf.reset();
! 201: buf = NULL;
! 202: }
1.1 root 203: }
204:
205: // 色 c で全体を塗りつぶす。
206: void
207: BitmapRGB::Fill(Color c)
208: {
209: FillRect(c, 0, 0, width, height);
210: }
211:
212: // (dx, dy) にビットマップ src を描画する。
213: // 描画範囲をはみ出さないこと。
214: void
215: BitmapRGB::DrawBitmap(int dx, int dy, const BitmapI1& src, const Color *palette)
216: {
217: assert(buf);
218:
219: const uint8 *srcbuf = src.GetBuf();
220:
221: uint8 data = 0;
222: for (int y = 0; y < src.GetHeight(); y++) {
223: uint8 *d = GetPtr(dx, dy + y);
224: for (int x = 0; x < src.GetWidth(); x++) {
225: if (__predict_false(x % 8 == 0)) {
226: data = *srcbuf++;
227: }
228: int cc;
229: if ((int8)data < 0) {
230: cc = 1;
231: } else {
232: cc = 0;
233: }
234: data <<= 1;
235:
236: Color c = palette[cc];
237: *d++ = c.r;
238: *d++ = c.g;
239: *d++ = c.b;
240: }
241: }
242: }
243:
1.1.1.2 root 244: // (dx, dy) にビットマップ src の全域を描画する。
245: // 描画範囲をはみ出さないこと。
246: void
247: BitmapRGB::DrawBitmap(int dx, int dy, const BitmapI8& src, const Color *palette)
248: {
249: Rect sr(0, 0, src.GetWidth(), src.GetHeight());
250: DrawBitmap(dx, dy, src, palette, sr);
251: }
252:
1.1 root 253: // (dx, dy) にビットマップ src の範囲 sr を描画する。
254: // 描画範囲をはみ出さないこと。
255: void
256: BitmapRGB::DrawBitmap(int dx, int dy, const BitmapI8& src, const Color *palette,
257: const Rect& sr)
258: {
259: for (int y = 0; y < sr.h; y++) {
260: uint8 *d = GetPtr(dx, dy + y);
261: const uint8 *s = src.GetPtr(sr.x, sr.y + y);
262: for (int x = 0; x < sr.w; x++) {
263: int cc = *s++;
264: Color c = palette[cc];
265: *d++ = c.r;
266: *d++ = c.g;
267: *d++ = c.b;
268: }
269: }
270: }
271:
272: // (dx, dy) にビットマップ src を描画する。
273: // 描画範囲をはみ出さないこと。
274: void
275: BitmapRGB::DrawBitmap(int dx, int dy, const BitmapRGB& src)
276: {
277: assert(buf);
278:
279: const uint8 *s = src.GetBuf();
280:
281: for (int y = 0; y < src.GetHeight(); y++) {
282: uint8 *d = GetPtr(dx, dy + y);
283: for (int x = 0; x < src.GetWidth(); x++) {
284: *d++ = *s++;
285: *d++ = *s++;
286: *d++ = *s++;
287: }
288: }
289: }
290:
291: // (dx, dy) にビットマップ src を scale 倍にして描画する。
292: // 描画範囲をはみ出さないこと。
293: void
294: BitmapRGB::DrawScaleUp(int dx, int dy, int scale, const BitmapI1& src,
295: const Color *palette)
296: {
297: assert(buf);
298:
299: const uint8 *srcbuf = src.GetBuf();
300: uint8 data = 0;
301: int dx0 = dx;
302:
303: for (int y = 0; y < src.GetHeight(); y++) {
304: dx = dx0;
305: for (int x = 0; x < src.GetWidth(); x++) {
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:
317: FillRect(palette[cc], dx, dy, scale, scale);
318: dx += scale;
319: }
320: dy += scale;
321: }
322: }
323:
1.1.1.2 root 324: // 分数で表現可能な拡大縮小描画。
325: // dr にビットマップ src の (sxN / sxD, syN / syD) からを
326: // dst の 1 ピクセルに対して (sxS / sxD, syS / syD) ずつ動かした位置を
327: // 読み出し位置として描画する。
328: // 描画範囲をはみ出さないこと。
329: void
330: BitmapRGB::DrawScale(const Rect& dr, const BitmapI8& src, const Color *palette,
331: int sxN, int sxD, int sxS, int syN, int syD, int syS)
332: {
333: assert(buf);
334: assert(sxD >= 1);
335: assert(syD >= 1);
336: assert(sxS != 0);
337: assert(syS != 0);
338:
339: int sxI = sxN / sxD;
340: int syI = syN / syD;
341:
342: sxN %= sxD;
343: syN %= syD;
344:
345: int sxI0 = sxI;
346: int sxN0 = sxN;
347:
348: int sx;
349: int sy = syI - 1;
350:
351: for (int y = 0; y < dr.h; y++) {
352: uint8 *d = GetPtr(dr.x, dr.y + y);
353:
354: if (sy != syI) {
355: sy = syI;
356:
357: sx = sxI0 - 1;
358: sxI = sxI0;
359: sxN = sxN0;
360:
361: uint8 cc = 0;
362: for (int x = 0; x < dr.w; x++) {
363: if (sx != sxI) {
364: sx = sxI;
365: const uint8 *s = src.GetPtr(sx, sy);
366: cc = *s;
367: }
368: const Color& c = palette[cc];
369: *d++ = c.r;
370: *d++ = c.g;
371: *d++ = c.b;
372:
373: sxN += sxS;
374: if (sxN >= sxD) {
375: sxI += sxN / sxD;
376: sxN %= sxD;
377: }
378: }
379: } else {
380: // 前のラスタの中からコピーすればよい
381: memcpy(d, d - stride, dr.w * 3);
382: }
383:
384: syN += syS;
385: if (syN >= syD) {
386: syI += syN / syD;
387: syN %= syD;
388: }
389: }
390: }
391:
1.1 root 392: // sr 範囲の色の平均を返す
393: Color
394: BitmapRGB::Mean(const RectD& sr) const
395: {
396: int x1 = std::floor(sr.x);
397: int y1 = std::floor(sr.y);
398: int x2 = std::ceil(sr.x + sr.w);
399: int y2 = std::ceil(sr.y + sr.h);
400: if (__predict_false(x2 >= GetWidth())) {
401: x2 = GetWidth();
402: }
403: if (__predict_false(y2 >= GetHeight())) {
404: y2 = GetHeight();
405: }
406:
407: ColorD sum;
408: // サブピクセルの辺の長さ
409: double rx, ry;
410: double s = 0;
411: for (int y = y1; y < y2; y++) {
412: if (y == y1) {
413: ry = 1 - (sr.y - y1);
414: } else if (y == y2 - 1) {
415: ry = 1 - (y2 - (sr.y + sr.h));
416: } else {
417: ry = 1;
418: }
419:
420: const uint8 *p = GetPtr(x1, y);
421: for (int x = x1; x < x2; x++) {
422: if (x == x1) {
423: rx = 1 - (sr.x - x1);
424: } else if (y == x2 - 1) {
425: rx = 1 - (x2 - (sr.x + sr.w));
426: } else {
427: rx = 1;
428: }
429:
430: Color c;
431: c.r = *p++;
432: c.g = *p++;
433: c.b = *p++;
434:
435: // 面積比をかけて合成
436: sum.r += c.r * rx * ry;
437: sum.g += c.g * rx * ry;
438: sum.b += c.b * rx * ry;
439: s += rx * ry;
440: }
441: }
442:
443: // 平均にする
444: sum.r /= s;
445: sum.g /= s;
446: sum.b /= s;
447:
448: return Color(sum.r, sum.g, sum.b);
449: }
450:
451: // 伸縮して描画
452: void
453: BitmapRGB::StretchDraw(Rect dr, const BitmapRGB& src)
454: {
455: if (__predict_false(dr.w == src.GetWidth() && dr.h == src.GetHeight())) {
456: DrawBitmap(dr.x, dr.y, src);
457: return;
458: }
459:
460: // dst の 1 pixel が src の 何 pixel に相当するか
461: double tx = (double)src.GetWidth() / dr.w;
462: double ty = (double)src.GetHeight() / dr.h;
463:
464: // src 上を動くウィンドウ
465: RectD sr(0, 0, tx, ty);
466:
467: for (int y = 0; y < dr.h; y++) {
468: uint8 *d = GetRowPtr(y);
469: sr.x = 0;
470: for (int x = 0; x < dr.w; x++) {
471: Color c = src.Mean(sr);
472:
473: *d++ = c.r;
474: *d++ = c.g;
475: *d++ = c.b;
476: sr.x += tx;
477: }
478: sr.y += ty;
479: }
480: }
481:
482: // 直線 (x1, y1) - (x2, y2) を描画する。終点は開区間。
483: // 描画範囲をはみ出さないこと。
484: void
485: BitmapRGB::DrawLine(Color c, int x1, int y1, int x2, int y2)
486: {
487: if (y1 == y2) {
488: DrawLineH(c, x1, y1, x2);
489: } else if (x1 == x2) {
490: DrawLineV(c, x1, y1, y2);
491: } else {
492: assert(false);
493: }
494: }
495:
496: // 水平の直線 (x1, y1) - (x2, y1) を描画する。終点は開区間。
497: // 描画範囲をはみ出さないこと。
498: void
499: BitmapRGB::DrawLineH(Color c, int x1, int y1, int x2)
500: {
501: assert(buf);
502:
503: if (__predict_false(x1 > x2)) {
504: std::swap(x1, x2);
505: x1++;
506: x2++;
507: }
508:
509: uint8 *d = GetPtr(x1, y1);
510:
511: // 12バイト境界に整列するまで
512: for (; (uintptr_t)d % 4 != 0 && x1 < x2 ; x1++) {
513: *d++ = c.r;
514: *d++ = c.g;
515: *d++ = c.b;
516: }
517:
518: // 12バイト境界から 12バイト以上残ってる間
519: int cnt = (x2 - x1) / 4;
520: if (__predict_true(cnt > 0)) {
521: uint32 pat[3];
522: for (uint8 *p = (uint8 *)pat, *pend = p + sizeof(pat); p < pend; ) {
523: *p++ = c.r;
524: *p++ = c.g;
525: *p++ = c.b;
526: }
527: for (int i = 0; i < cnt; i++) {
528: memcpy(d, pat, sizeof(pat));
529: d += sizeof(pat);
530: }
531: x1 += cnt * sizeof(pat) / 3;
532: }
533:
534: // 残り
535: for (; x1 < x2; x1++) {
536: *d++ = c.r;
537: *d++ = c.g;
538: *d++ = c.b;
539: }
540: }
541:
542: // 垂直の直線 (x1, y1) - (x1, y2) を描画する。終点は開区間。
543: // 描画範囲をはみ出さないこと。
544: void
545: BitmapRGB::DrawLineV(Color c, int x1, int y1, int y2)
546: {
547: assert(buf);
548:
549: if (__predict_false(y1 > y2)) {
550: std::swap(y1, y2);
551: y1++;
552: y2++;
553: }
554:
555: for (; y1 < y2; y1++) {
556: uint8 *d = GetPtr(x1, y1);
557: *d++ = c.r;
558: *d++ = c.g;
559: *d++ = c.b;
560: }
561: }
562:
563: // 矩形 (dx, dy) - (dw * dh) を色 c で塗りつぶす。
564: // 描画範囲をはみ出さないこと。
565: void
566: BitmapRGB::FillRect(Color c, int dx, int dy, int dw, int dh)
567: {
568: FillRect(c, Rect(dx, dy, dw, dh));
569: }
570:
571: // 矩形 rect を色 c で塗りつぶす。
572: // 描画範囲をはみ出さないこと。
573: void
574: BitmapRGB::FillRect(Color c, const Rect& rect)
575: {
576: assert(buf);
577:
578: const int l = rect.x;
579: const int t = rect.y;
580:
581: DrawLineH(c, l, t, l + rect.w);
582:
583: uint8 *s = GetPtr(l, t);
584: uint8 *d = s + stride;
585: const int len = rect.w * 3;
586:
587: for (int y = t + 1; y < t + rect.h; y++) {
588: memcpy(d, s, len);
589: d += stride;
590: }
591: }
592:
593: // 矩形 (dx, dy) - (dw * dh) を色 c で描画する。
594: // 描画範囲をはみ出さないこと。
595: void
596: BitmapRGB::DrawRect(Color c, int dx, int dy, int dw, int dh)
597: {
598: DrawRect(c, Rect(dx, dy, dw, dh));
599: }
600:
601: // 矩形 rect の辺を色 c で描画する。
602: // 描画範囲をはみ出さないこと。
603: void
604: BitmapRGB::DrawRect(Color c, const Rect& rect)
605: {
606: assert(buf);
607:
608: int l = rect.x;
609: int t = rect.y;
610: int r = rect.GetRight();
611: int b = rect.GetBottom();
612:
613: DrawLineH(c, l, t, r); // 上辺→
614: DrawLineV(c, r, t, b); // 右辺↓
615: DrawLineH(c, r, b, l); // 下辺←
616: DrawLineV(c, l, b, t); // 左辺↑
617: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.