snibgo's ImageMagick pages

Fancy frames

Making rectangular (or circular) frames from small elements.

The script patternFrame.bat builds a rectangular frame around an image, where the frame is built from repeated elements.

Parameter Description
%1 Input image that will be framed.
Required.
%2 Element for left piece of frame.
Required.
%3 Element for top piece of frame.
Default: left element.
%4 Element for right piece of frame.
Default: left element.
%5 Element for bottom piece of frame.
Default: top element.
%6 Format for ouput filenames.
This must contain "XX", eg my_files_XX.png.
Default: pf_XX.png.
%7 Tiling method. One of:
Default: floor.
%8 Resizing method. One of:
Default: aspect.
%9

Simple usage

We make an opaque element that will be used for all four sides. We design the element so it is horizontally tilable. In addition, to ensure the mitred corners look good, the 45° sloping lines from the top corners are equal, and the blue/green boundary is perpendicular to these lines.

%IMG7%magick ^
  -size 1000x400 xc:White ^
  +antialias ^
  -fill None -stroke #00f ^
  -draw "bezier 0,350 100,350 250,250" ^
  -draw "bezier 250,250  450,50 0,10 499,10" ^
  -draw "bezier 499,10 650,10 650,150 750,250" ^
  -draw "bezier 750,250 850,350 900,350 999,350" ^
  -fill #00f -draw "color 0,0 floodfill" ^
  -fill #0f0 -opaque White ^
  -colorspace RGB ^
  -resize "100x40^!" ^
  -colorspace sRGB ^
  ffr_elem.png
ffr_elem.png

We use the script patternFrame.bat to make a frame around toes.png.

call %PICTBAT%patternFrame ^
  toes.png ^
  ffr_elem.png . . . ^
  ffr_elem_XX.png

if ERRORLEVEL 1 goto error

set pf_ 
pf_FrmdH=300
pf_FrmdW=300
pf_ImgH=233
pf_ImgW=267
pf_WindH=220
pf_WindW=220
ffr_elem_frmd.pngjpg

The script has repeated the element to make each of the four sides, joined these to make a frame with mitred corners, and composited that over toes.png. It has used the largest number of elements that it can without creating gaps between the image and the frame. This has made the frame's window smaller than the image, so the image has been cropped by the frame.

The script has created environment variables containing the dimensions of the input image, the overall framed image, and the window in the frame.

Complex usage

We make four elements, one for each side, so each side will be different.

%IMG7%magick ^
  -pointsize 30 ^
  -background #f44 label:"?" -trim +repage -write ffr_l.png +delete ^
  -background #88f label:"@" -trim +repage -write ffr_t.png +delete ^
  -background #f2f label:"<" -trim +repage -write ffr_r.png +delete ^
  -background #0f0 label:"&" -trim +repage -write ffr_b.png +delete ^
  NULL:
ffr_l.png ffr_t.png ffr_r.png ffr_b.png

Make a frame.

call %PICTBAT%patternFrame ^
  toes.png ^
  ffr_l.png ffr_t.png ffr_r.png ffr_b.png ^
  ffr_edge_XX.png

set pf_ 
pf_FrmdH=270
pf_FrmdW=290
pf_ImgH=233
pf_ImgW=267
pf_WindH=219
pf_WindW=253
ffr_edge_frmd.pngjpg

We can examine the four edge pieces, and the complete frame.

The four edge pieces ffr_edge_l.png etc:

ffr_edge_l.png ffr_edge_t.png ffr_edge_r.png ffr_edge_b.png

The complete frame ffr_edge_frm.png

ffr_edge_frm.pngjpg

We demonstrate the four tiling methods.

call %PICTBAT%patternFrame ^
  toes.png ^
  ffr_l.png ffr_t.png ffr_r.png ffr_b.png ^
  ffr_edge_e_XX.png exact

if ERRORLEVEL 1 goto error

set pf_ 
pf_FrmdH=284
pf_FrmdW=304
pf_ImgH=233
pf_ImgW=267
pf_WindH=233
pf_WindW=267
ffr_edge_e_frmd.pngjpg
call %PICTBAT%patternFrame ^
  toes.png ^
  ffr_l.png ffr_t.png ffr_r.png ffr_b.png ^
  ffr_edge_f_XX.png floor

if ERRORLEVEL 1 goto error

set pf_ 
pf_FrmdH=270
pf_FrmdW=290
pf_ImgH=233
pf_ImgW=267
pf_WindH=219
pf_WindW=253
ffr_edge_f_frmd.pngjpg
call %PICTBAT%patternFrame ^
  toes.png ^
  ffr_l.png ffr_t.png ffr_r.png ffr_b.png ^
  ffr_edge_r_XX.png round

if ERRORLEVEL 1 goto error

set pf_ 
pf_FrmdH=285
pf_FrmdW=290
pf_ImgH=233
pf_ImgW=267
pf_WindH=234
pf_WindW=253
ffr_edge_r_frmd.pngjpg
call %PICTBAT%patternFrame ^
  toes.png ^
  ffr_l.png ffr_t.png ffr_r.png ffr_b.png ^
  ffr_edge_c_XX.png ceiling

if ERRORLEVEL 1 goto error

set pf_ 
pf_FrmdH=285
pf_FrmdW=319
pf_ImgH=233
pf_ImgW=267
pf_WindH=234
pf_WindW=282
ffr_edge_c_frmd.pngjpg

The script mitre4 takes four images for the edges, and joins them into a rectangle with mitred corners and a transparent window.

call %PICTBAT%mitre4 ^
  ffr_edge_l.png ffr_edge_t.png ffr_edge_r.png ffr_edge_b.png ^
  ffr_edge_4.png
ffr_edge_4.png

When the window width is greater than the image width. or the window height is greater than the image height, there would be a gap between the image and the frame. This can be fixed either by enlarging the image or by shrinking the frame.

We demonstrate the effect of different resizing methods. In the examples:

call %PICTBAT%patternFrame ^
  toes.png ^
  ffr_elem.png . . .  ^
  ffr_edge_cn_XX.png ceiling none

if ERRORLEVEL 1 goto error

set pf_ 
pf_FrmdH=400
pf_FrmdW=400
pf_ImgH=233
pf_ImgW=267
pf_WindH=320
pf_WindW=320
ffr_edge_cn_frmd.pngjpg
call %PICTBAT%patternFrame ^
  toes.png ^
  ffr_elem.png . . .  ^
  ffr_edge_ci_XX.png ceiling image

if ERRORLEVEL 1 goto error

set pf_ 
pf_FrmdH=400
pf_FrmdW=400
pf_ImgH=233
pf_ImgW=267
pf_WindH=320
pf_WindW=320
ffr_edge_ci_frmd.pngjpg
call %PICTBAT%patternFrame ^
  toes.png ^
  ffr_elem.png . . .  ^
  ffr_edge_ca_XX.png ceiling aspect

if ERRORLEVEL 1 goto error

set pf_ 
pf_FrmdH=400
pf_FrmdW=400
pf_ImgH=233
pf_ImgW=267
pf_WindH=320
pf_WindW=320
ffr_edge_ca_frmd.pngjpg
call %PICTBAT%patternFrame ^
  toes.png ^
  ffr_elem.png . . .  ^
  ffr_edge_cf_XX.png ceiling frame

if ERRORLEVEL 1 goto error

set pf_ 
pf_FrmdH=291
pf_FrmdW=333
pf_ImgH=233
pf_ImgW=267
pf_WindH=233
pf_WindW=267
ffr_edge_cf_frmd.pngjpg

Framing the frame

We can frame a framed image. We probably don't want the inner frame to be cropped by the outer frame, so that rules out using the default arguments.

call %PICTBAT%patternFrame ^
  ffr_edge_cf_frmd.png ^
  ffr_elem.png . . .  ^
  ffr_edge_cf2_XX.png ceiling frame
ffr_edge_cf2_frmd.pngjpg
call %PICTBAT%patternFrame ^
  ffr_edge_cf2_frmd.png ^
  ffr_elem.png . . .  ^
  ffr_edge_cf3_XX.png ceiling frame
ffr_edge_cf3_frmd.pngjpg

Shadows

The script can add a shadow to the frame before compositing over the image. For convenience in calculating the offset for the composite, the script trims above and to the left of the frame, so it only works when the shadow is to the bottom and right.

call %PICTBAT%patternFrame ^
  toes.png ^
  ffr_elem.png . . .  ^
  ffr_shad_XX.png ceiling frame ^
  100x3+5+5
ffr_shad_frmd.png

Circular frames

In this example, we decide the number of elements, and the dimensions follow from that. In practice, we might decide the required inner radius from the image to be framed.

set ELEMENT=ffr_elem.png

for /F "usebackq" %%L in (`%IMG7%magick ^
  %ELEMENT% ^
  -format "EleW=%%w\nEleH=%%h\n" ^
  info:`) do set %%L

set nEle=10

set /A PieceW=%nEle%*%EleW%

set OutRad=%%[fx:%PieceW%/2/Pi+%EleH%/2]
set InRad=%%[fx:%PieceW%/2/Pi-%EleH%/2]

%IMG7%magick ^
  %ELEMENT% -rotate 180 -write mpr:ELMT +delete ^
  -size %PieceW%x%EleH% xc: ^
  -tile mpr:ELMT -draw "color 0,0 reset" ^
  +write e.png ^
  -virtual-pixel None ^
  +distort polar %OutRad%,%InRad% ^
  ffr_circ_frm.png
ffr_circ_frm.png

When an image is to be framed, a script would make image pixels outside the inner radius transparent. Circular frames have similar alternatives to rectangular frames: whether to ensure an integer number of elements, and whether to enlarge the image or shrink the frame to avoid a gap.

Future

Something could be added to give more three-dimensionality to the frame pieces.

The idea could be extended to polygonal and elliptical frames.

Circular frames can easily be stretched to be elliptical, but then the frame thickness will vary. If we want a constant frame thickness, we could vary the height of the tiled image before the distortion.

Scripts

For convenience, .bat scripts are also available in a single zip file. See Zipped BAT files.

patternFrame.bat

rem %1 is an image for which a frame will be made,
rem %2 to %5 are input left, top, right and bottom elements for the frame.
rem %6 is format for outputs.
rem    Must contain XX, which will be replaced by l, t, r, b etc.
rem %7 is tiling method, one of:
rem    exact    fractional elements are used to exactly fit the image
rem    floor    may crop the image [default]
rem    round    may crop the image, and may create gaps
rem    ceiling  may create gaps
rem %8 is resizing method, one of:
rem    none     no resizing
rem    image    enlarges image exactly (ignoring aspect ratio)
rem    aspect   enlarges image, keeping aspect ratio
rem    frame    shrinks frame to fit image
rem %9 shadow OxS+X+Y eg 100x4+5+5 default no shadow
@rem
@rem The left, right and bottom pieces will be rotated.

@if "%2"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal enabledelayedexpansion

rem @call echoOffSave

call %PICTBAT%setInOut %1 pf

set LFILE=%2
set TFILE=%3
set RFILE=%4
set BFILE=%5

if "%TFILE%"=="." set TFILE=
if "%RFILE%"=="." set RFILE=
if "%BFILE%"=="." set BFILE=

if "%TFILE%"=="" set TFILE=%LFILE%
if "%RFILE%"=="" set RFILE=%LFILE%
if "%BFILE%"=="" set BFILE=%TFILE%

set OUTFILE=%6
if "%OUTFILE%"=="." set OUTFILE=
if "%OUTFILE%"=="" set OUTFILE=pf_XX.png

set OUT_L=%OUTFILE:XX=l%
set OUT_T=%OUTFILE:XX=t%
set OUT_R=%OUTFILE:XX=r%
set OUT_B=%OUTFILE:XX=b%
set OUT_FRM=%OUTFILE:XX=frm%
set OUT_FRMD=%OUTFILE:XX=frmd%
set IMG_RES=%OUTFILE:XX=imgr%

if %OUT_L%==%OUT_T% (
  echo %0: Outfile must include XX.
  exit /B 1
)

set MTHD=%7
if "%MTHD%"=="." set MTHD=
if "%MTHD%"=="" set MTHD=floor

set RESMTHD=%8
if "%RESMTHD%"=="." set RESMTHD=
if "%RESMTHD%"=="" set RESMTHD=aspect

set SHAD=%9
if "%SHAD%"=="" set SHAD=

if "%SHAD%"=="" (
  set sSHAD=
) else (
  set sSHAD=^( +clone -background Black -shadow %SHAD% ^) +swap -background None -layers Merge -layers Flatten
)



set FUNC=floor
if /I %MTHD%==exact (
  set EXTRA=0
  set FUNC=
) else if /I %MTHD%==floor (
  set EXTRA=0
) else if /I %MTHD%==round (
  set EXTRA=0.5
) else if /I %MTHD%==ceiling (
  set EXTRA=0.99999
) else (
  echo %0: Bad MTHD [%MTHD%]
  exit /B 1
)

for /F "usebackq" %%L in (`%IMG7%magick ^
  %INFILE% ^
  -format "ImgW=%%w\nImgH=%%h\n" ^
  info:`) do set %%L

:: In following, assume left and right will be rotated.
::
call :getdims %LFILE% LH LW
if ERRORLEVEL 1 exit /B 1

call :getdims %TFILE% TW TH
if ERRORLEVEL 1 exit /B 1

call :getdims %RFILE% RH RW
if ERRORLEVEL 1 exit /B 1

call :getdims %BFILE% BW BH
if ERRORLEVEL 1 exit /B 1

set sFMT=^
nT=%%[fx:%FUNC%((%ImgW%+%LW%+%RW%)/%TW%+(%Extra%))]\n^
nB=%%[fx:%FUNC%((%ImgW%+%LW%+%RW%)/%BW%+(%Extra%))]\n^
nL=%%[fx:%FUNC%((%ImgH%+%TH%+%BH%)/%LH%+(%Extra%))]\n^
nR=%%[fx:%FUNC%((%ImgH%+%TH%+%BH%)/%RH%+(%Extra%))]\n

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "%sFMT%" ^
  xc:`) do set %%L

echo %0: n:%nL% %nT% %nR% %nB%

set sFMT=^
FrmdW=%%[fx:floor(%TW%*%nT%+0.5)]\n^
FrmdH=%%[fx:floor(%LH%*%nL%+0.5)]\n

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "%sFMT%" ^
  xc:`) do set %%L

set /A WindW=%FrmdW%-%LW%-%RW%
set /A WindH=%FrmdH%-%TH%-%BH%

echo %0: WindW=%WindW% WindH=%WindH%

set sRESIMG=
set sRESFRM=
set ImgRW=%ImgW%
set ImgRH=%ImgH%
set FrmdRW=%FrmdW%
set FrmdRH=%FrmdH%
if /I %RESMTHD%==none (
  rem No resizing.
  echo No res
) else if /I %RESMTHD%==image (
  set sRESIMG=-resize "%WindW%x%WindH%^^^!"
) else if /I %RESMTHD%==aspect (
  set sRESIMG=-resize "%WindW%x%WindH%^^"
) else if /I %RESMTHD%==frame (

  set sFMT=^
FrmdRW=%%[fx:floor^(%FrmdW%*%ImgW%/%WindW%^)]\n^
FrmdRH=%%[fx:floor^(%FrmdH%*%ImgH%/%WindH%^)]\n

  for /F "usebackq" %%L in (`%IMG7%magick identify ^
    -format "!sFMT!" ^
    xc:`) do set %%L

  echo %0: FrmdRW=!FrmdRW! FrmdRH=!FrmdRH!

  set sRESFRM=-resize "!FrmdRW!x!FrmdRH!^^^!"
) else (
  echo %0: Bad RESMTHD [%RESMTHD%]
  exit /B 1
)

echo %0: sRESIMG %sRESIMG%
echo %0: sRESFRM %sRESFRM%

%IMG7%magick ^
  -size %FrmdW%x%TH% xc:None ^
  -tile %TFILE% -draw "color 0,0 reset" ^
  -write %OUT_T% ^
  +delete ^
  %LFILE% -rotate -90 -write mpr:TILE +delete ^
  -size %LW%x%FrmdH% xc:None ^
  -tile mpr:TILE -draw "color 0,0 reset" ^
  -write %OUT_L% ^
  +delete ^
  %RFILE% -rotate 90 -write mpr:TILE +delete ^
  -size %RW%x%FrmdH% xc:None ^
  -tile mpr:TILE -draw "color 0,0 reset" ^
  -write %OUT_R% ^
  +delete ^
  %BFILE% -rotate 180 -write mpr:TILE +delete ^
  -size %FrmdW%x%BH% xc:None ^
  -tile mpr:TILE -draw "color 0,0 reset" ^
  %OUT_B%

call %PICTBAT%mitre4 ^
  %OUT_L% %OUT_T% %OUT_R% %OUT_B% ^
  %OUT_FRM%

set needResize=0

if %WindW% GTR %ImgW% set needResize=1
if %WindH% GTR %ImgH% set needResize=1

set INFILE2=%INFILE%
if %needResize%==1 (
  echo %0: need resize.

  if not "%sRESIMG%" EQU "" (
    for /F "usebackq" %%L in (`%IMG7%magick ^
      %INFILE% ^
      %sRESIMG% ^
      +write %IMG_RES% ^
      -format "ImgRW=%%w\nImgRH=%%h\n" ^
      info:`) do set %%L
   set INFILE2=%IMG_RES%
  )

  if not "%sRESFRM%" EQU "" (
    for /F "usebackq" %%L in (`%IMG7%magick ^
      %OUT_FRM% ^
      %sRESFRM% ^
      +write %OUT_FRM% ^
      -format "WindW=%ImgW%\nWindH=%ImgH%\nLW=%%[fx:%LW%*w/%FrmdW%]\nTH=%%[fx:%TH%*h/%FrmdH%]\nFrmdW=%%w\nFrmdH=%%h\n" ^
      info:`) do set %%L
  )
)

%IMG7%magick ^
  %OUT_FRM% ^
  %sSHAD% ^
  %INFILE2% ^
  -geometry +%%[fx:%LW%+(%WindW%-%ImgRW%)/2]+%%[fx:%TH%+(%WindH%-%ImgRH%)/2] ^
  -compose DstOver -composite ^
  %OUT_FRMD%


call echoRestore

@endlocal & set pf_ImgW=%ImgW%& set pf_ImgH=%ImgH%& set pf_FrmdW=%FrmdW%& set pf_FrmdH=%FrmdH%& set pf_WindW=%WindW%& set pf_WindH=%WindH%

@exit /B 0


::------------------------------------------
:: Subroutines

:getdims

set %2=
for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "%2=%%w\n%3=%%H\n" ^
  %1`) do set %%L

if "!%2!"=="" exit /B 1

exit /B 0

mitre4.bat

rem Given %1 to %4 are left, top, right and bottom pieces for a frame,
rem creates output %5 with mitred corners.
rem The pieces will _not_ be rotated.

@if "%1"=="" findstr /B "rem @rem" %~f0 & exit /B 1

@setlocal enabledelayedexpansion

@call echoOffSave

set OUTFILE=%5
if "%OUTFILE%"=="" exit /B 1

for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "LW=%%w\nLH=%%H\n" ^
  %1`) do set %%L

call :getdims %1 LW LH
if ERRORLEVEL 1 exit /B 1

call :getdims %2 TW TH
if ERRORLEVEL 1 exit /B 1

call :getdims %3 RW RH
if ERRORLEVEL 1 exit /B 1

call :getdims %4 BW BH
if ERRORLEVEL 1 exit /B 1

if %LH% NEQ %RH% (
  echo %0: left and right have different heights
)

if %TW% NEQ %BW% (
  echo %0: top and bottom have different widths
)


%IMG7%magick ^
  -size %TW%x%LH% xc:None ^
  %1 -gravity West -compose Over -composite ^
  %3 -gravity East -compose Over -composite ^
  ( %2 ^
    ( +clone ^
      -fill Black -colorize 100 ^
      -fill White -draw "polygon 0,0 %%[fx:%TW%-1],0 %%[fx:%TW%-1-%RW%],%%[fx:%TH%-1] %%[fx:%LW%],%%[fx:%TH%-1]" ^
    ) ^
    -alpha off -compose CopyOpacity -composite ^
  ) -gravity North -compose Over -composite ^
  ( %4 ^
    ( +clone ^
      -fill Black -colorize 100 ^
      -fill White -draw "polygon 0,%%[fx:%BH%-1] %%[fx:%LW%],0 %%[fx:%BW%-1-%RW%],0 %%[fx:%BW%-1],%%[fx:%BH%-1]" ^
    ) ^
    -alpha off -compose CopyOpacity -composite ^
  ) -gravity South -compose Over -composite ^
  %OUTFILE%

call echoRestore

@endlocal

@exit /B 0

::------------------------------------------
:: Subroutines

:getdims

set %2=
for /F "usebackq" %%L in (`%IMG7%magick identify ^
  -format "%2=%%w\n%3=%%H\n" ^
  %1`) do set %%L

if "!%2!"=="" exit /B 1

exit /B 0

All images on this page were created by the commands shown, using:

%IM%identify -version
Version: ImageMagick 6.9.9-50 Q16 x64 2018-06-02 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Visual C++: 180040629
Features: Cipher DPC Modules OpenMP 
Delegates (built-in): bzlib cairo flif freetype gslib heic jng jp2 jpeg lcms lqr lzma openexr pangocairo png ps raw rsvg tiff webp xml zlib

To improve internet download speeds, some images may have been automatically converted (by ImageMagick, of course) from PNG to JPG.

Source file for this web page is fancyfr.h1. To re-create this web page, execute "procH1 fancyfr".


This page, including the images, is my copyright. Anyone is permitted to use or adapt any of the code, scripts or images for any purpose, including commercial use.

Anyone is permitted to re-publish this page, but only for non-commercial use.

Anyone is permitted to link to this page, including for commercial use.


Page version v1.0 7-February-2019.

Page created 03-Mar-2019 14:00:43.

Copyright © 2019 Alan Gibson.