/** * @file pix32_resize.h * @brief バイリニアサンプリングで拡大縮小. * @author tenk* (Masashi Kitamura) * @note * - 縮小は平均画素法を併用. * x,yサイズを整数で割れるように、一旦(計算上)、拡大する。 * 拡大は整数倍でなくてよい、あたりはミソかも。 */ #ifndef PIX32_RESIZE_H #define PIX32_RESIZE_H #if !(defined __cplusplus) && !(defined inline) && (defined _WIN32) #define inline __inline #endif #ifndef PIX32_ARGB /// 8bit値のa,r,g,bを繋げて 32bit の色値にする. //#define PIX32_ARGB(a,r,g,b) ((((unsigned char)(a))<<24)|(((unsigned char)(r))<<16)|(((unsigned char)(g))<<8)|((unsigned char)(b))) #define PIX32_ARGB(a,r,g,b) ((((a))<<24)|(((r))<<16)|(((g))<<8)|((b))) // a,r,g,bが予め0..255になっていること前提にする. #endif #ifndef PIX32_GET_B /// argb値中の blueの値を取得. #define PIX32_GET_B(argb) ((unsigned char)(argb)) #endif #ifndef PIX32_GET_G /// argb値中の greenの値を取得. #define PIX32_GET_G(argb) ((unsigned char)((argb)>>8)) #endif #ifndef PIX32_GET_R /// argb値中の greenの値を取得. #define PIX32_GET_R(argb) ((unsigned char)((argb)>>16)) #endif #ifndef PIX32_GET_A /// argb値中の alphaの値を取得. #define PIX32_GET_A(argb) (((unsigned)(argb))>>24) #endif /** 整数割りで、縮小. */ static inline void pix32_reducedsizeI(unsigned *dst, unsigned dstW, unsigned dstH, const unsigned *src, unsigned srcW, unsigned srcH) { unsigned mw, mh; unsigned msz; unsigned dx, dy, sxx, syy; if (srcW == 0 || srcH == 0 || dstW == 0 || dstH == 0) return; mw = srcW / dstW; mh = srcH / dstH; if (mw == 0 || mh == 0) return; //printf("[%d*%d->(%d,%d)->%d*%d]\n", srcW,srcH,mw,mh,dstW,dstH); msz = mw * mh; syy = 0; for (dy = 0; dy < dstH; ++dy) { sxx = 0; for (dx = 0; dx < dstW; ++dx) { double fa=0, fr=0, fg=0, fb=0; unsigned x, y; for (y = syy; y < syy+mh; ++y) { for (x = sxx; x < sxx+mw; ++x) { unsigned c = src[y*srcW + x]; fa += PIX32_GET_A(c); fr += PIX32_GET_R(c); fg += PIX32_GET_G(c); fb += PIX32_GET_B(c); } } { unsigned ia,ir,ig,ib; ia = (unsigned)(fa / msz); if (ia > 254) ia = 255; // 誤差で255になるべきものが254になるようなんで、強制して逃げ. ir = (unsigned)(fr / msz); if (ir > 255) ir = 255; ig = (unsigned)(fg / msz); if (ig > 255) ig = 255; ib = (unsigned)(fb / msz); if (ib > 255) ib = 255; dst[dy * dstW + dx] = PIX32_ARGB(ia,ir,ig,ib); } sxx += mw; } syy += mh; } } /** バイリニアサンプリングで拡大縮小. */ static inline void pix32_resize(unsigned *dst, unsigned dstW, unsigned dstH, const unsigned *src, unsigned srcW, unsigned srcH) { double rw, rh; if (srcW == 0 || srcH == 0 || dstW == 0 || dstH == 0) return; rw = (double)srcW / dstW; rh = (double)srcH / dstH; if ((rw >= 1.0) | (rh >= 1.0)) { // 縮小が発生する場合. if ((srcW % dstW) == 0 && (srcH % dstH) == 0) { // 整数割りですむ場合は、それ専用の処理にして、バイリニアによる滲み(誤差)を減らす. pix32_reducedsizeI(dst, dstW, dstH, src, srcW, srcH); return; } { // 縦横縮小. // 縮小が整数倍で行えるように、計算途中は一旦バイリニアサンプリングで拡大. unsigned msz; unsigned dmw,dmh; unsigned dx, dy; unsigned mw = (unsigned)rw + 1; // 何分の一にするか. unsigned mh = (unsigned)rh + 1; #if 1 mw += (mw < 2); mh += (mh < 2); #endif dmw = dstW * mw; // 出力幅の整数倍かつ元サイズよりも大きい値. dmh = dstH * mh; rw = (double)srcW / dmw; rh = (double)srcH / dmh; msz = mw * mh; for (dy = 0; dy < dstH; ++dy) { for (dx = 0; dx < dstW; ++dx) { double fa=0, fr=0, fg=0, fb=0; unsigned dmx, dmy ; // 仮想的に一旦tw*thサイズの画像に拡大して, // その中のmw*mh ピクセルのPIX32_ARGBの平均を出力の1ピクセルにする. for (dmy = dy*mh; dmy < (dy+1)*mh; ++dmy) { for (dmx = dx*mw; dmx < (dx+1)*mw; ++dmx) { double sx = dmx * rw - 0.5; double sy = dmy * rh - 0.5; if (sx < 0) sx = 0; if (sy < 0) sy = 0; { unsigned x0 = (unsigned)sx; unsigned y0 = (unsigned)sy; double kw = sx - x0; double kh = sy - y0; double k0 = (1.0 - kw) * (1.0 - kh); double k1 = kw * (1.0 - kh); double k2 = (1.0 - kw) * kh; double k3 = kw * kh; unsigned ccX0a1lW = (x0+1 < srcW); unsigned ccY0a1lH = (y0+1 < srcH); unsigned c0 = src[y0 * srcW + x0]; unsigned c1 = ccX0a1lW ? src[y0 * srcW + x0 + 1] : c0; unsigned c2 = ccY0a1lH ? src[(y0+1) * srcW + x0] : c0; unsigned c3 =(ccX0a1lW & ccY0a1lH) ? src[(y0+1) * srcW + (x0 + 1)] : ccX0a1lW ? c1 : ccY0a1lH ? c2 : c0; fa += (PIX32_GET_A(c0)*k0 + PIX32_GET_A(c1)*k1 + PIX32_GET_A(c2)*k2 + PIX32_GET_A(c3)*k3); fr += (PIX32_GET_R(c0)*k0 + PIX32_GET_R(c1)*k1 + PIX32_GET_R(c2)*k2 + PIX32_GET_R(c3)*k3); fg += (PIX32_GET_G(c0)*k0 + PIX32_GET_G(c1)*k1 + PIX32_GET_G(c2)*k2 + PIX32_GET_G(c3)*k3); fb += (PIX32_GET_B(c0)*k0 + PIX32_GET_B(c1)*k1 + PIX32_GET_B(c2)*k2 + PIX32_GET_B(c3)*k3); } } } { unsigned ia,ir,ig,ib; ia = (unsigned)(fa / msz); if (ia > 254) ia = 255; // 誤差で255になるべきものが254になるようなんで、強制して逃げ. ir = (unsigned)(fr / msz); if (ir > 255) ir = 255; ig = (unsigned)(fg / msz); if (ig > 255) ig = 255; ib = (unsigned)(fb / msz); if (ib > 255) ib = 255; dst[dy * dstW + dx] = PIX32_ARGB(ia,ir,ig,ib); } } } } } #if 1 else { //拡大処理 ※縮小側の処理で、拡大も問題ないが、分かりやすさと速度?のために、このルーチンは残す. unsigned dx,dy; for (dy = 0; dy < dstH; dy++) { for (dx = 0; dx < dstW; dx++) { double sx = dx * rw - 0.5; double sy = dy * rh - 0.5; if (sx < 0) sx = 0; if (sy < 0) sy = 0; { unsigned x0 = (unsigned)(sx-0.0); unsigned y0 = (unsigned)(sy-0.0); double kw = sx - x0; double kh = sy - y0; double k0 = (1.0 - kw) * (1.0 - kh); double k1 = kw * (1.0 - kh); double k2 = (1.0 - kw) * kh; double k3 = kw * kh; unsigned ccX0a1lW = x0+1 < srcW; unsigned ccY0a1lH = y0+1 < srcH; unsigned c0 = src[y0 * srcW + x0]; unsigned c1 = ccX0a1lW ? src[y0 * srcW + x0 + 1] : c0; unsigned c2 = ccY0a1lH ? src[(y0+1) * srcW + x0] : c0; unsigned c3 = (ccX0a1lW & ccY0a1lH) ? src[(y0+1) * srcW + x0 + 1] : ccX0a1lW ? c1 : ccY0a1lH ? c2 : c0; unsigned ir = (unsigned)( (PIX32_GET_R(c0)*k0 + PIX32_GET_R(c1)*k1 + PIX32_GET_R(c2)*k2 + PIX32_GET_R(c3)*k3) ); unsigned ig = (unsigned)( (PIX32_GET_G(c0)*k0 + PIX32_GET_G(c1)*k1 + PIX32_GET_G(c2)*k2 + PIX32_GET_G(c3)*k3) ); unsigned ib = (unsigned)( (PIX32_GET_B(c0)*k0 + PIX32_GET_B(c1)*k1 + PIX32_GET_B(c2)*k2 + PIX32_GET_B(c3)*k3) ); unsigned ia = (unsigned)( (PIX32_GET_A(c0)*k0 + PIX32_GET_A(c1)*k1 + PIX32_GET_A(c2)*k2 + PIX32_GET_A(c3)*k3) ); if (ia > 254) ia = 255; // 誤差で255になるべきものが254になるようなんで、強制して逃げ. if (ir > 255) ir = 255; if (ig > 255) ig = 255; if (ib > 255) ib = 255; dst[dy * dstW + dx] = PIX32_ARGB(ia,ir,ig,ib); } } } } #endif } #endif