I do some tests on drawing text on path. I made a background picture by setting bitmap to the canvas. Then, I draw text on a path to canvas, rotated by matrix. I have to shorten the code, I will only post the important part because it is too long. This images shown below are cropped with gimp, so don´t be irritated by the different sizes. My Rect, Path and Matrix objects:
RectF drawTextOval;
Path drawTextPath;
Matrix mDrawnMatrix;
Now, this is what I am doin to draw text on circle path:
drawTextOval.set(drawTextPosX - drawTextArc, drawTextPosY
- drawTextArc, drawTextPosX + drawTextArc, drawTextPosY
+ drawTextArc);
drawTextPath.addArc(drawTextOval, 0, 360);
drawTextPath.computeBounds(drawTextOval, true);
mDrawnMatrix.postRotate(drawTextArcStart,
(drawTextOval.right + drawTextOval.left) / 2,
(drawTextOval.bottom + drawTextOval.top) / 2);
drawTextPath.transform(mDrawnMatrix);
patternCanvas.drawTextOnPath(drawText, drawTextPath, 0, 0,
mFixedTextPaint);
Until this point, everything looks fine:
But after saving the whole screen, the rotated text looks warped at the saved .png image. All the other components looking good. I made some other drawings with text, linear or angular, all this works. Even some .png bitmaps drawing to canvas and the background image...all are looking normally. But the on circle path drawn text looks like this:
I don´t do any scaling on the bitmap, just saving the canvas image with:
FileOutputStream fos = new FileOutputStream(saveFile);
this.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
Bitmap bitmap = this.getDrawingCache();
bitmap.compress(CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
Why does the text look warped? Does anyone see my mistake? Thanks in advance...
EDIT
I tried some stuff and find out that setting any density to false or disable hardware acceleration in Manifest will show the effect at runtime before saving. That suggests to me, that when saving bitmap with getDrawingCache(), the scaling is disabled at this time. But why?
The last thing I found out, that the lower the curvature the lower is the text scaling. If the text is just a little bit curved, it seems good.
I would try setting the
this.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
before drawing to screen, since lower quality might imply a simpler transformation of the text. This is just guessing of course. Currently your only setting the high quality before writing to disk. Or simply remove it from the write to disk method to check if it affect the way the text is drawn.
Related
I'm trying to dynamically create images in android by taking an existing Bitmap and removing the centre of it in order to make a "cropped" version. The resulting image's height would naturally be smaller than the original, something like the attached example.
I've got a rough way of doing this by creating two new Bitmaps from the original, one containing the top of the image above the crop section (e.g. the android's head in the example) and the other containing the remaining image below the crop section (the android's feet) using the Bitmap.createBitmap(source, x, y, width, height) method, then drawing both of these bitmaps onto a canvas of a size equal to the original image minus the removed space.
This feels a bit clunky, and as I could be calling this method several times a second, it seems wasteful to create two bitmaps each time.
I was wondering if there was a more efficient way of doing this. Something like drawing the original Bitmap onto a canvas using a Path with it's Paint's xfermode set to a
new PorterDuffXfermode(Mode.DST_OUT) in order to cut out the portion of the image I wish to delete. But this seems to clear that area and not shrink the image down i.e. it leaves a big empty gap in the Android's middle.
Any suggestions greatly appreciated!
Why do you create two bitmaps? You only need to create one bitmap and then do canvas.drawBitmap() twice.
Bitmap bmpOriginal;
Bitmap bmpDerived = Bitmap.create(...);
Canvas canvas = new Canvas(bmpDerived);
canvas.drawBitmap(bmpOriginal, rectTopSrc, rectTopDst, null);
canvas.drawBitmap(bmpOriginal, rectBottomSrc, rectBottomDst, null);
Done.
If I were to initialize a Paint, and set it's text size like so:
Paint = new Paint();
Paint.setAntiAlias(true);
Paint.setARGB(255, 255, 0, 0);
Paint.setTextSize(screenWidth/100);
//screenWidth is the width of the screen in pixels given via display metrics.
And then draw text to the canvas like so:
String text = "Hello"
canvas.drawText(text, (screenWidth/13), (screenHeight/5), Paint);
Would the text Show up in the same relative spot the same relative size regardless of screen metrics? I ask because I only have 1 device and the Emulator doesn't run very well on my multi-core machine.
What I've been doing up until this point is simply using a bitmap with the text written over a background, but my memory usage is getting quite heavy, so I'm looking to cut down on the number of bitmaps loaded.
My other option is to save the text as a bitmap with a transparent background and overlay it on a single bitmap background. But that seems to be only half as productive, as it is actually creating 1 more bitmap, just reducing the total size of all the bitmaps stored. I also don't like this idea because i'd eventually like to take more control over the object life cycle and this will make that less effective.
Also, is there any method of adding styles to text (such as complicated fonts and color patterns besides using pre-made Drawables) so that the text can be drawn to canvas? (As cheaply as possible)
NVM, Solved By Poking around all day I figured out that DP units can be called from the res folder and will give a fairly uniform position of the text. and that paint is not as customization friendly as I wish.
I'm sorry if this topic has been brought before, but all my searches on the web and google groups did not help me.
I'm currently developing a little game with the Android SDK, and use hi-res bitmaps that I resize accordingly to match the device's resolution (letting the system do it for me is
not "crisp" enough).
I use a SurfaceView, on which I paint in one pass a canvas filling the whole surface. The paint uses setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)) to allow masking.
Beforehand, I retrieve various bitmaps -- which are resized at initialization with createScaledBitmap() and put in a cache -- and I apply the bitmaps with a paint on this canvas, before drawing this canvas on the SurfaceView.
My problem is, whatever I try, whatever paint settings I use (dithering, antialias, etc..), the resized bitmaps are not antialiased and the drawing present jagged edges. I tried everything.
The only little success I had was using inSampleSize to approach the desired scaled size and force a first pass of antialiasing, before invoking createScaledBitmap on the retrieved
hi-res bitmap, but it is not beautiful enough. I just can't allow to create multitudes of pre-sized bitmaps for every combination of resolution. What did I miss ?
Thanks a lot in advance
First when you load your bitmap you make sure that you don't lose any image quality by settings options to argb_8888:
Options options = new Options();
options.inScaled = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap pic = BitmapFactory.decodeResource(getResources(), R.id.pic, options);
When you scale the bitmap turn on the filter:
pic = Bitmap.createScaledBitmap(pic, screenW, screenH, true);
However if one streaches the image too much inevitably it degrades in quality.
When you use paint you can improve quality but lose on speed with turning on ditherig and filtering:
Paint paint = new Paint();
paint.setFlags(Paint.DITHER_FLAG);
paint.setFilterBitmap(true);
Finally the entire activity window could be set on argb_4444 instead on argb_8888 (OS < 2.3). You can chage this if you instert this line before setContentView:
getWindow().setFormat(PixelFormat.RGBA_8888);
If it comes down to it, you can manually antialias without too much trouble. Just apply a simple lowpass filter (something like an NxN average) to the pixel data before asking the bitmap object to rescale itself.
you may clear canvas buffer by youself! such as follows:
canvas.drawColor(Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR);
I would like to draw you attention to the following pictures
First the original image
then how it is rendered on the screen
as you can see on the original everything is nice and yellow, the edges have a slight transparency to look smooth.
but when I render I have some darkened pixels that appear.
To avoid the usual answer I get
I use
gl.glTexImage2D(GL10.GL_TEXTURE_2D, level, GL10.GL_RGBA, width, height, 0, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixelBuffer);
and not the glutil tool
also
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
Anybody has a solution for that ?
Thanks
Jason
Edit
I did some digging. in the original image my background is already the same color as the coin and I use an alpha mask.
I've also checked, when I load the bitmap it seems that all the bitmap who have an alpha 0 are automatically set to 0,0,0,0. this might be the problem.
edit
actually it seems to be the issue. I checked the values of the bitmap I open, the values for transparent pixels are 0,0,0,0 instead of the color I set. Is this a problem with Bitmap or how I created my image?
// Load up, and flip the texture:
Bitmap temp = BitmapFactory.decodeResource(context.getResources(), resource, opts);
Bitmap bmp = Bitmap.createBitmap(temp, 0, 0, temp.getWidth(), temp.getHeight(), flip, true);
temp.recycle();
What is the background color of your image? i.e. what does it look like when you remove the alpha channel? I'll guess - it's black? Try giving the background color the same color as your logo (i.e. yellowish) and try again.
EDIT: May as well edit the answer based on the comments. Even if you properly set the RGB color of the surrounding pixels, some image compression formats might still "optimize" that out and turn all completely transparent pixels into (0,0,0,0), which will cause the problems you describe when using bilinear filtering. Try using a different file format, or do research to see if you can turn this feature off in that particular compression, or consider fixing up the image in code after loading.
Your image probably has premultipled alpha values. Try using gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
See this post for more details.
Say I have a somewhat large (i.e. not fit in most phones' memory) bitmap on disk. I want to draw only parts of it on the screen in a way that isn't scaled (i.e. inSampleSize == 1)
Is there a way to load/draw just the part I want given a Rect specifying the area without loading the entire bitmap content?
I'm quite confident this is possible since you can load a really large bitmap file into an ImageView without problems so there must be some sort of a built-in way to handle large bitmaps... and after a few attempts, I've found a solution:
Instead of loading the entire bitmap and manually draw it yourself, load it as a Drawable instead:
InputStream mapInput = getResources().openRawResource(
R.drawable.transit_map);
_map = Drawable.createFromStream(mapInput, "transit_map");
_map.setBounds(0, 0, _mapDimension.width(), _mapDimension.height());
I'm using a resource file but since you can use Drawable.createFromStream to load image from any InputStream, it should works with arbitrary bitmap.
Then, use the Drawable.draw method to draw it onto the desired canvas like so:
int left = -(int) contentOffset.x;
int top = -(int) contentOffset.y;
int right = (int) (zoom * _mapDimension.width() - contentOffset.x);
int bottom = (int) (zoom * _mapDimension.height() - contentOffset.y);
_map.setBounds(left, top, right, bottom);
_map.draw(canvas);
As in the above case, You can also scale and translate the bitmap as well by manipulating the drawable's bounds and only the relevant parts of the bitmap will be loaded and drawn onto the Canvas.
The result is a pinch-zoomable view from just one single 200KB bitmap file. I've also tested this with a 22MB PNG file and it still works without any OutOfMemoryError including when screen orientation changes.
Now it's very relevant: BitmapRegionDecoder.
Note: available since Android SDK 10
It can easily be done by using RapidDecoder.
import rapid.decoder.BitmapDecoder;
Rect bounds = new Rect(10, 20, 30, 40);
Bitmap bitmap = BitmapDecoder.from("your-file.png")
.region(bounds)
.decode();
imageView.setImageBitmap(bitmap);
It supports down to Android 2.2 (API Level 8).
Generally speaking, that isn't possible, particularly since most image formats are compressed, so you don't even know which bytes to read until you've extracted the uncompressed form.
Break your image up into small tiles and load just the tiles you need to cover the region you want to display at runtime. To avoid jittery scrolling, you might also want to preload tiles that are just out of sight (the ones that border the visible tiles) on a background thread.