1/4ずつの円弧とブレゼンハムを組み合わせて角丸長方形(rounded rectangle)を描画する。
前回の円描画であえて1/4ずつ描画できるように作ったので、それをブレゼンハムと組み合わせる。
#pragma once #include <vector> struct Image { std::vector<unsigned char> img; int width; int height; }; void resize(Image* img, int w, int h) { img->width = w; img->height = h; img->img.resize(w * h); } //! @brief 画像のピクセルを着色する //! @param [in,out] img 画像 //! @param [in] x 座標 //! @param [in] y 座標 //! @return なし void plot(Image& img, int x, int y) { img.img[y * img.width + x] = 255; } enum class CPOS { left, right, top, bottom };
//! @brief 円の指定した1/4を描画する //! @param [out] img 画像 //! @param [in] cx 中心座標 //! @param [in] cy 中心座標 //! @param [in] r 半径 //! @param [in] bx 円の四分割したエリアの指定 //! @param [in] by 円の四分割したエリアの指定 //! @return なし void arc4(Image& img, const int cx, const int cy, const int r, const CPOS bx, const CPOS by) { int x, y;//描画位置 const int rr = r * r;//半径の二乗 int dx, dy; int limx; int limy; if (bx == CPOS::right && by == CPOS::bottom) { // □□ // □■ //初期位置 x = r; y = 0; dx = -1; dy = +1; limx = 0; limy = r; } else if (bx == CPOS::left && by == CPOS::bottom) { // □□ // ■□ x = 0; y = r; dx = -1; dy = -1; limx = -r; limy = 0; } else if (bx == CPOS::left && by == CPOS::top) { // ■□ // □□ x = -r; y = 0; dx = +1; dy = -1; limx = 0; limy = -r; } else if (bx == CPOS::right && by == CPOS::top) { // □■ // □□ x = 0; y = -r; dx = +1; dy = +1; limx = r; limy = 0; } //最初の一点 //plot(img,x + cx, y + cy); while ((x != limx) || (y != limy)) { ///////////////////////// // 次に描画するポイントを決定 //次に描画するのは以下のいずれか。 int xy1 = pow(x + dx, 2) + pow(y, 2); int xy2 = pow(x + dx, 2) + pow(y + dy, 2); int xy3 = pow(x, 2) + pow(y + dy, 2); // x*x + y*y == r*rなので // 上記各xyのうち最もr*rとの差が少ないx*x+y*yを選択 xy1 = abs(xy1 - rr); xy2 = abs(xy2 - rr); xy3 = abs(xy3 - rr); if (xy1 < xy2) { if (xy1 < xy3) { // xy1 x = x + dx; y = y; } else { // xy2 x = x + dx; y = y + dy; } } else { if (xy2 < xy3) { // xy2 x = x + dx; y = y + dy; } else { // xy3 x = x; y = y + dy; } } ///////////////////////// // 描画 if ( (img, x + cx < 0) || (img, x + cx >= img.width) || (img, y + cy < 0) || (img, y + cy >= img.height) ) { continue; } plot(img, x + cx, y + cy); } }
//! @brief ブレゼンハムの直線描画 //! @param [out] img 画像データ(一次元配列)へのポインタ //! @param [in] width 画像の幅(画素数) //! @param [in] height 画像の高さ(画素数) //! @param [in] sx 線の始点X //! @param [in] sy 線の始点Y //! @param [in] ex 線の終点X //! @param [in] ey 線の終点Y //! @param [in] color 線の色 void Bresenham( Image& img, const int sx, const int sy, const int ex, const int ey ) { // https://ja.wikipedia.org/wiki/%E3%83%96%E3%83%AC%E3%82%BC%E3%83%B3%E3%83%8F%E3%83%A0%E3%81%AE%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0 const int dx = std::abs(ex - sx); const int dy = std::abs(ey - sy); const int nx = (sx < ex) ? 1 : -1; const int ny = (sy < ey) ? 1 : -1; int err = dx - dy; int x = sx; int y = sy; while (1) { plot(img, x, y); if (x == ex && y == ey) break; const int e2 = 2 * err; if (e2 > -dy) { err = err - dy; x += nx; } if (e2 < dx) { err = err + dx; y += ny; } } }
//! @brief 角丸長方形を描画 //! @param [out] img 画像 //! @param [in] sx 長方形始点x //! @param [in] sy 長方形始点y //! @param [in] ex 長方形終点x //! @param [in] ey 長方形終点y //! @return なし void rounded_rectangle( Image& img, const int sx, const int sy, const int ex, const int ey, const int r) { Bresenham(img, sx + r, sy , ex - r, sy ); Bresenham(img, sx + r, ey , ex - r, ey ); Bresenham(img, sx , sy + r, sx , ey - r); Bresenham(img, ex , sy + r, ex , ey - r); arc4(img, sx + r, sy + r, r, CPOS::left, CPOS::top); arc4(img, sx + r, ey - r, r, CPOS::left, CPOS::bottom); arc4(img, ex - r, sy + r, r, CPOS::right, CPOS::top); arc4(img, ex - r, ey - r, r, CPOS::right, CPOS::bottom); }
#pragma warning(disable:4996) #include <iostream> #include "draw.hpp" //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] vmax 全てのRGBの中の最大値 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む void pnmP3_Write(const char* const fname, const int width, const int height, const unsigned char* const p) { // PPM ASCII FILE* fp = fopen(fname, "wb"); fprintf(fp, "P3\n%d %d\n%d\n", width, height, 255); size_t k = 0; for (size_t i = 0; i < (size_t)height; i++) { for (size_t j = 0; j < (size_t)width; j++) { fprintf(fp, "%d %d %d ", p[k], p[k], p[k]); k++; } fprintf(fp, "\n"); } fclose(fp); }
int main() { Image img; resize(&img, 250, 200); // 中心 int cx = 250; int cy = 250; //半径 int r = 50; rounded_rectangle(img, 50, // sx 50, // sy 200,// ex 150,// ey 20 // r ); pnmP3_Write("a.pgm", img.width, img.height, img.img.data()); }