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
Try using the CopyImage() Win32 function to resize the bitmap...
You'd assign the returned handle to Image->Picture->Bitmap->Handle.
Good luck,
Damon (TeamB)
C++ Builder seems to use this already to stretch the
pictures, so the result is the same...
regards,
Mauro
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)
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.
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)
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
> 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/
----------------------------------------