VB6 Image Resizer

38 views
Skip to first unread message

GS

unread,
Mar 28, 2020, 12:26:11 AM3/28/20
to
Hi All,
I've been searching for ideas on how to manage batch resizing of images in a
VB6.exe explorer app, but not finding much. I have the explorer part already
done; - it has been working well for the past 10+ years. I need some guidance
with how to batch resize a selection of image files (all are JPG format) in the
Fileview pane. (It's configured to list files only)

Thanks in advance for all/any assistance!

--
Garry

Free usenet access at http://www.eternal-september.org
Classic VB Users Regroup!
comp.lang.basic.visual.misc
microsoft.public.vb.general.discussion

Mayayana

unread,
Mar 28, 2020, 10:04:49 AM3/28/20
to
"GS" <g...@v.invalid> wrote

| I've been searching for ideas on how to manage batch resizing of images in
a
| VB6.exe explorer app, but not finding much.

I don't know if this will be what you want, but
I've needed to do that in the past for an Explorer
Bar. I also did it with a script-based HTA image
viewer, but that one just uses IE to resize (which
works surprisingly well).

A couple of things:

https://www.jsware.net/jsware/vbcode.php5#jpgthumbs

That has two projects. One extracts JPG thumbnails.
The other is code for turbojpeg, the best, fastest method
I could find to resize very large JPSs in order to create
a thumbnail. For basic resizing of a bitmap... that's a whole
other topic.
My approach was trying to get a thumbnail as fast as
possible to display in the Bar when an image file is selected.
A thumbnail is clearly fastest, but not always available.
So I was using a combination of methods.

Here's a VBScript version that can extract either JPG or
RGB versions of JPG thumbnails. It could be adapted for
VB:

https://www.jsware.net/jsware/scrfiles.php5#jpginf

(Little known fact: Most thumbnails in JPGs are JPGs,
but they can also be BMPs or an obscure 3rd type. This
script can extract the first two.)

You can also do all sorts of things with WIA. The syntax
of functions is horrendous, but the help provides samples.
If I remember correctly, though, it's actually quite slow.
But here's my HTA image editor that uses it:

https://www.jsware.net/jsware/scrfiles.php5#wiaed

This is the gist of the resize function, assuming you
already have the objects set up.

Set ImProc = CreateObject("WIA.ImageProcess")

Set ImgFile = CreateObject("WIA.ImageFile")

'--clear out all filters:
While (ImProc.Filters.Count > 0): ImProc.Filters.Remove 1: Wend
'-- add the resize filter:
ImProc.Filters.Add ImProc.FilterInfos("Scale").FilterID
'-- set sizes:
ImProc.Filters(1).Properties("MaximumWidth") = SelWidth
ImProc.Filters(1).Properties("MaximumHeight") = SelHeight
ImProc.Filters(1).Properties("PreserveAspectRatio") = True
Set ImgFile = ImProc.Apply(ImgFile)

You can use that in VB. WIA can do a lot of things. But
MS seem to have put some kind of halfwit in charge of
designing and implementing it. I don't really get the point
of it, aside from the scanner interface it introduced.

You might also be able to somehow hijack whatever IE is
using to resize. It does a beautiful job, very fast. Since you're
re-inventing the wheel there's no reason not to reuse the rim. :)


Arne Saknussemm

unread,
Mar 28, 2020, 12:17:33 PM3/28/20
to
It was Sat, 28 Mar 2020 10:04:29 -0400 when
"Mayayana" <maya...@invalid.nospam> wrote:

> (Little known fact: Most thumbnails in JPGs are
> JPGs, but they can also be BMPs or an obscure 3rd
> type. This script can extract the first two.)

In file... yes, but when "rendered" (which also
includes rendered on a NON VISIBLE device context),
all images will be DIB images (Device Independed
Bitmaps) then they may be stored using whatever
format you want, but after you load them into a
windows DC (be it a memory area, a window, a
printer or whatever else) they'll always be DIB, this
eases the task of handling images since the APIs will
always use and deal with the same format, makes a lot
of sense, and should be taken into account

Mayayana

unread,
Mar 28, 2020, 2:13:00 PM3/28/20
to
"Arne Saknussemm" <es215.10...@spamgourmet.com> wrote

| > (Little known fact: Most thumbnails in JPGs are
| > JPGs, but they can also be BMPs or an obscure 3rd
| > type. This script can extract the first two.)
|
| In file... yes, but when "rendered" (which also
| includes rendered on a NON VISIBLE device context),
| all images will be DIB images (Device Independed
| Bitmaps) then they may be stored using whatever
| format you want, but after you load them into a
| windows DC (be it a memory area, a window, a
| printer or whatever else) they'll always be DIB, this
| eases the task of handling images since the APIs will
| always use and deal with the same format, makes a lot
| of sense, and should be taken into account
|

I'm not sure what your point is. I'm just talking about
getting thumbnails out of the JPG header. They're
in there as small files. Getting a thumbnail will always be
much faster than resizing to thumbnail, when a thumbnail
is available. So you extract the JPG thumbnail, then load
that into whatever image display.

But once you decide to do that you need to be aware
that while most tumbnails are JPG, they can actually be
3 formats. I wrote code to check the type and take out
a JPG thumbnail file or uncompressed RGB data. I've only
seen the latter once or twice. I've never seen the third type
and haven't written code to extract it.


Mayayana

unread,
Mar 28, 2020, 2:44:01 PM3/28/20
to
I maybe should have explained that: One can't
just extract a thumbnail and load it into a
picturebox. A JPG thumbnail is, in fact, an actual
file. But uncompressed RGB thumbnails are in reverse
byte order and have no header. So the bytes have
to be reversed. Then a header has to be created.
Only then do you have a file that can be loaded.

(I know there are top-down and bottom-up bitmaps.
I don't remember which is which. I just remember
that when I extracted the RGB bytes I had to store
them in the file in reverse.)

There are also other ways the thumbnail can be stored
in the header. As far as I know, only the JPG version
is stored as an entire file that can be rreconstituted by
simply writing the bytes to disk.


GS

unread,
Mar 28, 2020, 3:00:01 PM3/28/20
to
As usual, a very thorough response! Big thanks for this info; - I will look
into all of it.

More about my task:
A friend's wife is a hoarder of pics, and snap-happy as well. Problem is that
now she has twice filled up the HDD in her laptop to the point of crashing. She
has absolutely 100s of 1000s of pics. I decided to introduce her to an external
storage device for all data, leaving the laptop HDD for OS+apps only. So far so
good! I replaced the original HDD with an equal size SSD and now recovering all
her pics from the old HDD onto a 3.5" x 7200RPM external drive (1TB for now).

I found this app...

https://www.bricelam.net/ImageResizer/

..which is working to my liking in that it allows selecting pics in WE and lets
me configure output sizes. (Resizes 300+ pics in less than 10 seconds) Problem
is that it makes some larger not smaller, even though there's an option setting
to not do that.

Since I already have a working files explorer app I thought I'd just add one
more custom feature that does same. I'm replacing existing files with smaller
versions suitable for Web use, (640x480) for both orientations
(portrait/landscape). So far, Office Picture Manager is doing the best job but
it requires hands-on selection of the specific files (their thumbnails) to
resize. Very slow so I'm looking to duplicate what/how that ImageResizer app
does.

I found stuff to convert BMPs to JPG, and lots of .NET solutions but I'm not
looking to add this feature to my C# version of the files explorer app. I
already have a recursive routine to drill down thru subfolders so just
need/want to pass the filename to be resized along with its new dims.

Mayayana

unread,
Mar 28, 2020, 4:40:11 PM3/28/20
to
"GS" <g...@v.invalid> wrote
|
| ..which is working to my liking in that it allows selecting pics in WE and
lets
| me configure output sizes. (Resizes 300+ pics in less than 10 seconds)
Problem
| is that it makes some larger not smaller, even though there's an option
setting
| to not do that.

Ah. I thought you wanted to do something like an
Explorer thumbnail view. You could take a look
at this:

https://www.jsware.net/jsware/pprep.php5

I made it mostly for the woman I live with but
also sold it as shareware for a few years. You drop
a file or folder, then you can batch process a whole
folder with resizing and/or cropping. To accommodate
a large audience there are only 3 possible sizes, but
in the settings you can decide what those are. The
cropping was especially aimed at easily creating images
with the right ratio for various printing options, to
fit an 8x10 frame, a 3x5 print, etc.

If it's useful I'm happy to share whatever code you
might want. I think most of it uses some very nice
code from one Peter Scale, who wrote various
image operations in pure VB, including various resizing
methods.


GS

unread,
Mar 28, 2020, 5:57:12 PM3/28/20
to
Thank you!
I'll also look into this Peter Scale to see if any of his works are around...

GS

unread,
Mar 28, 2020, 6:44:36 PM3/28/20
to
Found nothing on Peter Scale yet but I'm very interested in the resizing code
you use!

Mayayana

unread,
Mar 28, 2020, 10:50:03 PM3/28/20
to
"GS" <g...@v.invalid> wrote

| Found nothing on Peter Scale yet but I'm very interested in the resizing
code
| you use!
|
I think this is it:

http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=46515&lngWId=1

I'll try to pull together my code tomorrow and
post it or post a link.


Arne Saknussemm

unread,
Mar 29, 2020, 9:48:27 AM3/29/20
to
It was Sat, 28 Mar 2020 14:43:48 -0400 when
"Mayayana" <maya...@invalid.nospam> wrote:

> I maybe should have explained that: One can't
> just extract a thumbnail and load it into a
> picturebox. A JPG thumbnail is, in fact, an actual
> file. But uncompressed RGB thumbnails are in reverse
> byte order and have no header. So the bytes have
> to be reversed. Then a header has to be created.
> Only then do you have a file that can be loaded.

My point is that Windows GDI APIs work on DIBs, those
are an in-memory representation of an image which may
then be stored to (or retrieved from) whatever
format, be it JPG, PNG or whatever, such formats are
just STORAGE formats, so instead of focusing on how a
given image is stored, it would be a better idea to
focus on how to quickly render it, then, to retrieve
the image from storage and decode it, there are a
bunch of libraries and APIs, so that shouldn't be a
big issue, nor it would be how the image is stored,
once you load and decode it from storage, you'll have
a DIB and that's all you need to show/manipulate it,
no need to worry about orientation/format/whatever


Mayayana

unread,
Mar 29, 2020, 10:41:32 AM3/29/20
to
"Arne Saknussemm" <es215.10...@spamgourmet.com> wrote

| My point is that Windows GDI APIs work on DIBs, those
| are an in-memory representation of an image which may
| then be stored to (or retrieved from) whatever
| format, be it JPG, PNG or whatever, such formats are
| just STORAGE formats, so instead of focusing on how a
| given image is stored

You seem to have misunderstood. We all know a raster
image is a bitmap. We all know about DIBs. We all know
raster image formats are various ways to store various kinds
of bitmaps. What I was talking about is the process of
getting thumbnails from a JPG.

Say you have a 12 MB JPG. To make a thumbnail of that
is difficult to do quickly. If you have 30 of them it's going
to be very slow. So if they have embedded thumbnails in
the EXIF data that will be a much quicker solution. Virtually
instant to process 30 files. Those thumbnails are typically
something like 160x100. But to extract them you have to
know how they're stored, and that can be in several formats.
So you read the EXIF header data, look for a thumbnail,
check the numeric flag that tells you what type it is, and
if it's a type you're prepared to extract then you extract
the bytes. If it's JPG then you'll have extracted a complete
JPG file. If it's uncompressed RGB data then you'll have
extracted the RGB bytes, but in reverse order and without
a header. Maybe you could paint that directly to screen without
building a BMP file? Possibly. Maybe a JPG thumbnail can also
be loaded into a Picture by bytes rather than file path? I
seem to remember there was a way to do that. But either way,
you need to know about the EXIF storage system in order to
get it out.




GS

unread,
Mar 29, 2020, 1:22:42 PM3/29/20
to
> "GS" <g...@v.invalid> wrote
>
>> Found nothing on Peter Scale yet but I'm very interested in the resizing
>> code you use!
>>
> I think this is it:
>
> http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=46515&lngWId=1
>
Got it; - will have a look-see shortly! Thanks...

> I'll try to pull together my code tomorrow and
> post it or post a link.

That would be great! Big thanks for your interest!

GS

unread,
Mar 29, 2020, 1:30:52 PM3/29/20
to
The .NET examples I've found seem to do this because when they Save the
modified image the format is passed as arg2. Since VB6 doesn't work with the
.NET Framework (or does it somehow?) I'm looking to duplicate in VB6!

GS

unread,
Mar 29, 2020, 1:54:14 PM3/29/20
to
So what I'm looking to do is this:

1. Loop thru a dir and grab each pic file to process;
Already got this working recursively!

Need way to do the following:
2. Determine orientation by examine w/h max to get new width;
This will be 480 if portrait, 640 if landscape.
Aspect ratio will auto-set height.

3. Save the pic file by overwriting existing;

Mayayana

unread,
Mar 29, 2020, 3:36:47 PM3/29/20
to
"GS" <g...@v.invalid> wrote

| Need way to do the following:
| 2. Determine orientation by examine w/h max to get new width;
| This will be 480 if portrait, 640 if landscape.
| Aspect ratio will auto-set height.
|
| 3. Save the pic file by overwriting existing;
|


Here's my image class:

https://www.jsware.net/Files2/imclass.zip

The code works like this:

Dim oPic As StdPicture
Dim ImOb As cImage, ImOb2 As cImage

Set oPic = LoadPicture(sPathSrc)
If (Err.Number = 0) Then
Set ImOb = New cImage
LRet = ImOb.CopyStdPicture(oPic)

'-------
sPathSrc is the path of the image file. So you
load a BMP/JPG/GIF into a StdPicture and then
copy that into the class. From there all sorts of
operations can be done, including resizing, cropping,
etc. and writing a new imahe to disk as BMP or JPG.
These were the most efficient methods I found.

There's also

https://www.jsware.net/Files2/resizetest.zip

That demonstrates that GDI+ resizing is faster than
VB methods using an Image control, with much
better picture quality. It's also slightly better
than WIA in terms of both speed and quality.

For sheer speed with large images I found jpegturbo
was the fastest resizer, by using the closest size
among the 8 (?) presets, then resizing that as needed.
Going from very big down to small is what takes so
much time.

Of course, none of this will handle PNG or TIF.
You'll need external libraries for that. I have code
for rendering PNG in VB, but not for saving as PNG.
And it's slow, anyway.


GS

unread,
Mar 29, 2020, 4:19:32 PM3/29/20
to
> "GS" <g...@v.invalid> wrote
>
>> Need way to do the following:
>> 2. Determine orientation by examine w/h max to get new width;
>> This will be 480 if portrait, 640 if landscape.
>> Aspect ratio will auto-set height.
>>
>> 3. Save the pic file by overwriting existing;
>>
>
>
> Here's my image class:
>
> https://www.jsware.net/Files2/imclass.zip
>
> The code works like this:
>
> Dim oPic As StdPicture
> Dim ImOb As cImage, ImOb2 As cImage
>
> Set oPic = LoadPicture(sPathSrc)
> If (Err.Number = 0) Then
> Set ImOb = New cImage
> LRet = ImOb.CopyStdPicture(oPic)
>
> '-------
> sPathSrc is the path of the image file. So you
> load a BMP/JPG/GIF into a StdPicture and then
> copy that into the class. From there all sorts of
> operations can be done, including resizing, cropping,
> etc. and writing a new imahe to disk as BMP or JPG.
> These were the most efficient methods I found.

Looks simple enough but was looking to NOT need an image control. I'll give
this a try regardless.
downloaded this earlier...
>
> That demonstrates that GDI+ resizing is faster than
> VB methods using an Image control, with much
> better picture quality. It's also slightly better
> than WIA in terms of both speed and quality.

This might be better suited so I'll play with...
>
> For sheer speed with large images I found jpegturbo
> was the fastest resizer, by using the closest size
> among the 8 (?) presets, then resizing that as needed.
> Going from very big down to small is what takes so
> much time.

Give the shear volume of pics, speed is important for sure! Some of the files
are 6-7mb (jpg camera pics) and others are page scans (jpg); - a few are web
source (png) but I'll show her how to SaveAs JPG.
>
> Of course, none of this will handle PNG or TIF.
> You'll need external libraries for that. I have code
> for rendering PNG in VB, but not for saving as PNG.
> And it's slow, anyway.

I'm using SnippingTool to save PNGs as JPGs; - doing this makes a much smaller
file, and there are very few in this format to begin with anyway!

Mayayana

unread,
Mar 29, 2020, 6:40:49 PM3/29/20
to
"GS" <g...@v.invalid> wrote

| > sPathSrc is the path of the image file. So you
| > load a BMP/JPG/GIF into a StdPicture and then
| > copy that into the class. From there all sorts of
| > operations can be done, including resizing, cropping,
| > etc. and writing a new imahe to disk as BMP or JPG.
| > These were the most efficient methods I found.
|
| Looks simple enough but was looking to NOT need an image control. I'll
give
| this a try regardless.

Not an image control. A class that provides an
image object to simplify operations. You load the
file into a StdPicture, then copy that to the image
*class*. Then you can resize, crop, write to disk
without further handling details.


GS

unread,
Mar 29, 2020, 6:49:14 PM3/29/20
to
Nice! Thanks for clarifying as I know absolutely nothing about working with
images. (I use a 3rd party utility for making icons after I design them and
screencapture to transparent GIFs)

Mayayana

unread,
Mar 29, 2020, 11:18:06 PM3/29/20
to
"GS" <g...@v.invalid> wrote

| Nice! Thanks for clarifying as I know absolutely nothing about working
with
| images. (I use a 3rd party utility for making icons after I design them
and
| screencapture to transparent GIFs)
|

If you don't ffigure it out I can provide more sample
code. But it's pretty basic from outside the class.
You create an instance from a StdPicture and then
use the functions of the class object to do whatever
you need. Internally it's as lean as I could get it.


GS

unread,
Mar 30, 2020, 7:44:28 AM3/30/20
to
Thanks for making it as simple as possible; - still, it'll take awhile for me
to digest! At first read I don't see where AspectRatio plays a part; - looks
like I need to specify both width and height. This could be problematic where
original size is not 3x4 or 4x3 AR, requiring lots of calc code. (Unfortunately
most of the pics are NOT either AR) The apps I'm using now auto-adjust
according to AR; - I need to duplicate that behavior so I only pass 1 dimension
based on orientation derived from the original pic! I could be wrong though!

Mayayana

unread,
Mar 30, 2020, 9:52:49 AM3/30/20
to
"GS" <g...@v.invalid> wrote

| Thanks for making it as simple as possible; - still, it'll take awhile for
me
| to digest! At first read I don't see where AspectRatio plays a part; -
looks
| like I need to specify both width and height. This could be problematic
where
| original size is not 3x4 or 4x3 AR, requiring lots of calc code.
(Unfortunately
| most of the pics are NOT either AR) The apps I'm using now auto-adjust
| according to AR; - I need to duplicate that behavior so I only pass 1
dimension
| based on orientation derived from the original pic! I could be wrong
though!
|

Wouldn't that just be a calculation? I didn't
understand your original note about getting ratio,
but I assumed were reducing the largest dimension
to 640. So then it's just arithmetic to get the small
dim as the same ratio.
smalldim = (bigdim \ 640) x orig small dim

I haven't looked at this stuff for awhile. I'm
wondering whether the class is really the best
thing. Maybe if I have a chance I'll try a test,
adding the VB Bilinear resize method to the resize
test I linked from online.

Some time ago we spent a long time dealing
with this. I was looking for the fastest thumbnail
generator. I finally ended up with turbojpeg +
minor resizing.

The class I have was for my photo editing program.

I assume you want decent quality. The resize test
I linked is interesting in that respect. It shows that
GDI+ is slightl faster than WIA and much faster than
resizing an image control. But it also shows quality
differences. I have a 12 MB image of maple trees.
GDI+ resizes well but blurs it slightly. VB image control
produces far too much contract, making the leaves look
like bright color dots. WIA is slightly inferior to GDI+.
So GDI+ is best. I expect it's probably also better
than StretchBlt, but I don't know for sure.

What I also don't know is how the speed and quality
of GDI+ resize compare to the straight VB math code
for bilinear and bicubic in the cImage class. But maybe
I'll try to add that into the test project.


Peter T

unread,
Mar 30, 2020, 12:30:03 PM3/30/20
to
"GS" <g...@v.invalid> wrote in message
> Hi All,
> I've been searching for ideas on how to manage batch resizing of images in
> a VB6.exe explorer app,

I haven't been all through the thread so this might have already have been
suggested -

https://www.mvps.org/emorcillo/en/code/vb6/thumbnail.shtml

I adapted it a long time ago for VBA in Excel to create thumbnails of all
images in the supplied folder and show them on a form (in a column in a
scrollable frame), click a thumbnail to show the full size image on the same
form. For your purposes wouldn't need to show them, as StdPicture objects
just 'SavePicture' back to file, obviously with a modified name if to same
folder.

Peter T


GS

unread,
Mar 30, 2020, 12:34:48 PM3/30/20
to
Thanks Peter, I'll look at this too. I'll be doing all my testing in Excel14
anyway since I already have a project going for doing other related tasks!

GS

unread,
Mar 30, 2020, 12:37:45 PM3/30/20
to
Sorry if I wasn't clear! Here's my goal:

Loop a folder of pics and process each as follows...
'get existing dims of each pic;
picWidth = Pic.Width: picHeight = Pic.Height
'determine orientation by testing width to height dims;
IsPortrait = (picHeight > picWidth): IsSquare = (picHeight = picWidth)

'resize accordingly
If IsSquare Then Call ResizeS
If IsPortrait Then Call ResizeP Else Call ResizeL
End If

..where these Resize?() functions will test if the pic is larger than desired
before modifying.

Camera pics are normally 3:4 AR and so should be straight forward; - it's the
self-sized ones that I'm concerned about. In this case there is a filesize
limit as well since these are scans of magazine pages, so I'll need to do
downsizing by percent as well.

GS

unread,
Mar 30, 2020, 1:35:25 PM3/30/20
to
I stumbled onto a post by Olaf stating RichClient5 has this capability built
into one of its DLLs. I'll look into that as well...

Mayayana

unread,
Mar 30, 2020, 6:20:55 PM3/30/20
to
Got to playing with different methods of resizing.
Hands down winner: GDI+ bicubic. Below is what you need. I'm afraid
it's in bits. One function opens a file and loads it, resizes, then
paints the bitmap. If you take the loading path and combine it
with the saving part from the second half of the second function,
you'll have a load, resize, save as JPG that's better than other
options. If you use bicubic (4 as interpolation mode) then you
get a beter image than bilinear in about the same time.


Public Enum gdiplusStatus '-- returned by gdip* methods
Ok = 0
GenericError = 1
InvalidParameter = 2
OutOfMemory = 3
ObjectBusy = 4
InsufficientBuffer = 5
NotImplemented = 6
Win32Error = 7
WrongState = 8
Aborted = 9
FileNotFound = 10
ValueOverflow = 11
AccessDenied = 12
UnknownImageFormat = 13
FontFamilyNotFound = 14
FontStyleNotFound = 15
NotTrueTypeFont = 16
UnsupportedGdiplusVersion = 17
GdiplusNotInitialized = 18
PropertyNotFound = 19
PropertyNotSupported = 20
ProfileNotFound = 21
End Enum

Private Type GdiplusStartupInput
GdiplusVersion As Long
DebugEventCallback As Long
SuppressBackgroundThread As Long
SuppressExternalCodecs As Long
End Type

Private Type RECTF
nLeft As Single
nTop As Single
nWidth As Single
nHeight As Single
End Type

Public Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(0 To 7) As Byte
End Type

Private Const UnitPixel As Long = 2

Public Enum InterpolationConstants
InterpolationModeDefault = 0
InterpolationModeLowQuality = 1
InterpolationModeHighQuality = 2
InterpolationModeBilinear = 3
InterpolationModeBicubic = 4
InterpolationModeNearestNeighbor = 5
InterpolationModeHighQualityBilinear = 6
InterpolationModeHighQualityBicubic = 7
End Enum

Private Declare Function GdiplusStartup Lib "GdiPlus" (token As Long,
inputbuf As GdiplusStartupInput, Optional ByVal outputbuf As Long = 0) As
Long
Private Declare Function GdipLoadImageFromFile Lib "GdiPlus.dll" (ByVal
mFilename As Long, ByRef mImage As Long) As Long
Private Declare Function GdipDeleteGraphics Lib "GdiPlus.dll" (ByVal
mGraphics As Long) As Long
Private Declare Function GdipCreateFromHDC Lib "GdiPlus" (ByVal hdc As Long,
hGraphics As Long) As Long
Private Declare Function GdipDisposeImage Lib "GdiPlus" (ByVal Image As
Long) As Long
Private Declare Sub GdiplusShutdown Lib "GdiPlus" (ByVal token As Long)
Private Declare Function GdipCreateBitmapFromHBITMAP Lib "GdiPlus.dll"
(ByVal hbm As Long, ByVal hPal As Long, ByRef pBitmap As Long) As Long
Private Declare Function GdipGetImageBounds Lib "GdiPlus.dll" (ByVal nImage
As Long, srcRect As RECTF, srcUnit As Long) As Long
Private Declare Function GdipSetInterpolationMode Lib "GdiPlus" (ByVal
hGraphics As Long, ByVal Interpolation As Long) As Long
Private Declare Function GdipDrawImageRectRectI Lib "GdiPlus" (ByVal
hGraphics As Long, ByVal hImage As Long _
, ByVal dstX As Long, ByVal dstY As Long, ByVal
dstWidth As Long, ByVal dstHeight As Long _
, ByVal SrcX As Long, ByVal SrcY As Long, ByVal
srcWidth As Long, ByVal srcHeight As Long _
, ByVal srcUnit As Long, Optional ByVal
imageAttributes As Long = 0 _
, Optional ByVal Callback As Long = 0, Optional
ByVal callbackData As Long = 0) As Long
Public Declare Function GdipSaveImageToFile Lib "GdiPlus" (ByVal Image As
Long, ByVal sFilePath As Long, clsidEncoder As GUID, encoderParams As Any)
As Long

'-- used with GDIP:
Public Declare Function CLSIDFromString Lib "ole32" (ByVal str As Long, id
As GUID) As Long



'--resize an image in proportion to original and paint.
Public Function ResizeAndPaintGDIP(FileName As String, DesthDC As Long,
DestW As Long, Interpolation As Long) As Long
Dim GDIsi As GdiplusStartupInput
Dim gToken As Long, hGraphics As Long, hBitmap As Long, LRet As Long
Dim ImRect As RECTF
Dim DestH As Long
On Error Resume Next
GDIsi.GdiplusVersion = 1&
LRet = GdiplusStartup(gToken, GDIsi)
If LRet <> 0 Then ResizeAndPaintGDIP = LRet: Exit Function

LRet = GdipCreateFromHDC(DesthDC, hGraphics)
If hGraphics <> 0 Then
LRet = GdipLoadImageFromFile(StrPtr(FileName), hBitmap)
If hBitmap <> 0 Then
LRet = GdipGetImageBounds(hBitmap, ImRect, UnitPixel)
LRet = GdipSetInterpolationMode(hGraphics, Interpolation)
DestH = CLng(ImRect.nHeight * (DestW / ImRect.nWidth))
LRet = GdipDrawImageRectRectI(hGraphics, hBitmap, 0, 0, DestW,
DestH, 0, 0, ImRect.nWidth, ImRect.nHeight, UnitPixel, 0&, 0&, 0&)
GdipDisposeImage hBitmap
ResizeAndPaintGDIP = LRet
End If
GdipDeleteGraphics hGraphics
End If
GdiplusShutdown gToken
End Function


Public Function WriteJPG(sPath As String, ByVal Quality As Long) As Long
'11-11
Dim hdc As Long, LRet As Long, LRet2 As Long
Dim hGDIPBitmap As Long 'handle to GDI+ bitmap.
Dim BBits() As Byte
Dim GUIDEncodeJPG As GUID
Dim EncodeParams As EncoderParameters
Const EncoderValueType As Long = &H4
On Error Resume Next

ReDim BBits(BMPInfo.bmiHeader.biHeight * BytesPerScanLine) As Byte
hdc = CreateCompatibleDC(0&)
LRet = GetDIBits(hdc, CurPic, 0, Abs(BMPInfo.bmiHeader.biHeight),
BBits(0), BMPInfo, DIB_RGB_COLORS)
LRet2 = DeleteDC(hdc)
If (LRet = 0) Then
WriteJPG = 1
Exit Function
End If
GdipCreateBitmapFromGdiDib BMPInfo, ByVal VarPtr(BBits(0)), hGDIPBitmap
If hGDIPBitmap <> 0 Then
CLSIDFromString StrPtr("{557CF401-1A04-11D3-9A73-0000F81EF32E}"),
GUIDEncodeJPG
' Initialize the encoder parameters
EncodeParams.Count = 1
With EncodeParams.Parameter ' Quality
CLSIDFromString
StrPtr("{1D5BE4B5-FA4A-452D-9CDD-5DB35105E7EB}"), .GUID
.NumberOfValues = 1
.Type = EncoderValueType
.Value = VarPtr(Quality)
End With
LRet = GdipSaveImageToFile(hGDIPBitmap, StrPtr(sPath),
GUIDEncodeJPG, EncodeParams)
GdipDisposeImage hGDIPBitmap ' Destroy the bitmap
Else
LRet = 2 'set error code.
End If

WriteJPG = LRet
End Function


GS

unread,
Mar 30, 2020, 6:50:26 PM3/30/20
to
Wow! I really appreciate your commitment to this!

I'll need time to figure out what it's doing before I can start using it,
though, since it's all 'Greek' to me. Not to mention so many variations of
how-to with GDI+!

"If you use bicubic (4 as interpolation mode) then you get a beter image than
bilinear in about the same time."

Does this mean I should also pass 7 for the Quality arg of WriteJPG?

GS

unread,
Mar 30, 2020, 6:56:44 PM3/30/20
to
> '--resize an image in proportion to original and paint.
> Public Function ResizeAndPaintGDIP(FileName As String, DesthDC As Long,
> DestW As Long, Interpolation As Long) As Long

Do I assume that DesthDC and DestW are my target sizes? How would I know this
prior to loading the image to find out existing size? (file details, perhaps?)

Mayayana

unread,
Mar 30, 2020, 9:24:48 PM3/30/20
to
"GS" <g...@v.invalid> wrote

|> '--resize an image in proportion to original and paint.
| > Public Function ResizeAndPaintGDIP(FileName As String, DesthDC As Long,
| > DestW As Long, Interpolation As Long) As Long
|
| Do I assume that DesthDC and DestW are my target sizes? How would I know
this
| prior to loading the image to find out existing size? (file details,
perhaps?)
|
No. DesthDC is destination DC. That's the handle
for painting, which you don't want. Maybe I can clean this
up, but I was doing it last minute today. (Now that I'm
home all day I don't seem to have a minute to spare!)

It's confusing because I had two functions. One is
opening a file and doing a resized paint to a picturebox.
The other is taking bitmap bits, getting an hBitmap
handle from that, and using the handle to save a JPG
to disk.
So a lot of what you want is here but the middle step,
resizing and ending up with an hBitmap, needs to be
done differently. I'll try to work on it tomorrow.


GS

unread,
Mar 30, 2020, 9:30:31 PM3/30/20
to
Big thanks; - much appreciated!

I'm working out getting file info via "Shell.Application"; - going well!

Mayayana

unread,
Mar 30, 2020, 10:02:47 PM3/30/20
to
"GS" <g...@v.invalid> wrote

| I'm working out getting file info via "Shell.Application"; - going well!
|

That happens to be another area of interest for me.
I might have something useful from my explorer bar code.


GS

unread,
Mar 30, 2020, 10:12:03 PM3/30/20
to
I have this part working well from another project that retrieves extended file
properties via GetDetailsOf().

Peter T

unread,
Mar 31, 2020, 5:59:49 AM3/31/20
to

"GS" <g...@v.invalid> wrote in message news:r5t735$3bm$1...@dont-email.me...
>> "GS" <g...@v.invalid> wrote in message
>>> Hi All,
>>> I've been searching for ideas on how to manage batch resizing of images
>>> in a VB6.exe explorer app,
>>
>> I haven't been all through the thread so this might have already have
>> been suggested -
>>
>> https://www.mvps.org/emorcillo/en/code/vb6/thumbnail.shtml
>>
>> I adapted it a long time ago for VBA in Excel to create thumbnails of all
>> images in the supplied folder and show them on a form (in a column in a
>> scrollable frame), click a thumbnail to show the full size image on the
>> same form. For your purposes wouldn't need to show them, as StdPicture
>> objects just 'SavePicture' back to file, obviously with a modified name
>> if to same folder.
>>
>> Peter T
>
> Thanks Peter, I'll look at this too. I'll be doing all my testing in
> Excel14 anyway since I already have a project going for doing other
> related tasks!
>

I should have mentioned, if you just want the job done rather than the fun
of doing it yourself, look at www.irfanview.com

File / Batch Conversion/Rename / Advanced / Set long side to # (pixels, cm
or inches)
You'll probably want to tick preserve aspect ratio and resample, dozens more
options on the advanced page, save with same date/time as the original is a
nice touch.
Back on the main page various ways to filter which images to process, and an
'include subfolders' option.

I've been using IrfanView since last century and still find new things it
can do, my all time favourite freeware app since ever, bar none! Hmm, I'm
well overdue a top-up donation:)

Peter T


GS

unread,
Mar 31, 2020, 9:43:53 AM3/31/20
to
I have it and so will play; - thank you very much!

GS

unread,
Mar 31, 2020, 12:53:28 PM3/31/20
to
BINGO! Big thanks, Peter!
Don't know why I didn't do this already; - probably because I really don't use
it much as I do MSO PictureManager.

Mayayana

unread,
Mar 31, 2020, 5:41:19 PM3/31/20
to
Garry,

I spent most of the day fiddling with this, trying to
paste together old code without it taking a lot of
time. I just didn't want to get into mixing GDI and
GDI+ to make it work.

But here's another solution I came up with in a few
minutes. It's a button sub from a test program.
You need a reference to Windows Image Acquisition 2.
(It may need to be installed on XP. I don't remember.)
Then you need the button, a text box maned TTime,
and the declare. It will process a 10 MB JPG in about
1/3 second. In my other tests of painting an image it
wasn't quite as nice or quite as fast as GDI+, but it's
not far behind.

It might need a little work. One image kept erroring with
"the parameter is incorrect" on the ImProc.Apply line.
I don't know why. Others worked fine. A 12 MB image took
344 ms to load, resize, and save as a new file. Yet Irfan
View said it took 297 ms just to load the image. So I guess
344 isn't bad.

'------------------------------------------------

Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Tick1 As Long, Tick2 As Long

Private Sub ButWIA2_Click()
Dim s1 As String
Dim W As Long, H As Long
Dim DestH As Long, DestW As Long, SrcW As Long, SrcH As Long, LSize As Long
Dim Img1 As ImageFile, Img2 As ImageFile
Dim ImProc As ImageProcess
TTime.Text = ""
Tick1 = GetTickCount
Set Img1 = New ImageFile
Set ImProc = New ImageProcess
Img1.LoadFile TPath.Text
LSize = 640
SrcW = Img1.Width
SrcH = Img1.Height
Debug.Print SrcW & " " & SrcH
If SrcW >= SrcH Then
DestW = LSize
DestH = SrcH * (DestW / SrcW)
Else
DestH = LSize
DestW = SrcW * (DestH / SrcH)
End If


While (ImProc.Filters.Count > 0)
ImProc.Filters.Remove 1
Wend
ImProc.Filters.Add ImProc.FilterInfos("Scale").FilterID
ImProc.Filters(1).Properties("MaximumWidth") = DestW
ImProc.Filters(1).Properties("MaximumHeight") = DestH
ImProc.Filters(1).Properties("PreserveAspectRatio") = True
Set Img1 = ImProc.Apply(Img1)

Dim s As String, Pt1 As Long
s = TPath.Text
Pt1 = InStrRev(s, ".")
s = Left$(s, (Pt1 - 1)) & "640.jpg"
Img1.SaveFile s

Set ImProc = Nothing
Set Img1 = Nothing

Tick2 = GetTickCount
TTime.Text = CStr(Tick2 - Tick1)

End Sub


GS

unread,
Mar 31, 2020, 5:52:04 PM3/31/20
to
Thank you so much for your time&effort; - I appreciate it immensely!

Irfanview seems to be doing a much better job 'all-in-1-app' than anything I've
tried so far. (800+ files in about 8 seconds)

I'll spend time with your solution (once I've gained some lost ground here) so
I can have my own custom feature[s].

Mayayana

unread,
Mar 31, 2020, 9:08:59 PM3/31/20
to
"GS" <g...@v.invalid> wrote

| Thank you so much for your time&effort; - I appreciate it immensely!
|
| Irfanview seems to be doing a much better job 'all-in-1-app' than anything
I've
| tried so far. (800+ files in about 8 seconds)
|
| I'll spend time with your solution (once I've gained some lost ground
here) so
| I can have my own custom feature[s].
|

OK. I'll leave it at that. You probably can't beat
Irfan View. It was nice to refresh my memory a bit,
though. I've got free time lately but haven't had
any interesting coding projects for awhile.


GS

unread,
Mar 31, 2020, 9:14:47 PM3/31/20
to
Ah, and I have the interest but no time lately!
Reply all
Reply to author
Forward
0 new messages