This is a fairly compact (the application itself is only ~ 53kB) application based on the Open Source FreeImage Project . It can resize to a new width or height, either by pixels or percent of the original, maintaining the aspect ratio. The enlargement uses a unique algorithm in which the resizing is reiterated in multiples of 10%, giving a better result than usual methods. It is particularly good in converting 72 or 96 dpi to 300 dpi suitable for printing.
It can also convert and save the image in a number of formats, with an option to just convert without resizing.
Since many cell phone images taken in portrait mode produce images on the side rather than upright, there is also a batch rotation to create the proper alignment. This is a separate process and must be done prior to the resizing.
Also a custom batch text watermark can be added if desired.
This is how the interface appears:
Click image for larger view
The application is based on Device Independent Bitmaps (DIB) used by FreeImage.
Below is the code, written in BCX:
#include <FreeImage.h>
$library <shell32.lib>
CONST sx = GetSystemMetrics(SM_CXSCREEN)
CONST sy = GetSystemMetrics(SM_CYSCREEN)
OPTION BASE 1
GUI “Form1”, PIXELS
DIM Form1 AS CONTROL
DIM Button[3] as CONTROL
DIM Check1 as CONTROL
DIM Check2 as CONTROL
DIM Check3 as CONTROL
DIM Check4 as CONTROL
DIM Group1 as CONTROL
DIM Edit1 as CONTROL
DIM Edit2 as CONTROL
DIM Combo1 as CONTROL
dim FormDC as HDC, ScreenDC as HDC, ScreenBitmap as HBITMAP, dib as HBITMAP, drw as boolean, wdnew as integer, htnew as integer, del as boolean, fil[10000] as string, cnt as long, rot as double, cmnd as boolean
Declare Function FreeImage_GetFileType Lib “FreeImage.dll” Alias “_FreeImage_GetFileType@8” (filname$, size) As FREE_IMAGE_FORMAT
Declare Function FreeImage_ColorQuantizeEx Lib “FREEIMAGE.DLL” Alias “_FreeImage_ColorQuantizeEx@20″(dib as HBITMAP, quantize as FREE_IMAGE_QUANTIZE , PaletteSize as Integer , ReserveSize as Integer, ReservePalette as RGBQUAD PTR ) as HBITMAP
Declare Function FreeImage_Load Lib “FreeImage.dll” Alias “_FreeImage_Load@12” ( fif , filname$, flags As Integer ) As HBITMAP
Declare Function FreeImage_Rescale Lib “FreeImage.dll” Alias “_FreeImage_Rescale@16” (dib as HBITMAP, dst_width,dst_height, filter as FREE_IMAGE_FILTER) as HBITMAP
Declare Function FreeImage_GetInfoHeader Lib “FreeImage.dll” Alias “_FreeImage_GetInfoHeader@4” (dib As HBITMAP) As BITMAPINFOHEADER PTR
Declare Function FreeImage_GetInfo Lib “FreeImage.dll” Alias “_FreeImage_GetInfo@4” (dib As HBITMAP) As BITMAPINFO PTR
Declare Function FreeImage_GetBits Lib “FreeImage.dll” Alias “_FreeImage_GetBits@4” (dib As HBITMAP) As LPBYTE
Declare Function FreeImage_GetWidth Lib “FREEIMAGE.DLL” Alias “_FreeImage_GetWidth@4” (dib as HBITMAP)
Declare Function FreeImage_GetHeight Lib “FREEIMAGE.DLL” Alias “_FreeImage_GetHeight@4” (dib as HBITMAP)
Declare Function FreeImage_Save Lib “FreeImage.dll” Alias “_FreeImage_Save@16” (fif as FREE_IMAGE_FORMAT, dib As HBITMAP, filname$, flags as Integer) as Boolean
Declare Sub FreeImage_Unload Lib “FreeImage.dll” Alias “_FreeImage_Unload@4” (dib As HBITMAP)
Declare Function FreeImage_ConvertTo24Bits Lib “FREEIMAGE.DLL” Alias “_FreeImage_ConvertTo24Bits@4” (dib as HBITMAP) as HBITMAP
Declare Function FreeImage_RotateClassic Lib “FreeImage.dll” Alias “_FreeImage_RotateClassic@12” (dib as HBITMAP, Angle As Double) As HBITMAPSUB FORMLOAD
dim B[8] as string, j%, edittxt as string
rot = 0
Form1 = BCX_FORM(“Form1”, 0, 0, 0.3*sx, 0.3*sy)
Group1 = BCX_GROUP(“”, Form1, 100, .01*sx, .02*sy, .05*sx, .10*sy, WS_GROUP | WS_CHILD | WS_VISIBLE | BS_GROUPBOX)
Button[0] = BCX_RADIO(“Width”,Form1, 101, .012*sx,.03*sy,.045*sx,.03*sy)
Button[1] = BCX_RADIO(“Height”,Form1, 102, .012*sx,.055*sy,.045*sx,.03*sy)
Button[2] = BCX_RADIO(“Convert”,Form1, 104, .012*sx,.080*sy,.045*sx,.03*sy)
Check1 = BCX_CHECKBOX(“Watermark”,Form1, 106, .01*sx,.12*sy,.1*sx,.03*sy)
Check3 = BCX_CHECKBOX(“Delete Original”,Form1, 108, .01*sx,.145*sy,.1*sx,.03*sy)
Check4 = BCX_CHECKBOX(“Show Folder”,Form1, 109, .01*sx,.170*sy,.1*sx,.03*sy)
Edit2 = BCX_EDIT(“Rotation Angle eg: 90”,Form1, 110,.01*sx,.200*sy,.083*sx,.03*sy)
Edit1 = BCX_EDIT(“Value Pixels or %”,Form1, 103,.064*sx,.029*sy,.083*sx,.03*sy)
Check2 = BCX_CHECKBOX(“%”,Form1, 107, .15*sx,.029*sy,.05*sx,.03*sy)
Combo1 = BCX_COMBOBOX(“”,Form1,105,.18*sx,.028*sy,.075*sx,.15*sy)
Send Message(Check4,BM_SETCHECK,1,0)
B$[0] = “JPG”
B$[1] = “GIF”
B$[2] = “PNG”
B$[3] = “BMP”
B$[4] = “TIF”
B$[5] = “TGA”
for j% = 0 to 5
SendMessage(Combo1, CB_ADDSTRING, 0, B[j%])
next
if len(command$) > 4 then cmnd = true
edittxt = “JPG”
SendMessage(Combo1,WM_SETTEXT,0, edittxt)CENTER(Form1)
SHOW(Form1)
SetFocus(Edit1)
SendMessage(Edit1,EM_SETSEL,0, -1)
END SUB
BEGIN EVENTS
SELECT CASE CBMSG
case WM_LBUTTONUP
dim sav$
dim meas$, c as long, var as double, newname[10000] as string, buf$, fif as FREE_IMAGE_FORMAT, ending as string, t as FREE_IMAGE_FORMAT, bmiHeader as BITMAPINFOHEADER, bi as BITMAPINFO, strText$,txtColor as COLORREF, hfont as HFONT
dim filname as string * 10000000, direc as string, a as long, b as long, dib as HBITMAP, wd as double, ht as double, fname$, tmpstr$, iter%, rep%
! LPBYTE pBits;if cmnd then
filname = command$
cmnd = false
a = instr(filname,chr$(34), 2) – 1
direc = mid$(filname, 2, a – 2)
a = instrrev(direc, “\”) – 1
direc = mid$(direc, 1, a )
filname = replace$(filname, direc, “”)
filname = replace$(filname, chr$(34) & “\”, “”)
filname = replace$(filname, chr$(34) & ” “, “,”)
filname = direc & “,” & replace(filname, chr$(34), “”)
else
filname = GETFILENAME$(“Open”,”Picture Files|*.bmp;*.jpg;*.gif;*.png;*.tif|BMP Files|*.bmp|JPG Files|*.jpg|GIF Files|*.gif|PNG FILES|*.png|All Files|*.*”,0, Form1 ,OFN_EXPLORER | OFN_ALLOWMULTISELECT )
end ifif instr(filname, “,”) = 0 then
a = instrrev(filname, “\”) – 1
fname = trim$(right$(filname, len(filname) – a – 1))
filname = trim$(left$(filname, a)) & “,”
filname = filname & fname
end if
a = instr(filname, “,”)- 1
direc = trim$(left$(filname, a) & “\”)
if bcx_get_text$(Edit2) != “Rotate,90,180,270” then
rot = val(bcx_get_text$(Edit2))
end if
if rot = 0 then
tmpstr$ = direc & “Resize”
if not exist(tmpstr$ & “\file.fil”) then
MKDIR tmpstr$
open tmpstr$ & “\file.fil” for output as hf
close hf
end if
end if
meas$ = BCX_GET_TEXT$(Edit1)
SendMessage(Combo1,WM_GETTEXT,4, buf$)
BCX_CURSOR(IDC_WAIT )
if SendMessage(Check3,BM_GETCHECK,0,0) = 1 then del = true
if SendMessage(Check1,BM_GETCHECK,0,0) = 1 then
drw = true
strText$ = inputbox$(“Text”,”What is the Text?”,””)
if strText$ = “” then function = 0
bcx_fontdlg
txtColor = bcx_colordlg
hfont = BCX_SET_FONT(BCX_Font.Name$, BCX_Font.Size, BCX_Font.Bold, BCX_Font.Italic, BCX_Font.Underline, BCX_Font.Strikeout)
end if
do while a <> 0
b = a + 1
cnt = cnt + 1
a = instr(filname, “,”, b)
newname[cnt] = mid$(filname, b, a – b)
newname[cnt] = left$(newname[cnt],len(newname[cnt]) – 3)
fil[cnt] = trim$(direc & mid$(filname, b, a – b))
if len(fil[cnt]) > len(direc) then
dib = FreeImage_Load(FreeImage_GetFileType(fil[cnt], 0), fil[cnt], 0)
wd = cdbl(FreeImage_GetWidth(dib))
ht = cdbl(FreeImage_GetHeight(dib))
wdnew = wd
htnew = ht
if SendMessage(Button[0],BM_GETCHECK,0,0) = 1 then
if SendMessage(Check2,BM_GETCHECK,0,0) = 1 then
wdnew = INT((val(meas$) /100) * wd )
if wdnew > wd then
iter% = log(wdnew / wd) / log(1.1)
end if
else
iter% = 0
wdnew = val(meas$)
end if
if SendMessage(Check2,BM_GETCHECK,0,0) = 0 and wdnew > wd then
iter% = log(wdnew / wd) / log(1.1)
else
iter% = 0
end if
var = cdbl(ht/wd)
var = wdnew * var
htnew = INT(var)
elseif SendMessage(Button[1],BM_GETCHECK,0,0) = 1 thenif SendMessage(Check2,BM_GETCHECK,0,0) = 1 then
htnew = INT(val(meas$) /100 * ht )
if htnew > ht then
iter% = log(htnew / ht) / log(1.1)
end if
else
iter% = 0
htnew = val(meas$)
end ifif SendMessage(Check2,BM_GETCHECK,0,0) = 0 and htnew > ht then
iter% = log(htnew / ht) / log(1.1)
else
iter% = 0
end ifvar = cdbl(wd/ht)
var = htnew * var
wdnew = INT(var)
end ifif iter% > 0 then
wdnew = wd
htnew = ht
for rep% = 1 to iter%
wdnew = 1.1 * wdnew
htnew = 1.1 * htnew
dib = FreeImage_Rescale(dib,wdnew, htnew, FILTER_CATMULLROM)
next
elseif SendMessage(Check1,BM_GETCHECK,0,0) <> 1 and iter% = 0 and rot = 0 then
dib = FreeImage_Rescale(dib,wdnew, htnew, FILTER_CATMULLROM)
end if
if rot <> 0 then dib = FreeImage_RotateClassic(dib, rot)if SendMessage(Check1,BM_GETCHECK,0,0) = 1 then
GLOBAL rcTxt as RECT,sze as SIZE
BCX_CURSOR(IDC_WAIT )
FormDC = GetDC(Form1)
ScreenDC = CreateCompatibleDC(FormDC)
ScreenBitmap = CreateCompatible Bitmap(FormDC,wd,ht)
SelectObject(ScreenDC,ScreenBitmap)
bmiHeader = *FreeImage_GetInfoHeader(dib)
bi = *FreeImage_GetInfo(dib)
pBits = FreeImage_GetBits(dib)
SetStretchBltMode(ScreenDC,HALFTONE)
SetBrushOrgEx(ScreenDC,0,0,0)
if StretchDIBits(ScreenDC,0,0,wd,ht,0,0,wd,ht,pBits,&bi,0,SRCCOPY) = 0 then msgbox str$(GetLastError())
SelectObject(ScreenDC,hfont)
GetTextExtentPoint32(ScreenDC,strText$,len(strText$),&sze)rcTxt.left = 30
rcTxt.top = 30
rcTxt.right = 1.05*(30 + sze.cx)
rcTxt.bottom = 30 + sze.cy
SetBkMode(ScreenDC,1)
SetTextColor(ScreenDC,txtColor)
DrawText(ScreenDC,strText$,-1,&rcTxt,DT_LEFT)
savebmp(ScreenDC,appexepath$ & “tmp.bmp”)
FreeImage_Unload(dib)
DeleteObject(ScreenBitmap)
DeleteDC(ScreenDC)
dib = FreeImage_Load(FIF_BMP,appexepath$ & “tmp.bmp”,0)
if wdnew <> wd then dib = FreeImage_Rescale(dib,wdnew, htnew, FILTER_CATMULLROM)
ScreenDC = CreateCompatibleDC(FormDC)
ScreenBitmap = CreateCompatible Bitmap(FormDC,wdnew,htnew)
SelectObject(ScreenDC,ScreenBitmap)
bmiHeader = *FreeImage_GetInfoHeader(dib)
bi = *FreeImage_GetInfo(dib)
pBits = FreeImage_GetBits(dib)
SetStretchBltMode(ScreenDC,HALFTONE)
SetBrushOrgEx(ScreenDC,0,0,0)
if StretchDIBits(ScreenDC,0,0,wdnew,htnew,0,0,wdnew,htnew,pBits,&bi,0,
SRCCOPY) = 0 then msgbox str$(GetLastError())
end if
buf$ = UCASE$(buf$)
if buf$ = “JPG” then
fif = FIF_JPEG
ending = “jpg”
elseif buf$ = “GIF” then
dim ReservePalette as RGBQUAD
dib = FreeImage_ColorQuantizeEx(dib,FIQ_WUQUANT,256,0, &ReservePalette)
fif = FIF_GIF
ending = “gif”
elseif buf$ = “PNG” then
fif = FIF_PNG
ending = “png”
elseif buf$ = “BMP” then
fif = FIF_BMP
ending = “bmp”
elseif buf$ = “TIF” then
fif = FIF_TIFF
ending = “tif”
elseif buf$ = “TGA” then
fif = FIF_TARGA
ending = “tga”
end if
if fif != FIF_GIF then dib = FreeImage_ConvertTo24Bits(dib)
if rot = 0 then sav$ = tmpstr$ & “\” & newname[cnt] & ending
if rot <> 0 then sav$ = direc & “\” & newname[cnt] & “_Rotated.” & ending
FreeImage_Save(fif, dib, sav$, 85)
end if
FreeImage_Unload(dib)
loop
BCX_CURSOR(IDC_APPSTARTING )
if SendMessage(Check4,BM_GETCHECK,0,0) = 1 then
ShellExecute(Form1, “explore”, tmpstr$, 0, 0, SW_SHOWNORMAL)
elseif not exist(tmpstr$) then
msgbox “Unable to open folder”
end if
SendMessage(Form1, WM_CLOSE, 0,0)
case WM_CLOSE
dim i as long
if dib <> 0 then FreeImage_Unload(dib)
DeleteObject(ScreenBitmap)
DeleteDC(ScreenDC)
ReleaseDC(Form1, FormDC)
kill appexepath$ & “tmp.bmp”
if del then
for i = 1 to cnt
kill fil[i]
next
end if
END SELECT
END EVENTS
FreeImage.dll must be imported #include <FreeImage.h> and needed functions declared. The interface is then created:
Form1 = BCX_FORM(“Form1”, 0, 0, 0.3*sx, 0.3*sy)
Group1 = BCX_GROUP(“”, Form1, 100, .01*sx, .02*sy, .05*sx, .10*sy, WS_GROUP | WS_CHILD | WS_VISIBLE | BS_GROUPBOX)
Button[0] = BCX_RADIO(“Width”,Form1, 101, .012*sx,.03*sy,.045*sx,.03*sy)
Button[1] = BCX_RADIO(“Height”,Form1, 102, .012*sx,.055*sy,.045*sx,.03*sy)
Button[2] = BCX_RADIO(“Convert”,Form1, 104, .012*sx,.080*sy,.045*sx,.03*sy)
Check1 = BCX_CHECKBOX(“Watermark”,Form1, 106, .01*sx,.12*sy,.1*sx,.03*sy)
Check3 = BCX_CHECKBOX(“Delete Original”,Form1, 108, .01*sx,.145*sy,.1*sx,.03*sy)
Check4 = BCX_CHECKBOX(“Show Folder”,Form1, 109, .01*sx,.170*sy,.1*sx,.03*sy)
Edit2 = BCX_EDIT(“Rotation Angle eg: 90”,Form1, 110,.01*sx,.200*sy,.083*sx,.03*sy)
Edit1 = BCX_EDIT(“Value Pixels or %”,Form1, 103,.064*sx,.029*sy,.083*sx,.03*sy)
Check2 = BCX_CHECKBOX(“%”,Form1, 107, .15*sx,.029*sy,.05*sx,.03*sy)
Combo1 = BCX_COMBOBOX(“”,Form1,105,.18*sx,.028*sy,.075*sx,.15*sy)
Send Message(Check4,BM_SETCHECK,1,0)
B$[0] = “JPG”
B$[1] = “GIF”
B$[2] = “PNG”
B$[3] = “BMP”
B$[4] = “TIF”
B$[5] = “TGA”
for j% = 0 to 5
SendMessage(Combo1, CB_ADDSTRING, 0, B[j%])
next
if len(command$) > 4 then cmnd = true
edittxt = “JPG”
SendMessage(Combo1,WM_SETTEXT,0, edittxt)
CENTER(Form1)
SHOW(Form1)
SetFocus(Edit1)
SendMessage(Edit1,EM_SETSEL,0, -1)
An array with image extensions is used to populate the combobox:
SendMessage(Combo1, CB_ADDSTRING, 0, B[j%])
A single file can be resized by right clicking its icon or multiple files can be selected from a file dialog
if cmnd then
filname = command$
cmnd = false
a = instr(filname,chr$(34), 2) – 1
direc = mid$(filname, 2, a – 2)
a = instrrev(direc, “\”) – 1
direc = mid$(direc, 1, a )
filname = replace$(filname, direc, “”)
filname = replace$(filname, chr$(34) & “\”, “”)
filname = replace$(filname, chr$(34) & ” “, “,”)
filname = direc & “,” & replace(filname, chr$(34), “”)
else
filname = GETFILENAME$(“Open”,”Picture Files|*.bmp;*.jpg;*.gif;*.png;*.tif|BMP Files|*.bmp|JPG Files|*.jpg|GIF Files|*.gif|PNG FILES|*.png|All Files|*.*”,0, Form1 ,OFN_EXPLORER | OFN_ALLOWMULTISELECT )
end if
The output is saved in a subdirectory named Resize, so the directory must be extracted from the filenames:
direc = trim$(left$(filname, a) & “\”)
if rot = 0 then
tmpstr$ = direc & “Resize”
if not exist(tmpstr$ & “\file.fil”) then
MKDIR tmpstr$
open tmpstr$ & “\file.fil” for output as hf
close hf
end if
end if
If rotation is desired the value is set in a text box:
if bcx_get_text$(Edit2) != “Rotate,90,180,270” then
rot = val(bcx_get_text$(Edit2))
end if
If a watermark is desired then the text is created:
if SendMessage(Check1,BM_GETCHECK,0,0) = 1 then
drw = true
strText$ = inputbox$(“Text”,”What is the Text?”,””)
if strText$ = “” then function = 0
bcx_fontdlg
txtColor = bcx_colordlg
hfont = BCX_SET_FONT(BCX_Font.Name$, BCX_Font.Size, BCX_Font.Bold, BCX_Font.Italic, BCX_Font.Underline, BCX_Font.Strikeout)
end if
The string obtained from the file dialog is parsed, the dibs created and the dimensions determined by looping:
do while a <> 0
b = a + 1
cnt = cnt + 1
a = instr(filname, “,”, b)
newname[cnt] = mid$(filname, b, a – b)
newname[cnt] = left$(newname[cnt],len(newname[cnt]) – 3)
fil[cnt] = trim$(direc & mid$(filname, b, a – b))
if len(fil[cnt]) > len(direc) then
dib = FreeImage_Load(FreeImage_GetFileType(fil[cnt], 0), fil[cnt], 0)
wd = cdbl(FreeImage_GetWidth(dib))
ht = cdbl(FreeImage_GetHeight(dib))
wdnew = wd
htnew = ht
If the resizing is by width and the new width is increased the number of iterations is calculated and the resizing is done by looping:
if SendMessage(Check2,BM_GETCHECK,0,0) = 0 and wdnew > wd then
iter% = log(wdnew / wd) / log(1.1)
for rep% = 1 to iter%
wdnew = 1.1 * wdnew
htnew = 1.1 * htnew
dib = FreeImage_Rescale(dib,wdnew, htnew, FILTER_CATMULLROM)
next
The same would be true if the resizing were by height, only the height values would be used.
If the resizing is by width, the aspect ratio is calculated and the new height determined:
var = cdbl(ht/wd)
var = wdnew * var
htnew = INT(var)
The desired file ending is determined:
buf$ = UCASE$(buf$)
if buf$ = “JPG” then
fif = FIF_JPEG
ending = “jpg”
elseif buf$ = “GIF” then
dim ReservePalette as RGBQUAD
dib = FreeImage_ColorQuantizeEx(dib,FIQ_WUQUANT,256,0, &ReservePalette)
fif = FIF_GIF
ending = “gif”
If a gif is desired the dib must be paletted.
Finally the image is saved in the desired format and the dib unloaded to regain memory:
if fif != FIF_GIF then dib = FreeImage_ConvertTo24Bits(dib)
if rot = 0 then sav$ = tmpstr$ & “\” & newname[cnt] & ending
if rot <> 0 then sav$ = direc & “\” & newname[cnt] & “_Rotated.” & ending
FreeImage_Save(fif, dib, sav$, 85)
end if
FreeImage_Unload(dib)
The conversion to dibs makes the resizing much faster than working with bitmaps.
You can download the latest update from the following source .
Click Downloads and download BWM6.zip