画像関連モジュール

HSPで画像ファイルを操作したり画像加工したりするモジュールです。
画像ファイル&画像加工に関してHSPでは標準命令だけでもかなりサポートしています。 また、Artlet2D(a2d.hsp)やhspcv(hspcv.as・hspcv.dll)などの拡張機能も用意されています。 このモジュールでは、そんな画像関連がもうちょこっとだけ便利に扱えることを目指しています。
一部機能でGDI+(GDIPlus)を使用します
GDI+は(おそらく)WindowsAPIの一種で、WindowsXPからは標準で含まれていますがそれ以前のOSでは別途入手する必要があります。 HSPのパッケージに含まれているArtlet2Dは、この難解なGDI+をHSPから簡単に利用できるように設計されたすごいモジュールです!

モジュールの機能

モジュールの使い方

モジュールをスクリプトにコピペするだけで以下の命令・関数を使用できます。
命令・関数(詳細はモジュール内コメントを参照)備考
画像ファイル操作2号
val = ImgF_GetFormat(Path)
画像ファイルを解析して、そのファイルの記録形式を返します。memfile命令対応。
val
0 (不明)
1 (BITMAP)
2 (JPEG)
3 (GIF)
4 (PNG)
ImgF_GetPicSize Path, SizeX, SizeY
画像ファイルを解析して、その画像を描画した時の画面サイズを取得します。memfile命令対応。
分析可能な形式
BMP,JPG,GIF,PNG
ImgF_PicloadEx Path, Mode, Option, WinID
GDI+を使用して画像ファイルをロードします。memfile命令対応。
picload命令のすごい版として製作しましたがHSP3.31でpicload命令が強化されましたv^ ^。
この命令では透過GIFやアルファブレンドPNGを、gmode 7で使用できるように画像とマスクに分けてのロードが可能です。
ImgF_jpgsave Path, Quality
bmpsave命令みたいなものです。GDI+を使用してJPG形式でファイル保存します。品質指定付き。
Quality 品質
0 (最低・ボロボロ)
100 (最高・すべすべ)
画像加工と描画
ImgP_CalcFitSize Res_W, Res_H, PicW, PicH, RectW, RectH
縦横比固定で画像を拡縮する時に指定領域に収まる最大サイズを算出します。
ImgP_gzoom SizeX, SizeY, TrimWinID, TrimX, TrimY, TrimW, TrimH
画像を変倍コピーするgzoom命令のGDI+版です。少しキレイ(〃▽〃)に拡大できるかもです。
ImgP_grotate TrimWinID, TrimX, TrimY, Angle, Width, Height
画像を回転コピーするgrotate命令のGDI+版です。確実にキレイ(〃▽〃)に描画できます。
ImgP_RotateFlip Mode, TrimWinID, TrimX, TrimY, TrimW, TrimH
GDI+を使って画像をひっくり返したり裏返したり直角に倒したりします。
Mode
0 ( 0°回転)
1 ( 90°回転)
2 (180°回転)
3 (270°回転)
4 ( 0°回転+左右反転)
5 ( 90°回転+左右反転)
6 (180°回転+左右反転)
7 (270°回転+左右反転)
ImgP_FilterPastel Lumin, Width, Height
画面ピクセルの輝度を変化させます。各ピクセルは、まっくろからまっしろの間で濃度が変化します。
ImgP_FilterVivid Lumin, Width, Height
画面ピクセルの輝度を変化させます。各ピクセルは、色を強調するような感じに変化します。
Lumin
-256〜256 (変化度合)
※ 0 (基準、変化なし)
ImgP_FilterNega Width, Height
現在の画面に対してカレントカラーとのXOR演算を行います。これによりネガ反転などの効果を得ることができます。
ImgP_GcopySubAbs DiffWinID, PosX, PosY, Width, Height
ふたつの画像の差をとりその絶対値を視覚的に描画します。簡単に言うと、間違い探しの瞬殺が可能です。
ImgP_Memsave Res_Bin, Format, Width, Height, Option
GDI+を使用して画面イメージを画像ファイルとして保存しますが、保存先はメモリ(変数)上になります。
Format 保存形式
1 (BMP)
2 (JPG)
3 (GIF)
4 (PNG)
ImgP_LoopCopy Width, Height, WinID, PosX, PosY, SizeW, SizeH, OffsetX, OffsetY
画像をタイルのように敷き詰めるお任せ命令です。
ImgP_PairCopy Width, Height, WinID1, PosX1, PosY1, WinID2, PosX2, PosY2, OffsetX, OffsetY
ふたつの画像を左右に並べるお任せ命令です。
ImgP_Exchange WinID, PosX, PosY, SizeW, SizeH
ふたつの画像を交換するお任せ命令です。

モジュール

このモジュールのみで動作します。DLLやモジュールを別途用意する必要はありませんがWindowsAPI(Win32API)とGDI+を使用しますので環境に依存します。
ヘルプ書式を内包していますのでcommonフォルダに保存(標準ファイル名 ImageModule2.hsp)するとHDLでヘルプを読むことができます。
/*=======================================================================================================
                                                                    画像ファイルにいろいろするモジュール
HSP3.22         2010.12.30  新規製作
                2011. 1. 4  追加:ImgM_GetSize JPG-$C4処理
                        12  追加:ImgM_SetImageData (暫定)
                        16  修正:Jpeg Endマーカー無し対策
                            製作:ImgF_GdipPicload , ImgM_GdipPicload , ImgM_GdipGzoom
                            移植:ImgM_CalcFitSize
                        27  修正:Jpeg MarkerSize不正(NikonD700バグ?)対策
HSP3.3β1             2.16  修正:Jpeg 破損画像 対策
HSP3.22(笑)           4. 7  収穫。
                      5. 1  修正:ImgM_CalcFitSize 0割り
HSP3.3β3             7.12  製作:ImgM_GdipJpgsave
HSP3.3RC1(正式でてるのに)   9.15    製作:ImgM_GdipRotateFlip
                     11. 3  上記モジュール全て破棄(Next Stage へ)

                                                    画像ファイル操作モジュール2号 & 画像加工モジュール
HSP3.3          2011.11. 3  [製作]ImgF_PicloadEx
                     12.20  [製作]ImgF_GetPicSize,ImgF_GetFormat,ImgP_CalcFitSize,ImgP_gzoom
                2012. 1. 9  [移植](コピペ)ImgF_jpgsave,ImgP_RotateFlip
                      1.11  体裁、モジュール化
HSP3.4β4       2014. 6. 7  ふたつのモジュールを統合、HDL対応
                〜    6.28  [廃止](内部関数)_ImgF_LoadAndSigCheck ⇒ ImgF_GetFormatに統合
                            [製作](内部関数)(実験機構)ImgM_CreateH , ImgM_CloseH
                            [編集]ImgF_PicloadEx 今さらMode2、やらないって言ってたアルファ取得
                            [編集]ImgP_gzoom 実験中に放置してたらしいorz
                            [製作]ImgP_Memsave , ImgP_FilterPastel/Vivid/Nega/SubAbs , ImgP_grotate
HSP3.4+3.5β1   2015. 4.26  [編集]ImgF_PicloadEx ⇒ hsp3cランタイム対応、実験機構の排除1
                        28  [制作]ImgP_PairCopy, ImgP_LoopCopy
                            [編集]ImgP_Memsave ⇒ hsp3cランタイム対応、実験機構の排除2
HSP3.4+3.5β1+β2     6.16  [製作]ImgP_Exchange, ImgP_FilterBrush
(↑このアプデの仕方は非推奨)[編集]ImgP_FilterNega(マクロ化), ImgP_FilterSubAbs ⇒ ImgP_GcopySubAbs(変更)
                      9. 8  [廃止]ImgM_CreateH , ImgM_CloseH ⇒ 実験機構の排除3(完了)
                            [編集]ImgF_jpgsave,ImgP_gzoom,ImgP_RotateFlip,ImgP_GcopySubAbs,ImgP_grotate
%--------------------------------------------------------------------------------------------------------
%dll        ;                   HDL(HSP Document Library)対応ファイル。commonに放り込むだけで対応します。
和謹製モジュール
%port       ;   DLLやモジュールを別途用意する必要はありませんがWin32APIを使用しますので環境に依存します。
Win
%author     ;                                       Copyright (C) 2010-2015 衣日和 All rights reserved.
衣日和
%url        ;                                   最新版はこちらから。なんかてきとーWEB Site『略して仮。』
http://www.tvg.ne.jp/menyukko/
%note       ;                                                                           標準ファイル名
ImageModule2.hsp をインクルードする。
WinXP以降の環境はGDI+を標準装備してます。
; com を使用します。...使いたくないです、いずれ排除したいです。...排除しました。
%======================================================================================================*/

#ifndef ImageModule2Included        ; 多重インクルード対策
#define ImageModule2Included        ; ←実は不要だったりする(モジュール名で#ifdefできる)
#module ImageModule2

#uselib "gdiplus"   ; ◆◆ ハローワールドの中心でふぁっきゅーと叫びたいorz ------------------------------

; ◆ Gdiplusの基礎
#func GdiplusStartup    "GdiplusStartup"    var, var, nullptr
                                            ; [Rtn Token(LoadHandle?)][OpenPrm Struct][Output Struct]
#func GdiplusShutdown   "GdiplusShutdown"   int                 ; [Token]

; ◆ ImageとBitmapとその周辺と
;   ◇画像ファイルから画像取得
#func GdipLoadImageFromFile         "GdipLoadImageFromFile"     wstr, var   ; 無用[FileName][Rtn Image]
#func GdipLoadImageFromStream       "GdipLoadImageFromStream"   int, var    ; [Stream][Rtn Image]
            ; ストリームから入力することで、メモリ上のバイナリデータからの読み込みもできる
            ; [Stream]は comobj でもいいんだけど、いろいろ都合が悪い。

;   ◇画像をファイル出力
#func GdipSaveImageToFile           "GdipSaveImageToFile"   int, wstr, var, var
            ; [Param]不要なら var より nullptr がいい       ; [Image][FileName][DataFormat][Param]
#func GdipSaveImageToStream         "GdipSaveImageToStream" int, int, var, var
            ; [stream](変数・メモリ上)に保存                ; [Image][Stream][DataFormat][Param]

;   ◇[Bitmap]を製作
#func GdipCreateBitmapFromGdiDib    "GdipCreateBitmapFromGdiDib" int, int, var  ; from HSP Window
                                            ; [BitmapInfo(bmscr.6)][BitmapData(bmscr.5)][Rtn Bitmap]
#func GdipCloneBitmapAreaI  "GdipCloneBitmapAreaI"  int, int, int, int, int, int, var   ; from Bitmap
                                            ; [x][y][Width][Height][PixelFormat][Bitmap][Rtn NewBitmap]

;   ◇描画サイズ取得
#func GdipGetImageWidth             "GdipGetImageWidth"     int, var    ; 横幅[Image][Rtn Width]
#func GdipGetImageHeight            "GdipGetImageHeight"    int, var    ; 縦幅[Image][Rtn Height]

;   ◇90°回転やミラー反転など
#func GdipImageRotateFlip           "GdipImageRotateFlip"   int, int    ; [Image][FlipType]
            ;   [FlipType]
            ; 0 = RotateNoneFlipNone , Rotate180FlipXY      ; 4 = RotateNoneFlipX , Rotate180FlipY
            ; 1 = Rotate90FlipNone   , Rotate270FlipXY      ; 5 = Rotate90FlipX   , Rotate270FlipY
            ; 2 = Rotate180FlipNone  , RotateNoneFlipXY     ; 6 = Rotate180FlipX  , RotateNoneFlipY
            ; 3 = Rotate270FlipNone  , Rotate90FlipXY       ; 7 = Rotate270FlipX  , Rotate90FlipY

;   ◇[Image]を破棄する
#func GdipDisposeImage  "GdipDisposeImage"  int

; ◆ ImageAttributesとゆかいな仲間たち
#func GdipCreateImageAttributes     "GdipCreateImageAttributes"     var     ; [Rtn ImageAttr]
#func GdipDisposeImageAttributes    "GdipDisposeImageAttributes"    int
#func GdipSetImageAttributesColorMatrix "GdipSetImageAttributesColorMatrix" \
                                                                    int, int, int, var, nullptr, nullptr
    ; 色マトリ [ImageAttr][ColorAdjustType][TRUE=SetCMat/FALSE=ClearCMat][ColorMat][GrayMat][GrayFlag]

; ◆ Matrixのジャケにさり気なく映り込む黒幕w
#func GdipCreateMatrix      "GdipCreateMatrix"      var                     ; 作る
#func GdipDeleteMatrix      "GdipDeleteMatrix"      int                     ; 捨てる
#func GdipTranslateMatrix   "GdipTranslateMatrix"   int, float, float, int  ; [Matrix][OffsetX][Y][Order]
#func GdipRotateMatrix      "GdipRotateMatrix"      int, float, int         ; [Matrix][angle][Order]

; ◆ Graphicsが行く
#func GdipCreateFromHDC     "GdipCreateFromHDC"     int, var    ; hdcからGraphics製造
#func GdipDeleteGraphics    "GdipDeleteGraphics"    int         ; Graphics破棄
#func GdipSetWorldTransform "GdipSetWorldTransform" int, int    ; GraphicsにMatrix適用 [Graph][Matr]

; ◆ ImageとGraphicsの描画関係(かなりどろどろ)
#func GdipDrawImageI            "GdipDrawImageI"        int, int, int, int  ; [Graphics][Image][x][y]
#func GdipDrawImageRectI        "GdipDrawImageRectI"    int, int, int, int, int, int
                                                        ; [Graphics][Image][x][y][Width][Height]
#func GdipDrawImageRectRectI    "GdipDrawImageRectRectI" \
                        int, int, int, int, int, int, int, int, int, int, int, int, nullptr, nullptr
    ; GraphicsにImageを転写(拡縮付き)
                    ; [Graphics][Image][Paste x][y][Width][Height][base x][y][Width][Height]
                    ;                               [UnitPixel][ImageAttributes][Callback][CallbackData]

; ◆ 定数など
; GdipCloneBitmapAreaI [PixelFormat]
#const PixelFormatIndexed           $00010000   ; Indexes into a palette
#const PixelFormatGDI               $00020000   ; Is a GDI-supported format
#const PixelFormatAlpha             $00040000   ; Has an alpha component
#const PixelFormatCanonical         $00200000 
#const PixelFormat8bppIndexed        3|( 8<<8)|PixelFormatGDI|PixelFormatIndexed
#const PixelFormat24bppRGB           8|(24<<8)|PixelFormatGDI
#const PixelFormat32bppARGB         10|(32<<8)|PixelFormatGDI|PixelFormatAlpha|PixelFormatCanonical
; GdipSetImageAttributesColorMatrix [enum ColorAdjustType]
    ; ColorAdjustTypeDefault, ColorAdjustTypeBitmap, ColorAdjustTypeBrush, ColorAdjustTypePen,
    ; ColorAdjustTypeText, ColorAdjustTypeCount, ColorAdjustTypeAny/*Reserved*/

#uselib "kernel32.dll"  ; ◆◆ カーネル -----------------------------------------------------------------

#func GlobalAlloc   "GlobalAlloc"   int, int
#func GlobalFree    "GlobalFree"    int
#func GlobalLock    "GlobalLock"    int
#func GlobalUnlock  "GlobalUnlock"  int
#func GlobalSize    "GlobalSize"    int
#define GMEM_MOVEABLE      2
#define GMEM_ZEROINIT     64
#define GMEM_SHARE      8192
#define GHND              66

#uselib "Ole32.dll"     ; ◆◆ おぇ ---------------------------------------------------------------------

#func CreateStreamOnHGlobal "CreateStreamOnHGlobal" int, int, var   ; ◆Create Stream With GlobalMemory
        ; [GlobalHandle]グローバルメモリのハンドル(0にするとストリームが内部で用意してくれる)
        ; [DelOnRelease]1にするとストリームの解放時にグローバルメモリも削除してくれる...らしい
        ; [Rtn hStream] 出来あがったストリームのハンドルを受け取る変数
#func GetHGlobalFromStream  "GetHGlobalFromStream"  int, var    ; ◆[Stream]の持つグローバルメモリを拝借
#func ReleaseStgMedium      "ReleaseStgMedium"      var         ; ◆[Stream]やgdi系いろいろハンドルを解放
        ; 参考サイト:   むしゃぺらり(http://www.setsuki.com/) -> HSP ページ -> gdiplus_test.hsp

#uselib "gdi32.dll"     ; ◆◆ gdi ----------------------------------------------------------------------

#func BitBlt                    "BitBlt"                    int, int, int, int, int, int, int, int, int
#func SelectObject              "SelectObject"              int, int
#func CreateCompatibleBitmap    "CreateCompatibleBitmap"    int, int, int
#func DeleteObject              "DeleteObject"              int
#func CreateCompatibleDC        "CreateCompatibleDC"        int
#func DeleteDC                  "DeleteDC"                  int
; BitBltのモード(ラスタオペレーションって実はいっぱいある)
#define SRCPAINT    $00EE0086       ; コピー元とコピー先のor
#define SRCCOPY     $00CC0020       ; 単純コピー
#define PATINVERT   $005A0049       ; ブラシとの排他的論理和
#define DSTINVERT   $00550009       ; ネガポジ反転

; bmscr構造体(32bit)    mref bmscr,67(カレ窓) or mref bmscr,96+(昔、上限あったけど?)
;   ginfoなどの取得関数がある場合はそちらを使った方が適切。
;   (18)WinID  (19)redraw  (27/28)描画位置座標
;   (33/34)gmodeのサイズ  (35)gmodeのモード  (65)gmodeのアルファ

; ハンドルを一括管理する実験は失敗に終わった。gdi+とstream以外は直接管理に戻す。
;   [変数]ImghGdip,ImghStream はそれぞれ 0:ハンドル的なもの 1:呼び出しカウント ←変数別けるべきか?
#deffunc ImgM_OpenGdiplus
    ImghGdip(1) ++
    if ImghGdip(1) == 1 {
        ImghGdip(2) = 1, 0, 0, 0
        GdiplusStartup ImghGdip, ImghGdip(2)
    }
    return
#deffunc ImgM_CloseGdiplus      ; onexitをつけるべきか?
    ImghGdip(1) --
    if ImghGdip(1) == 0  : GdiplusShutdown ImghGdip
    return

#deffunc ImgM_CreateStream int h
    ImghStream(1) ++                ; 未初期化変数対策も兼ねてカウントアップする
    if ImghStream(1) == 1 {         ; 多重してはならない
        if h == 0  : CreateStreamOnHGlobal 0, 1, ImghStream     ; GM指定なし
        if h != 0  : CreateStreamOnHGlobal h, 0, ImghStream     ; GM指定あり
    }
    return
#deffunc ImgM_ReleaseStream     ; onexitをつけるべきか?
    ImghStream(1) --
    if ImghStream(1) == 0  : ReleaseStgMedium ImghStream
    return
            ; newcom bb, , -1, stream  : delcom bb
            ;   ストリームの破棄はコム化してからそのコムを破棄するという処理。
            ;   GMを開放したら一緒にストリームも破棄されるっていうのはただの都市伝説orz
            ; コンパクト版ランタイムではコムが使えないので ●脱コム宣言(笑)●

/*=======================================================================================================
%index                                                                          ; 旧 "ImageFileModule"
%group
画像関連モジュール(ファイル操作2号)
%--------------------------------------------------------------------------------------------------------
%index
ImgF_GetFormat
画像ファイル解析(画像フォーマット)
%prm
(Path)
Path [文字]ファイル名(パス)
%inst
Pathで指定したファイルを分析してその記録形式を返します。またmemfile命令による擬似ファイルにも対応しています。
この関数の戻り値は以下のいずれかです。
    0 : 不明なフォーマット
    1 : BITMAP
    2 : JPEG
    3 : GIF
    4 : PNG
%------------------------------------------------------------------------------------------------------*/
#defcfunc ImgF_GetFormat str n
    ; この関数は副産物として strsize にファイルサイズを返す
    ; モジュール内変数の sb にファイルデータ全部を取得する(ImgF_GetPicSizeで使うため)
    exist n  : if strsize < 0  : return 0               ; ファイルないよ
    sb = "(C)衣日和"  : memexpand sb, strsize           ; sbにファイルデータを全ロード
    bload n, sb, strsize
    if wpeek(sb, 0) == $4D42        : return 1          ; BITMAP    $42,$4D = "BM"
    if wpeek(sb, 0) == $D8FF        : return 2          ; JPEG      $FF,$D8 = "??"
    if lpeek(sb, 0) == $38464947    : return 3          ; GIF       $47,$49,$46,$38 = "GIF8"
    if lpeek(sb, 0) == $474E5089 & lpeek(sb, 4) == $0A1A0A0D  : return 4
                                            ; PNG   $89,$50,$4E,$47,$0D,$0A,$1A,$0A = "?PNG????"
    return 0    ; それ以外の何か

/*-------------------------------------------------------------------------------------------------------
%index
ImgF_GetPicSize
画像ファイル解析(画像の大きさ)
%prm
Path, SizeX, SizeY
Path  [文字]ファイル名(パス)
SizeX [変数]画像幅(X)が代入される変数(int)
SizeY [変数]画像高(Y)が代入される変数(int)
%inst
Pathで指定したファイルを分析してその画像をロードした時のイメージサイズを取得します。またmemfile命令による擬似ファイルにも対応しています。
分析可能な形式はBMP,JPG,GIF,PNGのいずれかで、命令実行後のシステム変数statにはファイル形式を示す値が代入されます。
%------------------------------------------------------------------------------------------------------*/
#deffunc ImgF_GetPicSize str n, var x, var y
    x = 0  : y = 0                      ; とりあえずぬるぽ。…じゃなくて0を代入
    ib = ImgF_GetFormat(n)              ; フォーマットタイプ
    ib(1) = strsize                     ; データサイズ

    if ib == 1 {                        ; ビットマップフォーマット
        if lpeek(sb, 14) == 40  : x = lpeek(sb, 18)  : y =lpeek(sb, 22)     ; Windows形式
    }

    if ib == 2 {                        ; JPEGフォーマット
        ib(2) = 2                           ; offset
        repeat
            if ib(1) <= ib(2)  : break          ; アプリクラッシャー(破損ファイル)対策
            if peek(sb, ib(2)) != $FF {         ; Nikonクラッシャー対策
                ib(2) ++                            ; nicoD700nicoのバグ(?)でアプリが堕ちたょ...回避策
                continue                            ; としてMarkerらしきところまでスキップ
            }

            ib(3) = peek(sb, ib(2) + 1)
            if ib(3) == $D9  : break            ; ファイル終了のお知らせ
            if ib(3) == $C0 | ib(3) == $C2 {
                                        ; 目的地発見 (ハフマンのベースラインかプログレッシブのMarker)
                ib(4) = wpeek(sb, ib(2) + 7), wpeek(sb, ib(2) + 5)  ; トラップ発動(高さが先だったorz)
                x = (ib(4) >> 8 & $00FF) | (ib(4) << 8 & $FF00)
                y = (ib(5) >> 8 & $00FF) | (ib(5) << 8 & $FF00)
                break
            }
            ib(4) = wpeek(sb, ib(2) + 2)        ; それ以外の何かの場合
            ib(5) = (ib(4) >> 8 & $00FF) | (ib(4) << 8 & $FF00)
            ib(2) += ib(5) + 2
        loop
    }

    if ib == 3  : x = wpeek(sb, 6)  : y = wpeek(sb, 8)  ; GIFフォーマット

    if ib == 4 {                        ; PNGフォーマット
        if lpeek(sb, 12) == $52444849 {     ; $49,$48,$44,$52 = "IHDR"  ⇒IHDRヘッダーであってしかるべき
            ib(2) = lpeek(sb, 16), lpeek(sb, 20)    ; ビッグエンディアンだなんてorz...
            x = (ib(2)>>24&$FF) | (ib(2)>>8&$FF00) | (ib(2)<<8&$FF0000) | (ib(2)<<24&$FF000000)
            y = (ib(3)>>24&$FF) | (ib(3)>>8&$FF00) | (ib(3)<<8&$FF0000) | (ib(3)<<24&$FF000000)
        }
    }
    return ib

/*-------------------------------------------------------------------------------------------------------
%index
ImgF_PicloadEx
画像ファイルをロード(GDI+)
%prm
Path, Mode, Option, WinID
Path   [文字]ファイル名(パス)
Mode   [定数]画像ロードモード
    0 : ウィンドウ初期化(白)
    1 : ウィンドウの初期化はしない
    2 : ウィンドウ初期化(黒)
Option [定数]ビットフラグ
    %**00 =  0 : 描画先Window標準動作
    %**01 =  1 : WinIDをbufferで初期化
    %**10 =  2 : WinIDをscreenで初期化
    %**11 =  3 : WinIDをbgscrで初期化
    %00** =  0 : 透過情報を適用(標準)
    %01** =  4 : 透過情報は無視する
    %10** =  8 : 透過情報のみを描画
    %11** = 12 : gmode 7 で使える形式
WinID  [数値]OptionでWindow初期化指定時に利用
     0以上 : 指定IDのウィンドウを初期化
    -1以下 : 未使用ウィンドウを初期化
%inst
HSP標準のpicload命令をGDI+を使って再現します。ロードできるファイル形式はBMP,JPG,GIF,PNGなどGDI+で読み込める必要があります。またmemfile命令による擬似ファイルにも対応しています。
PathとModeはpicload命令と同等ですがOptionを指定することで拡張ロードを実行できます。
Optionでウィンドウの初期化を指定することで、picload前のひと手間(screenやgsel)を省略可能です。ただしModeが1の時はこの設定は適用されません。
OptionでPNGやGIFファイルの持つ透過ピクセル/アルファブレンドの扱いも指定可能です。
%------------------------------------------------------------------------------------------------------*/
#deffunc ImgF_PicloadEx str s, int f, int m, int w
    ; 本家がPNGに正式対応したため存在意義なくなった。でもmemfile(png時)の拡張子省けるよ viiV <kanikani
    exist s  : if strsize == -1  : return
    ib = strsize, 0, 0, 0, 0, 0, 0, 0
            ; ファイルサイズ, 画像サイズW, 画像サイズH, hGlobal, hStream, hImage, hGraphics, hImageAttr
    ImgM_OpenGdiplus

    GlobalAlloc GMEM_ZEROINIT | GMEM_SHARE, ib  : ib(3) = stat  ; hGlobal
    GlobalLock ib(3)                                            ; GMを固定する
    dupptr bb, stat, ib, 2                                      ; 固定したGMに変数名を割り当てる
    bload s, bb, ib, 0                                          ; その変数にファイルの内容を流し込む
    GlobalUnlock ib(3)                                          ; 固定解除

    ImgM_CreateStream ib(3)                                     ; GMからStreamを作る ImghStream参照
    GdipLoadImageFromStream ImghStream,ib(5)                    ; StreamからImage

    GdipGetImageWidth  ib(5), ib(1)                         ; image.横
    GdipGetImageHeight ib(5), ib(2)                         ; image.縦

    if f == 0 | f == 2 {                        ; ウィンドウの初期化を伴う
        if (m & 3) == 0 {                           ; オプションでのウィンドウ指定は無い
            mref bb, 67  : ib(8) = bb(17), ginfo_sel
        } else {                                    ; オプションでのウィンドウ指定が有る
            ib(8) = m & 3
            if w < 0  : ib(9) = ginfo_newid  : else  : ib(9) = w
        }
        if (m & %1100) == %1100  : ib(10) = ib(1) * 2  : else  : ib(10) = ib(1) ; 横幅

        ; ここまででibは (8)ウィンドウ形状 (9)WindowID (10)Window横幅
        if ib(8) == 1  : buffer ib(9), ib(10), ib(2)
        if ib(8) == 2  : screen ib(9), ib(10), ib(2)
        if ib(8) == 3  : bgscr  ib(9), ib(10), ib(2)
        if f == 2  : boxf                           ; モード2の時は黒塗りする
    }

    GdipCreateFromHDC hdc, ib(6)                ; ウィンドウからグラフィクス

    if m & %1100 {                              ; オプションでアルファブレンド処理が指定されている場合
        GdipCreateImageAttributes ib(7)             ; アトリビュート
        if m & %0100 {                              ; アルファブレンドを無視する画像描画
            ib( 8) = $3F800000, 0, 0, 0, 0, 0, $3F800000, 0, 0, 0, 0, 0, $3F800000, 0, 0
            ib(23) = 0, 0, 0, 0, 0, 0, 0, 0, $3F800000, $3F800000
            GdipSetImageAttributesColorMatrix ib(7), 1, 1, ib(8)
            GdipDrawImageRectRectI ib(6),ib(5),ginfo_cx,ginfo_cy,ib(1),ib(2),0,0,ib(1),ib(2),2,ib(7)
        }
        if m & %1000 {                              ; アルファブレンドマスクの取り出し
            ib( 8) = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
            ib(23) = $3F800000, $3F800000, $3F800000, 0, 0, 0, 0, 0, $3F800000, $3F800000
            GdipSetImageAttributesColorMatrix ib(7), 1, 1, ib(8)
            GdipDrawImageRectRectI ib(6),ib(5),ginfo_cx+((m>>2&1)*ib(1)),ginfo_cy,ib(1),ib(2),0,0,ib(1),ib(2),2,ib(7)
                ; 先にプレーン画像を描画しているときは、その画像の右に描画するようにする。
        }
        GdipDisposeImageAttributes ib(7)            ; アトリビュートの破棄
    } else {                            ; オプションにアルファブレンド処理が指定ない場合は普通のコピー
        GdipDrawImageRectI ib(6), ib(5), ginfo_cx, ginfo_cy, ib(1), ib(2)   ; (アルファは適用される)
    }

    GdipDeleteGraphics ib(6)        ; 事後処理
    GdipDisposeImage   ib(5)
    ImgM_ReleaseStream
    GlobalFree         ib(3)
    ImgM_CloseGdiplus
    mref bb, 67  : if bb(19) & $FFFF0000  : redraw 1        ; 再描画処理
    return

/*-------------------------------------------------------------------------------------------------------
%index
ImgF_jpgsave
画面イメージセーブ.JPG編(GDI+)
%prm
Path, Quality
Path    [文字]保存するファイル名(パス)
Quality [数値]品質
    0:高圧縮(粗い)〜100:低圧縮(きめ細やか)
%inst
HSP標準のbmpsave命令みたいなものです。GDI+を使用してJPG形式でファイル保存します。品質指定付き。
%href
alSaveFile
ImgP_Memsave
%------------------------------------------------------------------------------------------------------*/
#deffunc ImgF_jpgsave str s, int p  ; HSP同梱のGDI+イメージ保存にjpeg圧縮率指定が無いことに対する措置
    ib     = p,0,$557CF401,$11D31A04,$0000739A,$2EF31EF8,1,$1D5BE4B5,$452DFA4A,$B35DDD9C,$EBE70551,1,4,0
    ib(13) = varptr(ib)                 ; 配列自動確保がされてもいいように確定してから代入
    ImgM_OpenGdiplus
    mref bb, 67  : GdipCreateBitmapFromGdiDib bb(6), bb(5), ib(1)
    GdipSaveImageToFile ib(1), s, ib(2), ib(6)
    GdipDisposeImage ib(1)
    ImgM_CloseGdiplus
    return

/*=======================================================================================================
%index                                                                          ; 旧 "ImagePrintModule"
%group
画像関連モジュール(加工描画)
%--------------------------------------------------------------------------------------------------------
%index
ImgP_CalcFitSize
等倍計算(矩形に収まる画像サイズ)
%prm
Res_W, Res_H, PicW, PicH, RectW, RectH
Res_W, Res_H [変数]結果を受け取る変数(int型)
PicW,  PicH  [数値]元画像の大きさ(横幅、縦幅)
RectW, RectH [数値]矩形の大きさ(横幅、縦幅)
%inst
縦横比固定で画像を拡縮する時に指定領域に収まる最大サイズを算出します。
%href
ImgP_gzoom
%index
ImgP_gzoom
変倍して画面コピー(GDI+)
%prm
SizeX, SizeY, TrimWinID, TrimX, TrimY, TrimW, TrimH
SizeX, SizeY [数値]貼り付け時の画面サイズ
TrimWinID    [数値]コピー元のウィンドウID
TrimX, TrimY [数値]コピー元の起点座標
TrimW, TrimH [数値]コピー元の切り出しサイズ
%inst
HSP標準のgzoom命令をGDI+を使用して再現します。標準命令よりも、特に拡大時に画質が良くなるかもしれません。
画像はカレントポジションを左上とした位置にSizeXとSizeYで指定した大きさで描画されます。
TrimXとTrimYは通常左上座標ですが、TrimWやTrimHに負数値を指定することでミラー反転を行うことも可能です。
%href
ImgP_CalcFitSize
%------------------------------------------------------------------------------------------------------*/
#deffunc ImgP_CalcFitSize var x, var y, int a, int b, int w, int h
    if a == 0 | b == 0  : x = 0  : y = 0    : return    ; 0割り。
    ib = w * 10000 / a , h * 10000 / b                  ; 万分率(笑)‰。
    if ib <  ib(1)  : x = w  : y = b * ib / 10000
    if ib == ib(1)  : x = w  : y = h
    if ib >  ib(1)  : x = a * ib(1) / 10000  : y = h
    if x + 1 == w  : x = w                              ; 誤差1pixel
    if y + 1 == h  : y = h
    return                                              ; この命令で実数使ってないのは昔の名残。

#deffunc ImgP_gzoom int w, int h, int i, int x, int y, int a, int b
    ib = ginfo_sel, 0, 0
    ImgM_OpenGdiplus
    gsel i   : mref bb, 67  : GdipCreateBitmapFromGdiDib bb(6), bb(5), ib(1); コピー元をImageにする
    gsel ib  : GdipCreateFromHDC hdc, ib(2)                                 ; コピー先をGraphicにする
    GdipDrawImageRectRectI ib(2), ib(1), ginfo_cx, ginfo_cy, w, h, x, y, a, b, 2    ; コピー実行。
    GdipDeleteGraphics ib(2)
    GdipDisposeImage   ib(1)
    ImgM_CloseGdiplus
    mref bb, 67  : if bb(19) & $FFFF0000  : redraw 1                        ; 再描画処理
    return

/*-------------------------------------------------------------------------------------------------------
%index
ImgP_RotateFlip
画像の反転や90°回転(GDI+)
%prm
Mode, TrimWinID, TrimX, TrimY, TrimW, TrimH
Mode         [定数]回転方法
    0 : 何もしない            (gcopy状態)
    1 : 時計回り 90°回転
    2 : 時計回り180°回転     (上下左右反転ともいう)
    3 : 時計回り270°回転
    4 :   0°回転後、左右反転 (左右反転というまでもない)
    5 :  90°回転後、左右反転
    6 : 180°回転後、左右反転 (上下反転ともいう)
    7 : 270°回転後、左右反転
TrimWinID    [数値]コピー元のウィンドウID
TrimX, TrimY [数値]コピー元の起点座標
TrimW, TrimH [数値]コピー元の切り出しサイズ
%inst
画像をミラー反転や90度回転してコピーします。画像はカレントポジションを左上とした位置に描画されます。
%href
ImgP_grotate
%------------------------------------------------------------------------------------------------------*/
#deffunc ImgP_RotateFlip int m, int i, int x, int y, int w, int h
    ib = ginfo_sel, 0, 0, 0

    ImgM_OpenGdiplus
    gsel i
    mref bb, 67  : GdipCreateBitmapFromGdiDib bb(6), bb(5), ib(1)           ; コピー元をImageにする
    GdipCloneBitmapAreaI x, y, w, h, PixelFormat32bppARGB, ib(1), ib(2)     ; トリミングしたImage
    GdipImageRotateFlip ib(2), m                                            ; 回転実行
    gsel ib
    GdipCreateFromHDC hdc, ib(3)                                            ; 貼り付け先のGraphics
    GdipDrawImageI ib(3), ib(2), ginfo_cx, ginfo_cy                         ; 貼り付け実行

    GdipDeleteGraphics ib(3)
    GdipDisposeImage   ib(2)
    GdipDisposeImage   ib(1)
    ImgM_CloseGdiplus
    mref bb, 67  : if bb(19) & $FFFF0000  : redraw 1                ; 再描画処理
    return

/*-------------------------------------------------------------------------------------------------------
%index
ImgP_Memsave
画面イメージセーブto変数(GDI+)
%prm
Res_Bin, Format, Width, Height, Option
Res_Bin [変数]データを受け取る変数(文字列型に変換可能であること)
Format  [定数]保存形式
    1 : BMP
    2 : JPG
    3 : GIF
    4 : PNG
Width   [数値]横幅
Height  [数値]縦幅
Option  [数値]JPGの時 : 品質(0〜100)
%inst
現在の画面イメージをFormatで指定したファイル形式で保存します。Res_Binで指定した変数が出力先となります。命令実行後、システム変数statに出力されたデータサイズ(byte)が代入されています。

WidthかHeightが0や省略の場合、画面全体が対象となります(画像サイズはウィンドウの初期化サイズです)。WidthとHeightが0以外の場合はカレントポジションから指定の範囲が保存されます。
保存形式が2(JPG)の場合、Optionで品質を指定します。0(高圧縮、粗い)から100(低圧縮、きめ細やか)の整数で指定可能です。省略時は0扱いです。
%href
ImgF_jpgsave
%------------------------------------------------------------------------------------------------------*/
#deffunc ImgP_Memsave var b, int m, int w, int h, int o, int p  ; 変数,形式,幅,高さ,品質,(pixelformat)
    ib = 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0, 1, 0,0,0,0,0,0,0,0
    ;   (0)GlobalSize (1)stream (2)image(結果) (3)image(works) (4)hGlobal
    ;   (5)PosX (6)PosY (7)Width (8)Height (9)PixelFormat (10〜13)ImageCodec
    ;   (14)パラメ数(0だと保存に失敗するので1以上、1こも必要ない場合はパラメに無効な値をいれるとか...)
    ;   (15〜18)パラメエンコーダ (19)パラメ要素数? (20)パラメ型 (21)ポインタ(っ!)
    ;   (22〜)パラメエンコーダから繰り返し

    if w==0 | h==0 : ib(5) = 0,0,ginfo_sx,ginfo_sy :else: ib(5) = ginfo_cx,ginfo_cy,w,h ; トリミング

    mref bb, 67
    if bb(3)  : ib(9) = PixelFormat8bppIndexed  : else  : ib(9) = PixelFormat24bppRGB   ; ピクフォマ
    if p  : ib(9) = p   ; ピクセルフォーマットを外部から指定する実験

    if m == 1  : ib(10) = $557CF400, $11D31A04, $0000739A, $2EF31EF8        ; BMP
    if m == 2 {
        ib(22) = o
        ib(10) = $557CF401, $11D31A04, $0000739A, $2EF31EF8                 ; JPG
        ib(14) = 1 ,$1D5BE4B5, $452DFA4A, $B35DDD9C, $EBE70551, 1, 4, varptr(ib(22))
    }
    if m == 3  : ib(10) = $557CF402, $11D31A04, $0000739A, $2EF31EF8        ; GIF
    if m == 4  : ib(10) = $557CF406, $11D31A04, $0000739A, $2EF31EF8        ; PNG

    ImgM_OpenGdiplus
    GdipCreateBitmapFromGdiDib bb(6), bb(5), ib(3)
    GdipCloneBitmapAreaI ib(5), ib(6), ib(7), ib(8), ib(9), ib(3), ib(2)
    ImgM_CreateStream 0
    GdipSaveImageToStream ib(2), ImghStream, ib(10), ib(14)
;   if stat  : ImgM_CloseH  : return 0                  ; 明らかな不成功とか、マジ消えろ。
                                                        ; ↑この安全装置は解除した。。。
    GetHGlobalFromStream ImghStream, ib(4)              ; ストリーム管理のhGlobalを拝借
    GlobalSize ib(4)  : ib = stat                       ; おおきさ
    b = "(C)衣日和"  : memexpand b, ib                  ; メモリ確保
    GlobalLock ib(4)
    dupptr bb, stat, ib, vartype("str")
    memcpy b, bb, ib
    GlobalUnlock ib(4)

    ImgM_ReleaseStream
    GdipDisposeImage ib(2)
    GdipDisposeImage ib(3)
    ImgM_CloseGdiplus
    return ib

/*-------------------------------------------------------------------------------------------------------
%index
ImgP_FilterPastel
画像フィルター(輝度補正)
%prm
Lumin, Width, Height
Lumin  [数値]補正値(-256〜256)
Width  [数値]横幅
Height [数値]縦幅
%inst
現在の画面に対して輝度補正を行います。
WidthかHeightが0や省略の場合、画面全体が対象となります。WidthとHeightが0以外の場合はカレントポジションから指定の範囲が対象です。

Luminに輝度変化の割合を指定します。この値によって各ピクセルは、まっくろ(-256指定時)からまっしろ(256指定時)の間で変化します。なお、0を指定した時の輝度変化はありません(基準)。
Luminに正数を指定すると、パステル調の白が混じったようなやわらかい印象になります。
Luminに負数を指定すると、全体的に暗く色変化の乏しい画像になります。
%href
ImgP_FilterVivid
%index
ImgP_FilterVivid
画像フィルター(色強調)
%prm
Lumin, Width, Height
Lumin  [数値]補正値(-256〜256)
Width  [数値]横幅
Height [数値]縦幅
%inst
現在の画面に対して輝度補正を行います。
WidthかHeightが0や省略の場合、画面全体が対象となります。WidthとHeightが0以外の場合はカレントポジションから指定の範囲が対象です。

Luminに輝度変化の割合を指定します。この値によって各ピクセルは色を強調するように変化します。
Luminに正数を指定すると、各ピクセルの色は濃くなります。特に暗い色ほど、より大きく輝度が下がることになります。Lumin=256の時の目安は以下の通りです。
    輝度 = 元輝度 - (255 - 元輝度)
    ※例) 元輝度が250の時、輝度は245
    ※例) 元輝度が130の時、輝度は5
Luminに負数を指定すると、各ピクセルの色は白に近づきます。Lumin=-256の時、元の輝度の2倍の輝度に変化します。
Luminに0を指定した時の輝度変化はありません(基準)。
いずれの場合も輝度が0〜255の範囲に収まるように下上限処理されます。
%href
ImgP_FilterPastel
%------------------------------------------------------------------------------------------------------*/
#deffunc ImgM_FilterLight int l, int w, int h, int m    ; 光度?輝度?
    mref bb, 67                                         ; ginfoでも結構いけるけど全部これで
    ib = bb(19), bb(35), bb(33), bb(34), bb(65)         ; 再描画フラグとgmodeの現在値
    if w == 0 | h == 0  : ib(5) = 0, 0, bb(1), bb(2)  : else  : ib(5) = bb(27), bb(28), w, h    ; 範囲
    ib(9) = bb(27), bb(28)                              ; カレントポジション(忘れてた...)
    if m == 0  : gmode 5, , , limit(abs(l), 0, 256)     ; モード:ビビッドトーン
    if m == 1  : gmode 6, , , limit(abs(l), 0, 256)     ; モード:パステルトーン
    redraw 0                                            ; ネガ時のgcopyが一瞬光るので強制的にオフっとく
    if 0 < l  : BitBlt hdc, ib(5), ib(6), ib(7), ib(8), , , , DSTINVERT     ; ネガ
    pos ib(5), ib(6)  : gcopy bb(18), ib(5), ib(6), ib(7), ib(8)
    if 0 < l  : BitBlt hdc, ib(5), ib(6), ib(7), ib(8), , , , DSTINVERT     ; ポジ

    pos ib(9),ib(10)  : gmode ib(1),ib(2),ib(3),ib(4)   ; そして元の位置に戻す
    if ib & $FFFF0000  : redraw 1                       ; 再描画(設定の復元もかねて)
    return

    ; 組み合わせて4つの命令ができたけど↑最終的に1こにまとまった(笑  ↓マクロの形で提供
#define global ImgP_FilterPastel(%1,%2=0,%3=0)  ImgM_FilterLight %1,%2,%3,1
#define global ImgP_FilterVivid(%1,%2=0,%3=0)   ImgM_FilterLight %1,%2,%3,0
    ; 補正値が正数だとビビっとあざやかって感じではなくガンマとかコントラストとかが近いかも

/*-------------------------------------------------------------------------------------------------------
%index
ImgP_FilterNega
画像フィルター(ネガポジ、排他的論理和=XOR)
%prm
Width, Height
Width  [数値]横幅
Height [数値]縦幅
%inst
現在の画面に対してカレントカラーとのXOR演算を行います。
WidthかHeightが0や省略の場合、画面全体が対象となります。WidthとHeightが0以外の場合はカレントポジションから指定の範囲が対象です。

XORはいわばビット反転をする計算で、たとえば
    %11001100 ^ %00001111 → %11000011
    ※ %11001100:被数  ^:演算子  %00001111:対象ビット指定
となります。
ですので、まっしろ(255, 255, 255)とのXORはネガポジ効果を、まっくろ(0, 0, 0)とのXORは無意味となるほか、さまざまな画像効果を得ることができます。
%------------------------------------------------------------------------------------------------------*/
#deffunc ImgP_FilterBrush int w, int h, int o
    mref bb, 67
    SelectObject hdc, bb(36)  : ib = stat           ; HSPが作り置きしているブラシオブジェを設定
    if w == 0 | h == 0  : ib(1) = 0, 0, bb(1), bb(2)  : else  : ib(1) = bb(27), bb(28), w, h    ; ハニ
    BitBlt hdc, ib(1), ib(2), ib(3), ib(4), , , , o     ; ブラシ                        はにっ!?→範囲
    SelectObject hdc, ib                                ; 元のブラシに戻す
    if bb(19) & $FFFF0000  : redraw 1                   ; 再描画処理
    return

#define global ImgP_FilterNega(%1=0,%2=0)   ImgP_FilterBrush %1,%2,PATINVERT@ImageModule2
    ; ネガポジと言うには性質がやや違う気がするorz

/*-------------------------------------------------------------------------------------------------------
%index
ImgP_GcopySubAbs
画像フィルター(2画像の差の絶対値)
%prm
DiffWinID, PosX, PosY, Width, Height
DiffWinID     [数値]画像BのウィンドウID
PosX, PosY    [数値]画像Bの左上座標
Width, Height [数値]横幅、縦幅
%inst
現在の画面(画像A)とパラメータ指定の画面(画像B)で差をとりその絶対値を示します。この画像は輝度の差が小さいほど暗い(黒い)点として表示され
    ・良く似た2つの画像の相違点を視覚的に示す
    ・圧縮による画質の劣化具合の判定
    ・色減算コピー時にクランプされている部分の視覚化
といった用途に使用可能です。

画像Aについて、WidthかHeightが0や省略の場合は現在の画面全体が対象となります。WidthとHeightが0以外の場合はカレントポジションから指定の範囲が対象です。
画像Bは指定座標を左上とする位置から画像Aと同サイズが対象となります。結果画像は画像Aを上書きする形で描画されます。
%------------------------------------------------------------------------------------------------------*/
#deffunc ImgP_GcopySubAbs int i, int x, int y, int w, int h
    ; やっていることは[A-B=C][B-A=D][C+D=E]の画像加減コピーに過ぎないが、作業用Win不要化で回りくどい。
    ; 画像ストックのコンパチはコピー元Winに合わせる(コピー元画像(計算後に残る方)の劣化を防ぐため)。
    ; [C+D=E]は、負数クランプの為、一方は 0 になっている → 論理和/排他的論理和/合算(加算)どれでもOK!

    ;   ib( 0〜 7)コピー先設定  0:WinID   1:redrawFrag   2-5:gmode   6-7:curPos
    ;   ib( 8〜11)出力位置範囲  8-9:pos   10-11:size
    ;   ib(12〜18)コピー元設定  12:redrawFlag   13-16:gmode   17-18:curPos
    ;   ib(19〜21)ハンドルズ    19:CreateHDC   20:CreatehBitmap   21:hBitmapStock

    mref bb, 67
    ib = bb(18), bb(19), bb(35), bb(33), bb(34), bb(65), bb(27), bb(28)     ; コピー先設定(控え)
    redraw 0                                                                ; コピー先再描画オフ

    if w == 0 | h == 0 : ib(8) = 0, 0, bb(1), bb(2) :else: ib(8) = bb(27), bb(28), w, h ; 範囲計算

    gsel i                      ; ここからの hdc はコピー元
    mref bb, 67
    ib(12) = bb(19), bb(35), bb(33), bb(34), bb(65), bb(27), bb(28), 0, 0, 0    ; コピー元控え+hndl域
    redraw 0                                                                    ; コピー元再描画オフ

    CreateCompatibleDC hdc                          : ib(19) = stat     ; デバコン作り
    CreateCompatibleBitmap hdc, ib(10) * 2, ib(11)  : ib(20) = stat     ; ビトマ作り
    SelectObject ib(19), ib(20)                     : ib(21) = stat     ; ビトマ差し替え

    BitBlt ib(19), ib(10), 0, ib(10), ib(11), hdc, x, y, SRCCOPY        ; コピー元の画像を控えておく
    gmode 6, ib(10), ib(11), 256  : pos x, y  : gcopy ib, ib(8), ib(9)  ; まずコピー元に減算実行
    BitBlt ib(19), 0, 0, ib(10), ib(11), hdc, x, y, SRCCOPY             ; 減算結果を控える
    BitBlt hdc, x, y, ib(10), ib(11), ib(19), ib(10), 0, SRCCOPY        ; コピー元は減算前に戻す

    gsel ib                     ; ここからの hdc はコピー先
    gmode 6, ib(10), ib(11), 256  : pos ib(8), ib(9)  : gcopy i, x, y   ; 次にコピー先に減算実行
    BitBlt hdc, ib(8), ib(9), ib(10), ib(11), ib(19), 0, 0, SRCPAINT    ; ふたつの減算結果を合体

    SelectObject ib(19), ib(21)                 ; 差し替えていたビトマを差し戻す
    DeleteObject ib(20)                         ; ビトマ廃棄
    DeleteDC     ib(19)                         ; デバコン廃棄

    ; ウィンドウのCurPos/gmode/RedrawFlagを復元(WinID一致の可能性を考慮して最後にまとめて仕上げる)
    gsel i  : pos ib(17),ib(18) : gmode ib(13),ib(14),ib(15),ib(16) : if ib(12)&$FFFF0000 : redraw 1
    gsel ib : pos ib( 6), ib(7) : gmode ib( 2),ib( 3),ib( 4),ib( 5) : if ib( 1)&$FFFF0000 : redraw 1
    return

/*-------------------------------------------------------------------------------------------------------
%index
ImgP_grotate
矩形画像を回転してコピー(GDI+)
%prm
TrimWinID, TrimX, TrimY, Angle, Width, Height
TrimWinID     [数値]元画像のウィンドウID
TrimX, TrimY  [数値]元画像の起点(左上)座標
Angle         [実数]回転角度(ラジアン、1周=2π・時計回り)
Width, Height [数値]描画サイズ(横幅、縦幅)
%inst
HSP標準のgrotate命令をGDI+を使用して再現します。…が異なる点も多いです。標準命令よりも画質が良くなります。

描画はカレントポジションを中心とした位置に行われます。
描画後の矩形サイズをWidthとHeightに指定します。回転するため画像はこの矩形には収まりません。WidthかHeightを0か省略した場合は等倍コピー、大きさを指定した場合は拡大・縮小コピーになります。
元画像のサイズはgmode命令で設定した値を使用しますので、あらかじめ指定しておいてください。

gmodeのコピーモードは0か1のみに対応しています(つまりベタぬりです)。
元画像の範囲に画面外領域が含まれる場合、その領域は透過画素の扱いになります(grotateではリサイズ?されるようで、拡縮・描画位置に影響が出ます)。
%href
ImgP_RotateFlip
%------------------------------------------------------------------------------------------------------*/
;   grotateの再現と言うより、任意角回転命令としてまとめた方が簡素で良いかも?
#deffunc ImgP_grotate int i, int x, int y, double r, int w, int h
    mref bb, 67                                 ; この処理って実のところartlet2dにー対ーで対応できる
    ib = bb(18), bb(19), bb(35), bb(33), bb(34), bb(65), bb(27), bb(28), w, h, 0, 0, 0
    if w == 0 | h == 0  : ib(8) = ib(3), ib(4) ; : else  : ib(8) = w, h     ; 描画サイズ
    ;   ib(0-7)描画先情報   0:WinID   1:redrawFrag   2-5:gmode   6-7:curPos←使ってなくね?
    ;   ib(8-9)貼り付け時の大きさ     ちなみにib(3-4)切り取りサイズ
    ;   ib(10-12)ハンドル matrix,graphic,image

    ImgM_OpenGdiplus
    GdipCreateMatrix    ib(10)                                  ; matrixを作る
    GdipTranslateMatrix ib(10), ginfo_cx, ginfo_cy, 0           ; 座標は、実は実数
    GdipRotateMatrix    ib(10), rad2deg(r), 0                   ; GDI+ではrad→degする
    GdipTranslateMatrix ib(10), -ginfo_cx, -ginfo_cy, 0
    GdipCreateFromHDC   hdc, ib(11)                             ; 描画先のgraphic
    GdipSetWorldTransform ib(11), ib(10)

    gsel i  : mref bb, 67  : GdipCreateBitmapFromGdiDib bb(6), bb(5), ib(12)    ; 元画像のimage
    gsel ib
    GdipDrawImageRectRectI ib(11),ib(12),ginfo_cx-ib(8)/2,ginfo_cy-ib(9)/2,ib(8),ib(9),x,y,ib(3),ib(4),2 

    GdipDisposeImage    ib(12)
    GdipDeleteGraphics  ib(11)
    GdipDeleteMatrix    ib(10)
    ImgM_CloseGdiplus

    if ib(1) & $FFFF0000  : redraw 1
    return

/*-------------------------------------------------------------------------------------------------------
%index
ImgP_PairCopy
画面コピー(2画像を左右に並べる)
%prm
Width, Height, WinID1, PosX1, PosY1, WinID2, PosX2, PosY2, OffsetX, OffsetY
Width, Height        [数値]1画像の横幅と縦幅
WinID1, PosX1, PosY1 [数値]左側画像指定
WinID2, PosX2, PosY2 [数値]右側画像指定
OffsetX, OffsetY     [数値]右側画像の位置補正
%inst
現在の画面・座標に対してgmode 0の画面コピー(gcopy)を2回実行するおまとめ命令です。2つの画像は横に隣接するように描画され
    ・離れた場所にあるgmode 7用の画像とマスクを組み合わせる
    ・視差(ステレオグラム)を使った演出
といった用途に使えます。
%------------------------------------------------------------------------------------------------------*/
#deffunc ImgP_PairCopy int w, int h, int l, int m, int n, int r, int s, int t, int o, int v
    mref bb, 67                                                         ; ステレオって立体って意味だった。
    ib = bb(35), bb(33), bb(34), bb(65), bb(27), bb(28)                 ; 設定戻し用(gmodeとpos)
    if w == 0 | h == 0  : ib(6) = ib(1), ib(2)  : else  : ib(6) = w, h  ; 描画サイズ
    gmode 0, ib(6), ib(7)
    pos ib(4) + ib(6) + o, ib(5) + v  : gcopy r, s, t
    pos ib(4), ib(5)  : gcopy l, m, n
    gmode ib, ib(1), ib(2), ib(3)  ;: pos ib(4), ib(5)                  ; 設定戻し
    return

/*-------------------------------------------------------------------------------------------------------
%index
ImgP_LoopCopy
画面コピー(範囲敷き詰め)
%prm
Width, Height, WinID, PosX, PosY, SizeW, SizeH, OffsetX, OffsetY
Width, Height                   [数値]敷き詰める範囲
WinID, PosX, PosY, SizeW, SizeH [数値]敷き詰める画像
OffsetX, OffsetY                [数値]画像切り出し位置(左上)
%inst
現在の画面・座標からWidth/Heightで指定した範囲を埋めるように画像を繰り返しコピーします。gmode命令でコピー方法を指定することが可能です。
%------------------------------------------------------------------------------------------------------*/
#deffunc ImgP_LoopCopy int w, int h, int i, int x, int y, int s, int t, int o, int v
    mref bb, 67
    ib = bb(19), ginfo_cx, ginfo_cy, 0, 0, 0    ; redraw flag, curposx, y, temp hdc, hbitmap, old hbmp
    if s == 0 | t == 0  : ib(6) = bb(33), bb(34)  : else  : ib(6) = s, t    ; 1チップのサイズ
    ib(8) = ib(1), ib(2), w, h                                  ; 描画開始位置とサイズ
    if o  : ib(8) -=  o  : ib(10) += o                          ; サイズ補正(左側半端域)
    if v  : ib(9) -=  v  : ib(11) += v                          ; サイズ補正(上側半端域)
    if ib(10) \ ib(6)  : ib(10) += ib(6) - ib(10) \ ib(6)       ; サイズ補正(右側半端域)
    if ib(11) \ ib(7)  : ib(11) += ib(7) - ib(11) \ ib(7)       ; サイズ補正(下側半端域)
    ib(12) = ib(10) / ib(6), ib(11) / ib(7)                     ; 横縦にチップを敷き詰める枚数

    if w != ib(10) | h != ib(11) {              ; 前後左右に半端域がある場合(補正で枚数が増えたとき)
        CreateCompatibleDC      hdc                 : ib(3) = stat
        CreateCompatibleBitmap  hdc, ib(10), ib(11) : ib(4) = stat
        SelectObject ib(3), ib(4)  : ib(5) = stat
        BitBlt ib(3), 0, 0, ib(10), ib(11), hdc, ib(8), ib(9), SRCCOPY
        ; 半端部分は一度描画しちゃってから元の絵を貼りなおすという処理にする(gmode 7 対応)
    }
    redraw 0
    repeat ib(13)
        ib(14) = cnt * ib(7) + ib(9)
        repeat ib(12)
            pos cnt * ib(6) + ib(8), ib(14)  : gcopy i, x, y, ib(6), ib(7)
        loop
    loop
    if w != ib(10) | h != ib(11) {              ; 半端域戻し
        ib(14) = ib(8) + ib(10) - ib(1) - w, ib(9) + ib(11) - ib(2) - h         ; 右下削り幅
        if o  : BitBlt hdc, ib(8), ib(9), o, ib(11), ib(3), 0, 0, SRCCOPY
        if v  : BitBlt hdc, ib(8), ib(9), ib(10), v, ib(3), 0, 0, SRCCOPY
        if ib(14) : BitBlt hdc,ib(8)+ib(10)-ib(14),ib(9),ib(14),ib(11), ib(3),ib(10)-ib(14),0, SRCCOPY
        if ib(15) : BitBlt hdc,ib(8),ib(9)+ib(11)-ib(15),ib(10),ib(15), ib(3),0,ib(11)-ib(15), SRCCOPY
        ; このbitbltはブラシやマスクで一発で決められないだろうか
        SelectObject ib(3), ib(5)  : ib(4) = stat   ; bitmapを戻す(ib.4の値は変わらないと思われる)
        DeleteObject ib(4)
        DeleteDC     ib(3)
    }
    pos ib(1), ib(2)  : if ib & $FFFF0000  : redraw 1   ; 設定戻し込み再描画
    return

/*-------------------------------------------------------------------------------------------------------
%index
ImgP_Exchange
画面コピー(2画像を交換)
%prm
WinID, PosX, PosY, SizeW, SizeH
WinID        [数値]画像BのウィンドウID
PosX, PosY   [数値]画像Bの左上座標
SizeW, SizeH [数値]横幅、縦幅
%inst
現在の画面(画像A)とパラメータ指定の画面(画像B)を交換します。
画像Aについて、WidthかHeightが0や省略の場合は現在の画面全体が対象となります。WidthとHeightが0以外の場合はカレントポジションから指定の範囲が対象です。
画像Bは指定座標を左上とする位置から画像Aと同サイズが対象となります。
%------------------------------------------------------------------------------------------------------*/
#deffunc ImgP_Exchange int i, int x, int y, int w, int h
    ib = ginfo_sel, hdc
    if w == 0 | h == 0  : ib(2) = 0, 0, ginfo_sx, ginfo_sy  : else  : ib(2) = ginfo_cx, ginfo_cy, w, h
    CreateCompatibleDC      hdc                 : ib(6) = stat
    CreateCompatibleBitmap  hdc, ib(4), ib(5)   : ib(7) = stat
    SelectObject ib(6), ib(7)                   : ib(8) = stat
    BitBlt ib(6), 0, 0, ib(4), ib(5), hdc, ib(2), ib(3), SRCCOPY

    gsel i
    BitBlt ib(1), ib(2), ib(3), ib(4), ib(5), hdc, x, y, SRCCOPY
    BitBlt hdc, x, y, ib(4), ib(5), ib(6), 0, 0, SRCCOPY    ; 領域が重なった場合は画像Aが優先になる

    SelectObject ib(6), ib(8)
    DeleteObject ib(7)
    DeleteDC     ib(6)

    mref bb, 67  : if bb(19) & $FFFF0000  : redraw 1
    if i != ib  : gsel ib  : mref bb, 67  : if bb(19) & $FFFF0000  : redraw 1
    return

; http://www.tvg.ne.jp/menyukko/ ; Copyright(C) 2010-2015 衣日和 All rights reserved.
#global
#endif

サンプル

上記のモジュールに連結して実行してください。
    screen 0, 800, 600  : title "『略して仮。』http://www.tvg.ne.jp/menyukko/"
    ; redraw 0          ; ←うちのモジュールは再描画フラグをちゃんとチェックしているはず、、、

    pos   4,   4  : mes "@PicloadEx(Opt=1+12)"             ; 透過もちPNGを画像/マスク分離ロード
    ImgF_PicloadEx dir_tv + "hsptv_img.png", 0, 1 + 12, 1   ; 未初期化ウィンドウ強制用意ロード
    gsel 0  : pos   4,  22  : gcopy 1, 416, 0, 192, 128

    pos 204,   4  : mes "AMemsave(BMP)以降の元絵"          ; 画面の一部をビットマップ形式で
    gsel 1  : pos 192, 320  : ImgP_Memsave BinBuf, 1, 192, 128  ; メモリに保存してmemfileで読み込む
    memfile BinBuf, 0, stat  : gsel 0  : pos 204, 22  : picload "MEM:dummy", 1

    pos 404,   4  : mes "BMemsave(GIF)"                    ; GIFの場合は最大256色
    pos 204, 22  : ImgP_Memsave BinBuf, 3, 192, 128
    memfile BinBuf, 0, stat  : pos 404, 22  : picload "MEM:dummy", 1

    pos 604,   4  : mes "CMemsave(PNG)+PicloadEx"          ; PNGをmemfile+picloadする場合は、
    pos 204, 22  : ImgP_Memsave BinBuf, 4, 192, 128         ;   拡張子(.png)が必要になるかもね?
    memfile BinBuf, 0, stat
    pos 604, 22  : ImgF_PicloadEx "MEM:dummy", 1            ; ←拡張子は付いてないよ。

    pos   4, 154  : mes "DMemsave(JPG Quo=90)"             ; JPGは非可逆なので劣化は免れない。
    pos 204, 22  : ImgP_Memsave BinBuf, 2, 192, 128, 90
    memfile BinBuf, 0, stat  : pos 4, 172  : picload "MEM:dummy", 1

    pos 204, 154  : mes "EMemsave(JPG Quo=10)"             ; JPG超圧縮。画質は…
    pos 204, 22  : ImgP_Memsave BinBuf, 2, 192, 128, 10
    memfile BinBuf, 0, stat  : pos 204, 172  : picload "MEM:dummy", 1

    pos 404, 154  : mes "FSubAbs  D&E"                  ; 圧縮率による画像の劣化具合を見てみよう
    pos 404, 172  : gcopy 0, 4, 172, 192, 128  : ImgP_GcopySubAbs 0, 204, 172, 192, 128

    pos 604, 154  : mes "GVivid  F×8"                    ; 劣化具合を強調してみる
    pos 604, 172  : gcopy 0, 404, 172, 192, 128
    ImgP_FilterVivid -256, 192, 128                             ; 2倍
    ImgP_FilterVivid -256, 192, 128                             ; 2倍の2倍 → つまり4倍
    ImgP_FilterVivid -256, 192, 128                             ; つまり4倍の2倍 → 8倍

    pos   4, 304  : mes "HPastel(Lum=128)"                 ; パステルフィルタとビビッドフィルタ
    pos   4, 322  : gcopy 0, 204, 22, 192, 128  : ImgP_FilterPastel  128, 192, 128
    pos 204, 304  : mes "IPastel(Lum=-128)"
    pos 204, 322  : gcopy 0, 204, 22, 192, 128  : ImgP_FilterPastel -128, 192, 128
    pos 404, 304  : mes "JVivid(Lum=128)"
    pos 404, 322  : gcopy 0, 204, 22, 192, 128  : ImgP_FilterVivid   128, 192, 128
    pos 604, 304  : mes "KVivid(Lum=-128)"
    pos 604, 322  : gcopy 0, 204, 22, 192, 128  : ImgP_FilterVivid  -128, 192, 128

    pos   4, 454  : mes "LNega(255,255,255)"               ; ネガポジる
    color 255, 255, 255
    pos   4, 472  : gcopy 0, 204, 22, 192, 128  : ImgP_FilterNega 192, 128
    color 0, 0, 0                                               ; 色もどし

    pos 204, 454  : mes "MNega(170,204,85)"                ; 適当な値でネガポジモドキる
    color %10101010, %11001100, %01010101
    pos 204, 472  : gcopy 0, 204, 22, 192, 128  : ImgP_FilterNega 192, 128
    color 0, 0, 0  : randomize                                  ; 色もどし

    pos 404, 454  : mes "NRotateFlip 270°MIRROR"          ; 立ててひっくり返す
    pos 404, 472  : ImgP_RotateFlip 7, 0, 204, 22, 192, 128

    pos 604, 454  : mes "OFitSize+ImgP_gzoom"              ; 只の2倍にならないように計算
    ImgP_CalcFitSize ResW, ResH, 64, 64, 200, 110
    pos 600 + (200 - ResW) / 2, 600 - 128 + (110 - ResH) / 2    ; センタリング
    if rnd(100) < 90 {
        ImgP_gzoom ResW, ResH, 1, 192, 192, 64, 64              ; アップでもキレイかな?
    } else {
        ImgP_gzoom ResW, ResH, 1, 320, 256, 64, 64              ; たまにエンカウントします。
    }

    pos 720, 600 - 18  : mes "(C)衣日和"                    ; ↑これの分をあけた。
    ; redraw 1                          ; ←APIトラップ。うちのモジュールは大丈夫だと信じたい。。。
; http://www.tvg.ne.jp/menyukko/ ; Copyright(C) 2014-2015 衣日和 All rights reserved.

モジュール化はしないって言ってたのに...
暫く闇鍋の具になっていたアレを結局モジュールに組みこみました。えぇ
実は私、過去に直接HSP作者様に要求を伝えたことがあるのです、picloadでマスク付ロードできるようにしてくれって...。 その時の返答は『HSPバッファを32bit化する方がシンプルで良い、今後対応していきたい。』みたいな感じ。 確かに現状のHSP3とHSP3Dishで挙動が異なる場面でもあるので、そうなればとても良いですね\(^▽^)/ 待ち遠しいなー...この辺に痕跡がっ
はいっ!Artlet2Dでした。。。やっぱGDI+だと簡単ですね〜。でもモジュール化の予定はないですよ、だって簡単なのはArtlet2Dだから。これ、直打ちすると何かが破裂しますってdouble型→REAL型あたりで。
とか言ってたアレですアレ。
#include "a2d.hsp"

    ; アルファチャンネル・透過情報を持つ画像を選択。
    dialog "png;*.gif", 16  : if stat == 0  : end

    alCreateImageByFile 0, refstr
    X = alGetWidth()
    Y = alGetHeight()

    screen 1, X * 2, Y

    ; 画像からαチャンネルを除いたプレーン画像を取り出す。
    cmatrix(MAT_R) = 1.0, 0.0, 0.0, 0.0, 0.0
    cmatrix(MAT_G) = 0.0, 1.0, 0.0, 0.0, 0.0
    cmatrix(MAT_B) = 0.0, 0.0, 1.0, 0.0, 0.0
    cmatrix(MAT_A) = 0.0, 0.0, 0.0, 0.0, 1.0
    alCopyModeColorMatrix cmatrix
    alCopyImageToScreen 0, 1, 0, 0  ; 画像を左に。

    ; 画像からαチャンネルを取り出したマスクを作る。
    cmatrix(MAT_R) = 0.0, 0.0, 0.0, 1.0, 0.0
    cmatrix(MAT_G) = 0.0, 0.0, 0.0, 1.0, 0.0
    cmatrix(MAT_B) = 0.0, 0.0, 0.0, 1.0, 0.0
    cmatrix(MAT_A) = 0.0, 0.0, 0.0, 0.0, 1.0
    alCopyModeColorMatrix cmatrix
    alCopyImageToScreen 0, 1, X, 0  ; マスクを右に。

    redraw 1

    ; 上で設定したマトリックスをリセットする。
    alResetCopyMode ; と言うかもう使わないけどね。

    ; 描画してみよう。
    gsel 0  : picload dir_tv + "bg04.jpg"
    gmode 7, X, Y  : gcopy 1
; http://www.tvg.ne.jp/menyukko/ ; Copyright(C) 2012-2014 衣日和 All rights reserved.
そう、コレコレ。改めてArtlet2Dは偉大だなと…痛感したですよ。
独立していたモジュールも統合しました
HSP掲示板に投稿したアレです。
……ところでdatasってなんだ?なぜ付けた?私orz...いや知ってたはずだからそんな過犯ないと信じろあたし!←震(((;゚д゚)))アワ
それはともかくarrayしてたりとイロイロ変なとこありますね。まったくワケが分からないよ。
そういえば私にもあったですよ。bsaveがmemfileに対応しているって信じていた頃が。 それで再現しようと右往左往していた残骸が「使わないよ」コメントとして残っていたのです。
そして、本当に合っているかは私にも(爆)ただ丁寧に探せば出てくるものですよHSPですごいことしてる方々が。 かなりお世話になりましたm(_ _)m