text: /*$
   Copyright (C) 2013-2020 Azel.
  
   This file is part of AzPainter.
  
   AzPainter is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
  
   AzPainter is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
  
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
  $*/
  
  /**************************************
   * DrawData
   * 
   * 選択範囲関連
   **************************************/
  /*
   * tileimgSel : 選択範囲がない時は NULL または空の状態。
   * sel.rcsel  : 選択範囲全体を含む矩形 (おおよその範囲)
   */
  
  #include "mDef.h"
  #include "mRectBox.h"
  #include "mStr.h"
  #include "mUtilFile.h"
  
  #include "defDraw.h"
  
  #include "draw_main.h"
  #include "draw_select.h"
  #include "draw_layer.h"
  #include "draw_op_sub.h"
  
  #include "TileImage.h"
  #include "TileImageDrawInfo.h"
  #include "LayerItem.h"
  #include "ConfigData.h"
  
  #include "MainWinCanvas.h"
  #include "PopupThread.h"
  
  
  //---------------
  
  #define _CURSOR_WAIT    MainWinCanvasArea_setCursor_wait();
  #define _CURSOR_RESTORE MainWinCanvasArea_restoreCursor();
  
  #define _FILENAME_SELCOPY  "selcopy.dat"
  
  //---------------
  
  
  
  //======================
  // sub
  //======================
  
  
  /** キャンバスイメージ全体の範囲を取得 */
  
  static void _get_canvimage_rect(DrawData *p,mRect *rc)
  {
  	rc->x1 = rc->y1 = 0;
  	rc->x2 = p->imgw - 1;
  	rc->y2 = p->imgh - 1;
  }
  
  /** 現在のレイヤにおける選択範囲の描画範囲を取得 */
  
  static void _get_cur_drawrect(DrawData *p,mRect *rc)
  {
  	if(drawOpSub_isFolder_curlayer())
  		//フォルダの場合はイメージ全体
  		_get_canvimage_rect(p, rc);
  	else
  		//カレントレイヤの描画可能範囲
  		TileImage_getCanDrawRect_pixel(p->curlayer->img, rc);
  }
  
  
  //======================
  //
  //======================
  
  
  /** 選択範囲があるか */
  
  mBool drawSel_isHave()
  {
  	return (APP_DRAW->tileimgSel && !mRectIsEmpty(&APP_DRAW->sel.rcsel));
  }
  
  /** 選択範囲解除 */
  
  void drawSel_release(DrawData *p,mBool update)
  {
  	if(p->tileimgSel)
  	{
  		//イメージ削除
  
  		TileImage_free(p->tileimgSel);
  		p->tileimgSel = NULL;
  
  		//更新
  
  		if(update)
  			drawUpdate_rect_canvas_forSelect(p, &p->sel.rcsel);
  	}
  }
  
  
  //======================
  // コマンド
  //======================
  
  
  /** 選択範囲反転 */
  
  void drawSel_inverse(DrawData *p)
  {
  	if(!drawSel_createImage(p)) return;
  
  	_CURSOR_WAIT
  
  	//描画
  
  	TileImageDrawInfo_clearDrawRect();
  
  	TileImage_inverseSelect(p->tileimgSel);
  
  	//透明部分を解放
  
  	drawSel_freeEmpty(p);
  
  	//更新
  
  	drawUpdate_rect_canvas_forSelect(p, &g_tileimage_dinfo.rcdraw);
  
  	_CURSOR_RESTORE
  }
  
  /** すべて選択 */
  
  void drawSel_all(DrawData *p)
  {
  	mRect rc;
  
  	//イメージ再作成
  
  	drawSel_release(p, FALSE);
  
  	if(!drawSel_createImage(p)) return;
  
  	//描画 (キャンバスイメージの範囲)
  
  	_CURSOR_WAIT
  
  	_get_canvimage_rect(p, &rc);
  
  	g_tileimage_dinfo.funcDrawPixel = TileImage_setPixel_new;
  
  	p->w.rgbaDraw.a = 0x8000;
  
  	TileImage_drawFillBox(p->tileimgSel, rc.x1, rc.y1,
  		rc.x2 - rc.x1 + 1, rc.y2 - rc.y1 + 1, &p->w.rgbaDraw);
  
  	//範囲セット
  
  	p->sel.rcsel = rc;
  
  	//更新
  
  	drawUpdate_rect_canvas_forSelect(p, &rc);
  
  	_CURSOR_RESTORE
  }
  
  /** 塗りつぶし/消去 */
  
  void drawSel_fill_erase(DrawData *p,mBool erase)
  {
  	mRect rc;
  
  	if(drawOpSub_canDrawLayer_mes(p)) return;
  
  	/* [!] beginDraw/endDraw でカーソルは変更される */
  
  	//範囲
  
  	drawSel_getFullDrawRect(p, &rc);
  
  	//描画
  
  	drawOpSub_setDrawInfo_fillerase(erase);
  	drawOpSub_beginDraw_single(p);
  
  	TileImage_drawFillBox(p->w.dstimg, rc.x1, rc.y1,
  		rc.x2 - rc.x1 + 1, rc.y2 - rc.y1 + 1, &p->w.rgbaDraw);
  
  	drawOpSub_endDraw_single(p);
  }
  
  /** コピー/切り取り */
  
  void drawSel_copy_cut(DrawData *p,mBool cut)
  {
  	int ret;
  	TileImage *img;
  	mStr str = MSTR_INIT;
  
  	//選択範囲なし
  
  	if(!drawSel_isHave()) return;
  
  	//カレントがフォルダの場合は除く。ロック時はコピーのみ
  
  	ret = drawOpSub_canDrawLayer(p);
  
  	if(ret == CANDRAWLAYER_FOLDER) return;
  
  	if(ret == CANDRAWLAYER_LOCK) cut = FALSE;
  
  	//現在のコピーイメージを削除
  
  	TileImage_free(p->sel.tileimgCopy);
  	p->sel.tileimgCopy = NULL;
  
  	//---------
  
  	_CURSOR_WAIT
  
  	//----- コピー/切り取り
  
  	//切り取り準備
  
  	if(cut)
  	{
  		drawOpSub_setDrawInfo_select_cut();
  		drawOpSub_beginDraw(p);
  	}
  
  	//コピー/切り取り
  
  	img = TileImage_createSelCopyImage(p->curlayer->img, p->tileimgSel,
  		&p->sel.rcsel, cut, FALSE);
  
  	//切り取り後
  
  	if(cut)
  		drawOpSub_finishDraw_single(p);
  
  	//---- イメージ保持
  	/* デフォルトで、作業用ディレクトリにファイル保存。
  	 * 作業用ディレクトリがない or 保存に失敗した場合は sel.tileimgCopy に保持。 */
  
  	if(img)
  	{
  		if(ConfigData_getTempPath(&str, _FILENAME_SELCOPY)
  			&& TileImage_saveTileImageFile(img, str.buf, p->curlayer->col))
  		{
  			//ファイル出力に成功した場合、イメージ削除
  
  			TileImage_free(img);
  		}
  		else
  		{
  			//ファイル出力できない場合、メモリ上に保持
  
  			p->sel.tileimgCopy = img;
  			p->sel.col_copyimg = p->curlayer->col;
  		}
  
  		mStrFree(&str);
  	}
  
  	//
  
  	_CURSOR_RESTORE
  }
  
  /** 新規レイヤに貼り付け */
  
  void drawSel_paste_newlayer(DrawData *p)
  {
  	mStr str = MSTR_INIT;
  	TileImage *img;
  	uint32_t col;
  
  	//コピーイメージが存在しない
  
  	if(!p->sel.tileimgCopy
  		&& !(ConfigData_getTempPath(&str, _FILENAME_SELCOPY) && mIsFileExist(str.buf, FALSE)))
  	{
  		mStrFree(&str);
  		return;
  	}
  
  	//レイヤのイメージ用意 (ファイルの場合、読み込み)
  
  	if(p->sel.tileimgCopy)
  	{
  		img = TileImage_newClone(p->sel.tileimgCopy);
  		col = p->sel.col_copyimg;
  	}
  	else
  		img = TileImage_loadTileImageFile(str.buf, &col);
  
  	mStrFree(&str);
  
  	if(!img) return;
  
  	//新規レイヤ
  
  	if(!drawLayer_newLayer_fromImage(p, img, col))
  		TileImage_free(img);
  }
  
  
  /** [スレッド] 拡張/縮小 */
  
  static int _thread_expand(mPopupProgress *prog,void *data)
  {
  	TileImage_expandSelect(APP_DRAW->tileimgSel, *((int *)data), prog);
  
  	return 1;
  }
  
  /** 範囲の拡張/縮小 */
  
  void drawSel_expand(DrawData *p,int cnt)
  {
  	//準備
  
  	g_tileimage_dinfo.funcColor = TileImage_colfunc_overwrite;
  
  	TileImageDrawInfo_clearDrawRect();
  
  	//スレッド
  
  	PopupThread_run(&cnt, _thread_expand);
  
  	//範囲
  
  	if(cnt > 0)
  		p->sel.rcsel = g_tileimage_dinfo.rcdraw;
  	else
  		drawSel_freeEmpty(p);
  
  	//更新
  
  	drawUpdate_rect_canvas_forSelect(p, &g_tileimage_dinfo.rcdraw);
  }
  
  
  //======================
  // 作業用処理
  //======================
  
  
  /** イメージ全体に対して描画する際の範囲取得 */
  
  void drawSel_getFullDrawRect(DrawData *p,mRect *rc)
  {
  	if(drawSel_isHave())
  		*rc = p->sel.rcsel;
  	else
  		_get_cur_drawrect(p, rc);
  }
  
  /** 選択範囲イメージを確保 (範囲セット前に行う)
   *
   * @return FALSE で失敗 */
  
  mBool drawSel_createImage(DrawData *p)
  {
  	mRect rc;
  
  	if(p->tileimgSel)
  		return TRUE;
  	else
  	{
  		//描画可能範囲から作成
  		
  		_get_cur_drawrect(p, &rc);
  
  		p->tileimgSel = TileImage_newFromRect(TILEIMAGE_COLTYPE_ALPHA1, &rc);
  
  		mRectEmpty(&p->sel.rcsel);
  
  		return (p->tileimgSel != NULL);
  	}
  }
  
  /** 範囲削除後の処理
   *
   * 透明タイルを解放して範囲を再計算。
   * すべて透明ならイメージ削除。 */
  
  void drawSel_freeEmpty(DrawData *p)
  {
  	if(p->tileimgSel)
  	{
  		if(TileImage_freeEmptyTiles(p->tileimgSel))
  		{
  			//すべて透明なら削除
  			
  			TileImage_free(p->tileimgSel);
  			p->tileimgSel = NULL;
  		}
  		else
  			//点が残っていれば、範囲を再計算
  			TileImage_getHaveImageRect_pixel(p->tileimgSel, &p->sel.rcsel, NULL);
  	}
  }
  
  