I'm rendering the font Consolas using freetype2.
I rendered the glyphs using
FT_Load_Glyph(face, glyphIndex, FT_LOAD_DEFAULT);
FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
and converted the bitmap to an ARGB map using a simple loop:
uint32_t* content = ... // ARGB bitmap
for (int py = 0; py < bitmap->rows; py++) {
for (int px = 0; px < bitmap->width; px++) {
uint8_t intensity = bitmap->buffer[py * bitmap->pitch + px];
content[py * bitmap->width + px] = (intensity << 24);
}
}
The result is that my rendered text looks much bolder than when it's rendered from for example notepad:
What could be the reason for this issue?
I'm not a Freetype expert (at all), but if you look at your example bitmap with a magnifying glass you'll see that the Notpad rendering has light blue and pink shades around it. Your rendering is anti-aliased, but all grey.
So I assume that Notepad renders in LCD mode , which really renders three subpixels (corresponding to the RBG rays of the Cathode Ray Display) per pixel and then smears them with the neighbouring pixels.
That also means that vertical hinting takes place along these subpixels and that, for example, the width of the vertical stems in the Notepad rendering is four or five sub-pixels, whereas in your rendering is is alwys at full pixel borders, i.e. six subpixels. This makes your rendering look bold in comparison to Notepad's. (Perhaps you can find font sizes where the effect is reversed: Your rendering looks lighter than Notepad's.)
You could do your rendering also in LCD mode (FT_RENDER_MODE_LCD). You'd render to a bitmap three times as wide and when you copy the text bitmap to your ARGB bitmap, you should take the average value of three adjacent pixels. (This will not give you true LCD rendering, but a lighter greyscale rendering.)
Related
I am creating the "perfect" sprite packer. This is a sprite packer that makes sure the output sprite is compatible with most if not all game engines and animation software. It is a program that merges images into a horizontal sprite sheet.
It converts (if needed) the source frames to BMP in memory
It considers the top-left pixel fully transparent for the entire image (can be configured)
It parses the frames each individually to find the real coordinates rect (where the actual frame starts, ends, its width and height (sometimes images may have a lot of extra transparent pixels).
It determines the frame box, which have the width and height of the frame with the largest width/height so that it is long enough to contain every frame. (For extra compatibility, every frame must have the same dimensions).
Creates output sprite with width of nFrames * wFrameBox
The problem is - anchor alignment. Currently, it tries to align each frame so that its center is on the frame box center.
if((wBox / 2) > (frame->realCoordinates.w / 2))
{
xpos = xBoxOffset + ((wBox / 2) - (frame->realCoordinates.w / 2));
}
else
{
xpos = xBoxOffset + ((frame->realCoordinates.w / 2) - (wBox / 2));
}
When animated, it looks better with it, but there is still this inconsistent horizontal frame position so that a walking animation looks like walking and shaking.
I also tried the following:
store the real x pixel position of the widest frame and use it as a reference point:
xpos = xBoxOffset + (frame->realCoordinates.x - xRef);
It also gives a little better results, showing that this is still not the correct algorithm.
Honestly, I don't know what am I doing.
What will be the correct way to align sprite frames (obtain the appropriate x position for drawing the next frame) given that the output sprite sheet have width of the number of frames multiplied by the width of the widest frame?
Your problem is that you first calculate the center then calculate the size of the required bounding box. That is why your image 'shakes' because in each image that center is different to the original center.
You should use the center of the original bounding box as your origin, then find out the size of each sprite, keeping track of the leftmost, rightmost, topmost and bottommost non transparent pixels. That would give you the bounding box you need to use to avoid the shaking.
The problem is that you will find that most sprites are already done that way, so the original bounding box is actually defined as to the minimum space to paint the whole sprite's sequence covering these non transparent pixels.
The only way to remove unused sprite space is to store the first sprite complete, and then the origin and dimensions of each other sprite, like is done in animated GIF and APNG ( Animated PNG -> https://en.wikipedia.org/wiki/APNG )
I am using FreeType to render some texts.
The surface where I want to draw the text is a bitmap image with format ARGB, pre-multiplied alpha.
The needed color of the text is also ARGB.
The rendered FT_Bitmap has format FT_PIXEL_MODE_LCD - it is as the text is rendered with white color on black background, with sub-pixel antialiasing.
So, for every pixel I have 3 numbers:
Da, Dr, Dg, Db - destination pixel ARGB (the background image).
Fr, Fg, Fb - FreeType rendered pixel (FT_Bitmap rendered with FT_RENDER_MODE_LCD)
Ca, Cr, Cg, Cb - The color of the text I want to use.
So, the question: How to properly combine these 3 numbers in order to get the result bitmap pixel.
The theoretical answers are OK and even better than code samples.
Interpet the FreeType data not as actual RGB colors (these 'raw' values are to draw text in black) but as intensities of the destination text color.
So the full intensity of each F color component is F*C/255. However, since your C also includes an alpha component, the intensity is scaled by it:
s' = F*C*A/(255 * 255)
assuming, of course, that F, C, and A are inside the usual range of 0..255. A is a fraction A/255, and the second division is to bring F*C back into the target range. s' is now the derived source color.
On to plotting it. Per color component, the new color gets add to D, and D in turn gets dimished by the source's alpha 255-A (scaled).
That leads to the full sum
D' = D*(255-A)/255 + F*C*A/(255 * 255)
equal to (moving one value to the right)
D' = (D*(255-A) + F*C*A/255)/255
for each separate channel r,g,b of D, F, C and A. The last one, alpha, also needs a separate calculation for each channel because your FreeType output data returns this format.
If the calculation is too slow, you could compare the visual result with not-LCD-optimized grayscale output from FreeType. I suspect that especially on 'busy' (not entirely monochrome) backgrounds the extra calculations are simply not worth it.
The numerical advantage of a pure grayscale input is that you only have to calculate A and 1-A once for each triplet of RGB colors.
The "background" also has an alpha channel but to draw text "on" it you can regard this as 'unused'. Drawing a transparent item onto another transparent item does not, in general, change its intrinsic transparency.
After some discovery, I found the right answer. It is disappointing.
It is impossible to draw subpixel rendered graphics (including fonts) on a transparent image with RGBA format.
In order to properly render such graphics, a format that supports separate alpha channels for every color is mandatory.
For example 48 bit per pixes: RrGgBg where r, g and b are the alpha channels for the red, green and blue collor channels respectively.
I'm trying to put overlay (watermark) on base image. Assuming it looks like this:
opaque rectangle with "fully transparent" text on it. I.e. all pixels of text is transparent, as well as background under it.
1 way:
I do opacity 60% in Photoshop:
And just compose it in code:
MagickWand* overlay = NewMagickWand();
MagickReadImage(overlay, overlaypath);
MagickCompositeImage(wand, overlay, OverCompositeOp, 100, 100);
//all open/destroy code is omitted
And result is rectangle is semi-transparent while text is fully transparent:
So far so good, but I also wanted to regulate overlay opacity via application config, so I tried second way:
2 way:
100% in Photoshop (as on very first image in this post) and trying to set transparency via MagickWand:
MagickReadImage(overlay, overlaypath);
MagickSetImageOpacity(overlay, 0.6);
MagickCompositeImage(wand, overlay, OverCompositeOp, 100, 100);
Result is totally blank rectangle, semi-transparent though
It looks like MagickSetImageOpacity set up every pixel alpha channel to the same 0.6 value regardless of its current value. What I need is currentAlpha -= givenAlpha for every pixel of overlay wand. Is this possible without iterate every wand pixel by hand?
The correct way to achieve the currentAlpha -= givenAlpha would be to invoke a PixelIterator, and iterator through each pixel, and apply the givenAlpha with PixelSetAlpha. But for you needs, you may be able to apply the same effects with MagickColorizeImage.
PixelWand *pColorize = NewPixelWand();
PixelWand *pGivenAlpha = NewPixelWand();
double userOpacity = 0.6;
// May need to be adjusted to "white" or "black" depending on your mask
PixelSetColor(pColorize, "transparent");
PixelSetAlpha(pGivenAlpha, userOpacity);
MagickWand *overlay = NewMagickWand();
MagickReadImage(overlay, overlaypath);
MagickColorizeImage(overlay, pColorize, pGivenAlpha); // Apply opacity filter
MagickCompositeImage(wand, overlay, OverCompositeOp, 100, 100);
I have a basic png file with two colors in it, green and magenta. What I'm looking to do is to take all the magenta pixels and make them transparent so that I can merge the image into another image.
An example would be if I have an image file of a 2D character on a magenta background. I would remove all the magenta in the background so that it's transparent. From there I would just take the image of the character and add it as a layer in another image so it looks like the character has been placed in an environment.
Thanks in advance.
That's the code i would use,
First, load your image :
IplImage *myImage;
myImage = cvLoadImage("/path/of/your/image.jpg");
Then use a mask like this to select the color, you should refer to the documentation. In the following, I want to select a blue (don't forget that in OpenCV images are in BGR format, therefore 125,0,0 is a blue (it corresponds to the lower bound) and 255,127,127 is blue with a certain tolerance and is the upper bound.
I chose lower and upper bound with a tolerance to take all the blue of your image, but you can select whatever you want...
cvInRangeS(image,
cvScalar(125.0, 0.0, 0.0),
cvScalar(255.0, 127.0, 127.0),
mask
);
Now we have selected the mask, let's inverse it (as we don't want to keep the mask, but to remove it)
cvNot(mask, mask);
And then copy your image with the mask,
IplImage *myImageWithTransparency; //You may need to initialize it before
cvCopy(myImage,myImageWithTransparency,mask);
Hope it could help,
Please refer to the OpenCVDocumentation for further information
Here it is
Julien,
I have a print preview that displays a captured panel on a form 'Panel1.DrawToBitmap(memoryImage, bounds);'
I also save the image to my hard drive - 'memoryImage.Save("diary.png")'
The image in the print preview at any zoom level is blurry, the saved image is perfect (viewed in windows photo viewer & PS).
Id like the print preview to be as good as the saved image, any ideas?
here's the code:-
private void CaptureScreen()
{
int x = splitContainerDiary.Location.X;
int y = splitContainerDiary.Location.Y;
int SCwidth = splitContainerDiary.Panel1.Width;
int SCheight = splitContainerDiary.Panel1.Height;
Rectangle bounds = new Rectangle(x, y, SCwidth, SCheight);
memoryImage = new Bitmap(SCwidth, SCheight, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
splitContainerDiary.Panel1.DrawToBitmap(memoryImage, bounds);
memoryImage.Save("diary.png");
}
private void printDocumentDiary_PrintPage(object sender, PrintPageEventArgs e)
{
CaptureScreen();
Font HeaderFont = new Font("Consolas", 16, FontStyle.Bold);
e.Graphics.DrawString(selectedYear.ToString() + " - " + name, HeaderFont, Brushes.Black, 15, 15);
e.Graphics.DrawImage(Image.FromFile("diary.png"), 5, 5);
// e.Graphics.DrawImage(memoryImage, 0, 40);
PrintDoodle(e);
}
I have tried to draw the image from memory (e.Graphics.DrawImage(memoryImage, 0, 40) and also from the saved image 'e.Graphics.DrawImage(Image.FromFile("diary.png"), 5, 5);' They are both blurry in print preview.
I have tried different Pixel formats with no joy either.
I have tried saving the image as BMP, JPG, PNG with no joy either (when drawing image fromFile).
I have tried using BitBlt routine also with the same results.
Tino
This is an inevitable consequence of the dramatic difference between the device resolution of a printer vs a monitor. A printer typically can print with a resolution of 600 dots per inch. A monitor is typically set to 96 DPI. So when you print an image that's razor sharp on a monitor, each pixel of the image requires printing a blob of 6 x 6. Short from the blockiness this produces, anything that's drawn on screen with anti-aliasing will get those anti-aliasing pixels drawn 6 times larger as well. Completely ruining the effect. This is especially noticeable with any text that's drawn with ClearType anti-aliasing. The red and blue fringes become very noticeable on paper.
You can partly solve this by drawing the image one-to-one on the printer, ensuring that 1 pixel in the image becomes 1 pixel on paper. That ought to now look nice and sharp (minus the ClearType problem) but you'll be looking at a postage stamp. Growing your arms six times longer would have the same effect.
Well, this just doesn't work well. Use the PrintDocument class so you can draw stuff to the printer using its native resolution. Use the methods provided by e.Graphics in the PrintPage event handler. Avoid images unless they are photos, anything that doesn't have finely detailed line art will scale well.
I have encountered a similar "blurry font" problem, while trying to print out some custom text, which I've pre-arranged as Labels in the TableLayoutPanel.
My solution for the blurriness was as follows: I have created a panel and labels four times as big as the desired final size (by using font 44 instead of 11, and using width and height four times greater).
Then I've created a (large) bitmap, and downscaled it in the final step (DrawImage):
using (var bmp = new Bitmap(tableLayout.Width, tableLayout.Height))
{
tableLayout.DrawToBitmap(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));
printPageEventArgs.Graphics.DrawImage(
bmp,
printPageEventArgs.MarginBounds.X,
printPageEventArgs.MarginBounds.Y,
bmp.Width / 4,
bmp.Height / 4);
}
The resulting text looks much sharper both in the preview and in the actual printed page.
Of course, such an approach can only work if you can manipulate the Control's size, for example by creating it "off screen". But it will not work if you require the actual displayed control to be printed.