Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Making picture smaller ends in bad quality

353 views
Skip to first unread message

Mauro

unread,
Jan 13, 2004, 7:51:19 AM1/13/04
to

Hi all,

when I load an bitmap 80x80 and make it smaller with my TImage
component by setting a smaller width and height to 20, the
quality is not good. Is there any way to improve it?

I mean that graphic programs do it also without problems, so I
think that somebody has piece of coding that helps to make a
picture smaller without loosing quality.

Thanks in advance,
Mauro

Damon Chandler (TeamB)

unread,
Jan 16, 2004, 12:59:37 AM1/16/04
to
Hi Mauro,

> when I load an bitmap 80x80 and make it smaller with my TImage
> component by setting a smaller width and height to 20, the
> quality is not good. Is there any way to improve it?

Try using the CopyImage() Win32 function to resize the bitmap...

http://tinyurl.com/ytykl

You'd assign the returned handle to Image->Picture->Bitmap->Handle.

Good luck,
Damon (TeamB)

Mauro

unread,
Jan 16, 2004, 4:52:29 AM1/16/04
to

Thanks for your suggestion,
but it does not work...

C++ Builder seems to use this already to stretch the
pictures, so the result is the same...

regards,
Mauro

Damon Chandler (TeamB)

unread,
Jan 16, 2004, 12:58:17 PM1/16/04
to
Mauro,

> Thanks for your suggestion,
> but it does not work...
> C++ Builder seems to use this already to stretch the
> pictures, so the result is the same...

Setting the Stretch property ultimately calls StretchBlt(), which
typically performs pixel replication. Are you using a true color image
(i.e., what's the PixelFormat of the Bitmap)? CopyImage() won't
interpolate if the image uses a color table.

Damon (TeamB)

Fishface

unread,
Jan 16, 2004, 2:49:10 PM1/16/04
to
Mauro wrote:
> Thanks for your suggestion,
> but it does not work...
>
> C++ Builder seems to use this already to stretch
> the pictures, so the result is the same...

In other applications, when making thumbnails, I run
a sharpen filter on the resized image (with good result).
If you look around, I think you can find something.


Damon Chandler (TeamB)

unread,
Jan 16, 2004, 5:43:57 PM1/16/04
to
> Mauro,
> > Thanks for your suggestion,
> > but it does not work...
> > C++ Builder seems to use this already to stretch the
> > pictures, so the result is the same...
>
> CopyImage() won't interpolate if the image uses a color table.

If you're finding that the shrunken image still contains aliasing, you
might have better results using bicubic versus CopyImage()'s bilinear
interpolation. Here's some code to do bicubic interpolation...

inline BYTE clip(int x)
{
if (x > 255) return 255;
if (x < 0) return 0;
return x;
}

inline float get_interp_weight(float x)
{
float v1 = x + 2.0f;
float v2 = x + 1.0f;
float v3 = x;
float v4 = x - 1.0f;

v1 = (v1 > 0.0f) ? v1*v1*v1 : 0.0f;
v2 = (v2 > 0.0f) ? 4.0f*v2*v2*v2 : 0.0f;
v3 = (v3 > 0.0f) ? 6.0f*v3*v3*v3 : 0.0f;
v4 = (v4 > 0.0f) ? 4.0f*v4*v4*v4 : 0.0f;

return (v1 - v2 + v3 - v4);
}

void ResizeBitmap(
Graphics::TBitmap& DstBitmap,
Graphics::TBitmap& SrcBitmap
)
{
SIZE const dst_size = {DstBitmap.Width, DstBitmap.Height};
SIZE const src_size = {SrcBitmap.Width, SrcBitmap.Height};

float const ratio_cx =
static_cast<float>(src_size.cx) /
static_cast<float>(dst_size.cx);
float const ratio_cy =
static_cast<float>(src_size.cy) /
static_cast<float>(dst_size.cy);

float dx, dy;
float x_src_f, y_src_f;
int x_src, y_src;

float weight;
float weight_x0, weight_x1, weight_x2, weight_x3;
float weight_y0, weight_y1, weight_y2, weight_y3;

RGBTRIPLE* p_dst_row;
RGBTRIPLE const* p_src_row0;
RGBTRIPLE const* p_src_row1;
RGBTRIPLE const* p_src_row2;
RGBTRIPLE const* p_src_row3;

float r_val, g_val, b_val;

for (int y_dst = 0; y_dst < dst_size.cy; ++y_dst)
{
p_dst_row = static_cast<RGBTRIPLE*>(DstBitmap.ScanLine[y_dst]);

y_src_f = y_dst * ratio_cy;
y_src = y_src_f;
dy = y_src_f - y_src;
y_src = (y_src < 1) ? 1 :
(y_src >= src_size.cy - 2) ? src_size.cy - 3 : y_src;

weight_y0 = get_interp_weight(dy + 1) / 36.0f;
weight_y1 = get_interp_weight(dy) / 36.0f;
weight_y2 = get_interp_weight(dy - 1) / 36.0f;
weight_y3 = get_interp_weight(dy - 2) / 36.0f;

p_src_row0 =
static_cast<RGBTRIPLE*>(SrcBitmap.ScanLine[y_src - 1]);
p_src_row1 =
static_cast<RGBTRIPLE*>(SrcBitmap.ScanLine[y_src]);
p_src_row2 =
static_cast<RGBTRIPLE*>(SrcBitmap.ScanLine[y_src + 1]);
p_src_row3 =
static_cast<RGBTRIPLE*>(SrcBitmap.ScanLine[y_src + 2]);

for (int x_dst = 0; x_dst < dst_size.cx; ++x_dst)
{
x_src_f = x_dst * ratio_cx;
x_src = x_src_f;
dx = x_src_f - x_src;
x_src = (x_src < 1) ? 1 :
(x_src >= src_size.cx - 2) ? src_size.cx - 3 : x_src;

weight_x0 = get_interp_weight(-1 - dx);
weight_x1 = get_interp_weight(-dx);
weight_x2 = get_interp_weight(1 - dx);
weight_x3 = get_interp_weight(2 - dx);

r_val = 0.0f, g_val = 0.0f, b_val = 0.0f;

weight = weight_x0 * weight_y0;
r_val += p_src_row0[x_src - 1].rgbtRed * weight;
g_val += p_src_row0[x_src - 1].rgbtGreen * weight;
b_val += p_src_row0[x_src - 1].rgbtBlue * weight;
weight = weight_x1 * weight_y0;
r_val += p_src_row0[x_src].rgbtRed * weight;
g_val += p_src_row0[x_src].rgbtGreen * weight;
b_val += p_src_row0[x_src].rgbtBlue * weight;
weight = weight_x2 * weight_y0;
r_val += p_src_row0[x_src + 1].rgbtRed * weight;
g_val += p_src_row0[x_src + 1].rgbtGreen * weight;
b_val += p_src_row0[x_src + 1].rgbtBlue * weight;
weight = weight_x3 * weight_y0;
r_val += p_src_row0[x_src + 2].rgbtRed * weight;
g_val += p_src_row0[x_src + 2].rgbtGreen * weight;
b_val += p_src_row0[x_src + 2].rgbtBlue * weight;

weight = weight_x0 * weight_y1;
r_val += p_src_row1[x_src - 1].rgbtRed * weight;
g_val += p_src_row1[x_src - 1].rgbtGreen * weight;
b_val += p_src_row1[x_src - 1].rgbtBlue * weight;
weight = weight_x1 * weight_y1;
r_val += p_src_row1[x_src].rgbtRed * weight;
g_val += p_src_row1[x_src].rgbtGreen * weight;
b_val += p_src_row1[x_src].rgbtBlue * weight;
weight = weight_x2 * weight_y1;
r_val += p_src_row1[x_src + 1].rgbtRed * weight;
g_val += p_src_row1[x_src + 1].rgbtGreen * weight;
b_val += p_src_row1[x_src + 1].rgbtBlue * weight;
weight = weight_x3 * weight_y1;
r_val += p_src_row1[x_src + 2].rgbtRed * weight;
g_val += p_src_row1[x_src + 2].rgbtGreen * weight;
b_val += p_src_row1[x_src + 2].rgbtBlue * weight;

weight = weight_x0 * weight_y2;
r_val += p_src_row2[x_src - 1].rgbtRed * weight;
g_val += p_src_row2[x_src - 1].rgbtGreen * weight;
b_val += p_src_row2[x_src - 1].rgbtBlue * weight;
weight = weight_x1 * weight_y2;
r_val += p_src_row2[x_src].rgbtRed * weight;
g_val += p_src_row2[x_src].rgbtGreen * weight;
b_val += p_src_row2[x_src].rgbtBlue * weight;
weight = weight_x2 * weight_y2;
r_val += p_src_row2[x_src + 1].rgbtRed * weight;
g_val += p_src_row2[x_src + 1].rgbtGreen * weight;
b_val += p_src_row2[x_src + 1].rgbtBlue * weight;
weight = weight_x3 * weight_y2;
r_val += p_src_row2[x_src + 2].rgbtRed * weight;
g_val += p_src_row2[x_src + 2].rgbtGreen * weight;
b_val += p_src_row2[x_src + 2].rgbtBlue * weight;

weight = weight_x0 * weight_y3;
r_val += p_src_row3[x_src - 1].rgbtRed * weight;
g_val += p_src_row3[x_src - 1].rgbtGreen * weight;
b_val += p_src_row3[x_src - 1].rgbtBlue * weight;
weight = weight_x1 * weight_y3;
r_val += p_src_row3[x_src].rgbtRed * weight;
g_val += p_src_row3[x_src].rgbtGreen * weight;
b_val += p_src_row3[x_src].rgbtBlue * weight;
weight = weight_x2 * weight_y3;
r_val += p_src_row3[x_src + 1].rgbtRed * weight;
g_val += p_src_row3[x_src + 1].rgbtGreen * weight;
b_val += p_src_row3[x_src + 1].rgbtBlue * weight;
weight = weight_x3 * weight_y3;
r_val += p_src_row3[x_src + 2].rgbtRed * weight;
g_val += p_src_row3[x_src + 2].rgbtGreen * weight;
b_val += p_src_row3[x_src + 2].rgbtBlue * weight;

p_dst_row[x_dst].rgbtRed = clip(r_val + 0.5f);
p_dst_row[x_dst].rgbtGreen = clip(g_val + 0.5f);
p_dst_row[x_dst].rgbtBlue = clip(b_val + 0.5f);
}
}
}

You'd use the ResizeBitmap() function like so (here, to show in Image2
the resized version of Image1)...

SIZE const dst_size = {20, 20};

Image2->Picture->Bitmap;
Image2->Picture->Bitmap->PixelFormat = pf24bit;
Image2->Picture->Bitmap->Height = 0;
Image2->Picture->Bitmap->Width = dst_size.cx;
Image2->Picture->Bitmap->Height = dst_size.cy;

ResizeBitmap(*Image2->Picture->Bitmap, *Image1->Picture->Bitmap);
Image2->Refresh();

Note that the ResizeBitmap() function could use some optimization (e.g.,
faster boundary conditions, integer approximations, etc.).

Good luck,
Damon (TeamB)

Mauro

unread,
Jan 19, 2004, 1:39:27 AM1/19/04
to

hi Damon,

thank you very much! You was right, the problem was the color
table (pf24bit). With your coding it works perfectly...

Have you written this coding on your own? I thought yue aren't
a graphic expert ;-)

Regards,
Marius

Michel Leunen

unread,
Jan 19, 2004, 2:47:01 AM1/19/04
to
Mauro wrote:

> Have you written this coding on your own? I thought yue aren't
> a graphic expert ;-)

Have a look at this:
http://graphicsbb.itgo.com/

Michel
--
----------------------------------------
Michel Leunen
mailto:mic...@leunen.com
C++Builder, C++BuilderX, BCC5.5.1 Web site:
http://www.leunen.com/
----------------------------------------

ag...@hotmail.com

unread,
May 27, 2013, 1:08:31 PM5/27/13
to
Hello, I need help with you code.
I'm sorry. I'm spanish and not speaks english.
My code is:

//guardamos el ancho y alto de la imagen en dos variables
int deltay = CurrentBitmap->Picture->Bitmap->Height - MAXHEIGHT;
int deltax = CurrentBitmap->Picture->Bitmap->Width - MAXWIDTH;
int anchoSalida, altoSalida;
float porcentaje;

//Averiguamos el ancho y alto apropiados para la imagen
if(deltay > deltax && deltay > 0)
{
porcentaje = (100.0 * MAXHEIGHT) / CurrentBitmap->Picture->Bitmap->Height;
anchoSalida = CurrentBitmap->Picture->Bitmap->Width - ((CurrentBitmap->Picture->Bitmap->Width - (CurrentBitmap->Picture->Bitmap->Width * porcentaje / 100)));
altoSalida = CurrentBitmap->Picture->Bitmap->Height - ((CurrentBitmap->Picture->Bitmap->Height - (CurrentBitmap->Picture->Bitmap->Height * porcentaje / 100)));
}
else if(deltax > deltay && deltax > 0)
{
porcentaje = (100.0 * MAXWIDTH) / CurrentBitmap->Picture->Bitmap->Width;
anchoSalida = CurrentBitmap->Picture->Bitmap->Width - ((CurrentBitmap->Picture->Bitmap->Width - (CurrentBitmap->Picture->Bitmap->Width * porcentaje / 100)));
altoSalida = CurrentBitmap->Picture->Bitmap->Height - ((CurrentBitmap->Picture->Bitmap->Height - (CurrentBitmap->Picture->Bitmap->Height * porcentaje / 100)));
}
//Si la imagen tiene un ancho y alto inferiores al maximo no hace falta ajustarlos
else
{
anchoSalida = CurrentBitmap->Picture->Bitmap->Width;
altoSalida = CurrentBitmap->Picture->Bitmap->Height;
}

pQRImage[i]->Picture->Bitmap->PixelFormat = pf24bit;
pQRImage[i]->Picture->Bitmap->Width = anchoSalida;
pQRImage[i]->Picture->Bitmap->Height = altoSalida;

ResizeBitmap(*pQRImage[i]->Picture->Bitmap, *CurrentBitmap->Picture->Bitmap);
pQRImage[i]->Refresh();

Error: Access violation at address 00409687 in module 'Project2.exe'. Read of address 00D81000.

Can you help?
I hardly understand your code and I need to resize images of a TImage in C + + Builder as it gives poor quality StrechDraw.
0 new messages