I'm currently using fo-dicon to build a simple dicom viewer in C#. I am currently increasing or decreasing the brightness and contrast by adjusting window width & center values.
How do I invert the grayscale using the InvertLut class?
public WriteableBitmap DisplayedImage {get;set;}
//...
private void ExecuteLoadImageCommand()
{
_dicomFile = DicomFile.Open(GetImageFileName());
_dicomImage = new DicomImage(_dicomFile.Dataset);
WindowLevel = _dicomImage.WindowCenter;
WindowWidth = _dicomImage.WindowWidth;
var grayScaleOptions = GrayscaleRenderOptions.FromBitRange(_dicomFile.Dataset);
Depth = grayScaleOptions.BitDepth.BitsAllocated;
DisplayedImage = _dicomImage.RenderImage().As<WriteableBitmap>();
}
With the solution provided in below fo-dicom issue, i was able to invert the image. I did not use InvertLUT class.
https://github.com/fo-dicom/fo-dicom/issues/784
I had to convert from WritableBitmap to Bitmap, then again to WritableBitmap.
Solution:
public static class BitmapHelper
{
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
public static extern void CopyMemory(IntPtr dest, IntPtr source, int Length);
public static void ConvertToWritableBitmap(Bitmap bitmap, ref WriteableBitmap writeableBitmap)
{
BitmapData data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
try
{
writeableBitmap.Lock();
CopyMemory(writeableBitmap.BackBuffer, data.Scan0,
(writeableBitmap.BackBufferStride * bitmap.Height));
writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, bitmap.Width, bitmap.Height));
writeableBitmap.Unlock();
}
finally
{
bitmap.UnlockBits(data);
bitmap.Dispose();
}
}
/// <summary>
///
/// </summary>
/// <param name="image"></param>
/// <returns></returns>
public static Bitmap InvertBitmapPixels(WriteableBitmap writeableBitmap)
{
var image = BitmapFromWriteableBitmap(writeableBitmap);
// create the negative color matrix
ColorMatrix color_matrix = new ColorMatrix(
new float[][] {
new float[] {-1, 0, 0, 0, 0},
new float[] {0, -1, 0, 0, 0},
new float[] {0, 0, -1, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {1, 1, 1, 0, 1}
}
);
// create some image attributes
ImageAttributes attributes = new ImageAttributes();
attributes.SetColorMatrix(color_matrix);
var bmp = new Bitmap(image);
{
using (var g = Graphics.FromImage(bmp))
{
g.DrawImage(
image,
new Rectangle(0, 0, image.Width, image.Height),
0, 0, bmp.Width, bmp.Height,
GraphicsUnit.Pixel,
attributes
);
}
return bmp;
}
}
public static Bitmap BitmapFromWriteableBitmap(WriteableBitmap writeBmp)
{
System.Drawing.Bitmap bmp;
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create((BitmapSource)writeBmp));
enc.Save(outStream);
bmp = new System.Drawing.Bitmap(outStream);
}
return bmp;
}
}
To invert:
var image = BitmapHelper.InvertBitmapPixels(_writableBitmapImage);
BitmapHelper.ConvertToWritableBitmap(image, ref _writableBitmapImage);
Related
I am developing an application which requires me to create a progress bar moving from right to left.
I tried using GaugeField by filling startVal as 100 and then on decrementing it but I couldn't achieve it.
Is there any way in BlackBerry say paint() method or drawRect() using timer where we can fill it from right to left?
Check following code for an implementation of Custom GaugeField.
Output
Implementation of CustomGaugeField
class CustomGaugeField extends GaugeField {
// Default constructor, need improvement
public CustomGaugeField() {
super("", 0, 100, 0, GaugeField.PERCENT);
}
// Colors
private static final int BG_COLOR = 0xd6d7d6;
private static final int BAR_COLOR = 0x63cb52;
private static final int FONT_COLOR = 0x5a55c6;
protected void paint(Graphics graphics) {
int xProgress = (int) ((getWidth() / 100.0) * getValue());
int xProgressInv = getWidth() - xProgress;
// draw background
graphics.setBackgroundColor(BG_COLOR);
graphics.clear();
// draw progress bar
graphics.setColor(BAR_COLOR);
graphics.fillRect(xProgressInv, 0, xProgress, getHeight());
// draw progress indicator text
String text = getValue() + "%";
Font font = graphics.getFont();
int xText = (getWidth() - font.getAdvance(text)) / 2;
int yText = (getHeight() - font.getHeight()) / 2;
graphics.setColor(FONT_COLOR);
graphics.drawText(text, xText, yText);
}
}
How to use
class MyScreen extends MainScreen {
public MyScreen() {
setTitle("Custom GaugeField Demo");
GaugeField gField;
for (int i = 0; i < 6; i++) {
gField = new CustomGaugeField();
gField.setMargin(10, 10, 10, 10);
add(gField);
}
startProgressTimer();
}
private void startProgressTimer() {
TimerTask ttask = new TimerTask() {
public void run() {
Field f;
for (int i = 0; i < getFieldCount(); i++) {
f = getField(i);
if (f instanceof CustomGaugeField) {
final CustomGaugeField gField = (CustomGaugeField) f;
final int increment = (i + 1) * 2;
UiApplication.getUiApplication().invokeLater(
new Runnable() {
public void run() {
gField.setValue((gField.getValue() + increment) % 101);
}
}
);
}
}
}
};
Timer ttimer = new Timer();
ttimer.schedule(ttask, 1000, 300);
}
}
Here is what I recommend you do. Download the BlackBerry Advanced UI Samples ... select the Download as Zip button.
Take a look at some screenshots of what the samples have here. The one you need to use is the Bitmap Gauge Field:
What you can do is modify the BitmapGaugeField class that they have in the sample folder, under Advanced UI -> src/com/samples/toolkit/ui/component
In BitmapGaugeField.java, you will only need to change the drawHorizontalPill() method:
private void drawHorizontalPill( Graphics g, Bitmap baseImage, Bitmap centerTile, int clipLeft, int clipRight, int width )
{
int yPosition = ( _height - baseImage.getHeight() ) >> 1;
width = Math.max( width, clipLeft + clipRight );
// ORIGINAL IMPLEMENTATION COMMENTED OUT HERE:
// Left
//g.drawBitmap( 0, yPosition, clipLeft, baseImage.getHeight(), baseImage, 0, 0);
// Middle
//g.tileRop( _rop, clipLeft, yPosition, Math.max( 0, width - clipLeft - clipRight ), centerTile.getHeight(), centerTile, 0, 0);
// Right
//g.drawBitmap( width - clipRight, yPosition, clipRight, baseImage.getHeight(), baseImage, baseImage.getWidth() - clipRight, 0);
int offset = _width - width;
// Left
g.drawBitmap( 0 + offset, yPosition, clipLeft, baseImage.getHeight(), baseImage, 0, 0);
// Middle
g.tileRop( _rop, clipLeft + offset, yPosition, Math.max( 0, width - clipLeft - clipRight ), centerTile.getHeight(), centerTile, 0, 0);
// Right
g.drawBitmap( width - clipRight + offset, yPosition, clipRight, baseImage.getHeight(), baseImage, baseImage.getWidth() - clipRight, 0);
}
The way you use this class is to pass in values for the background, and foreground (fill) stretchable bitmaps, the range of values, initial value, and some clipping margins.
public BitmapGaugeField(
Bitmap background, /** bitmap to draw for gauge background */
Bitmap progress, /** bitmap to draw for gauge foreground */
int numValues, /** this is the discrete range, not including 0 */
int initialValue,
int leadingBackgroundClip,
int trailingBackgroundClip,
int leadingProgressClip,
int trailingProgressClip,
boolean horizontal ) /** it looks like you could even do vertical! */
An example, if you want this gauge to go from 0 to 100, and have an initial value of 30 (this code goes in a Manager class):
Bitmap gaugeBack3 = Bitmap.getBitmapResource( "gauge_back_3.png" );
Bitmap gaugeProgress3 = Bitmap.getBitmapResource( "gauge_progress_3.png" );
BitmapGaugeField bitGauge3 = new BitmapGaugeField( gaugeBack3, gaugeProgress3,
100, 30,
14, 14, 14, 14,
true );
bitGauge3.setPadding(15,5,15,5);
add(bitGauge3);
bitGauge3.setValue(80); // change the initial value from 30 to 80
You'll find in the project some PNG images, like gauge_back_3.png and gauge_progress_3.png. If you don't like the colors or shapes, you can swap those images out for ones you draw yourself (in Photoshop, or another drawing program).
Good luck!
This is default look when i launch activity.
After i play around with the wheel track pad then become like this. The button image or whole button lost.
Here is my custom buttonfield which extends ButtonField.
public class Custom_ButtonField extends ButtonField {
Bitmap mNormal;
Bitmap mFocused;
Bitmap mActive;
int mWidth;
int mHeight;
public Custom_ButtonField(Bitmap normal, Bitmap focused,
Bitmap active) {
super(CONSUME_CLICK);
mNormal = normal;
mFocused = focused;
mActive = active;
mWidth = mNormal.getWidth();
mHeight = mNormal.getHeight();
setMargin(0, 0, 0, 0);
setPadding(0, 0, 0, 0);
setBorder(BorderFactory
.createSimpleBorder(new XYEdges(0, 0, 0, 0)));
setBorder(VISUAL_STATE_ACTIVE, BorderFactory
.createSimpleBorder(new XYEdges(0, 0, 0, 0)));
}
protected void paint(Graphics graphics) {
Bitmap bitmap = null;
switch (getVisualState()) {
case VISUAL_STATE_NORMAL:
bitmap = mNormal;
break;
case VISUAL_STATE_FOCUS:
bitmap = mFocused;
break;
case VISUAL_STATE_ACTIVE:
bitmap = mActive;
break;
default:
bitmap = mNormal;
}
graphics.drawBitmap(0, 0, bitmap.getWidth(), bitmap.getHeight(),
bitmap, 0, 0);
}
public int getPreferredWidth() {
return mWidth;
}
public int getPreferredHeight() {
return mHeight;
}
protected void layout(int width, int height) {
setExtent(mWidth, mHeight);
}
}
It looks like you're trying to create a button with an image so you might be better off using the Advanced UI Library published by RIM. Take a look at the BitmapButtonField class.
Set Focusable on constructor.
super(Field.FOCUSABLE);
I want to show a image from the SD card in a BitmapField. How to do that? Can anyone give me some sample code for that?
This may be Help full.
public Bitmap getImage(){
Bitmap bitmapImage=null;
try{
InputStream input;
FileConnection fconn = (FileConnection) Connector.open("file:///store/home/user/dirname/imgname.png", Connector.READ_WRITE);
input = fconn.openInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int j = 0;
while((j=input.read()) != -1) {
baos.write(j);
}
byte[] byteArray = baos.toByteArray();
bitmapImage = Bitmap.createBitmapFromBytes(byteArray,0,byteArray.length,1);
}catch(Exception ioe){
System.out.println(ioe);
}
return bitmapImage;
}
Enjoy..
Hi Guys above code is useful for BB OS >= 5.0
I'm using a code which can used for OS 4.2 or higher.
private Bitmap resizeBitmap(Bitmap image, int width, int height)
{
int rgb[] = new int[image.getWidth()*image.getHeight()];
image.getARGB(rgb, 0, image.getWidth(), 0, 0, image.getWidth(), image.getHeight());
int rgb2[] = rescaleArray(rgb, image.getWidth(), image.getHeight(), width, height);
Bitmap temp2 = new Bitmap(width, height);
temp2.setARGB(rgb2, 0, width, 0, 0, width, height);
return temp2;
}
private int[] rescaleArray(int[] ini, int x, int y, int x2, int y2)
{
int out[] = new int[x2*y2];
for (int yy = 0; yy < y2; yy++)
{
int dy = yy * y / y2;
for (int xx = 0; xx < x2; xx++)
{
int dx = xx * x / x2;
out[(x2 * yy) + xx] = ini[(x * dy) + dx];
}
}
return out;
}
Try this sample code:
public class LoadingScreen extends MainScreen implements FieldChangeListener
{
private VerticalFieldManager ver;
private ButtonField showImage;
private BitmapField bitmapField;
public LoadingScreen()
{
ver=new VerticalFieldManager(USE_ALL_WIDTH);
showImage=new ButtonField("Show Image",Field.FIELD_HCENTER);
showImage.setChangeListener(this);
ver.add(showImage);
bitmapField=new BitmapField(null,Field.FIELD_HCENTER);
bitmapField.setPadding(10, 0, 10, 0);
ver.add(bitmapField);
add(ver);
}
public void fieldChanged(Field field, int context)
{
if(field==showImage)
{
selectImageFromSDCARD();
}
}
private void selectImageFromSDCARD()
{
String PATH="";
if(SDCardTest.isSDCardAvailable())//sdcard available then
PATH = System.getProperty("fileconn.dir.memorycard.photos");//The default stored Images Path;
else
PATH = System.getProperty("fileconn.dir.photos");//The default stored Images Path;
FilePicker filePicker=FilePicker.getInstance();
filePicker.setPath(PATH);
filePicker.setListener(new Listener()
{
public void selectionDone(String url)
{
System.out.println("======================URL: "+url);
try
{
FileConnection file = (FileConnection)Connector.open(url);
if(file.exists())
{
InputStream inputStream = file.openInputStream();
byte[] data=new byte[inputStream.available()];
data=IOUtilities.streamToBytes(inputStream);
Bitmap bitmap=Bitmap.createBitmapFromBytes(data, 0, data.length,1);//Here we get the Image;
Bitmap scaleBitmap=new Bitmap(400, 300);//Now we are scaling that image;
bitmap.scaleInto(scaleBitmap, Bitmap.FILTER_LANCZOS);
bitmapField.setBitmap(scaleBitmap);
}
else
{
bitmapField.setBitmap(Bitmap.getBitmapResource("icon.png"));
}
}
catch (IOException e)
{
bitmapField.setBitmap(Bitmap.getBitmapResource("icon.png"));
}
}
});
filePicker.show();
}
protected boolean onSavePrompt() //It doesn't show the "Save","Discard","Cancel" POPUP;
{
return true;
}
public boolean onMenu(int instance) //It doesn't show the Menu;
{
return true;
}
}
If you have any doubts refer this Blog: Get Image From SDcard
I want to display a Vertical Gradient as the background for a ListField Row in BlackBerry. I have used shadedFillPath function to achieve this, but failed:
int[] cols = new int[]{0xFFFFFF, 0xEEEEEE,0xEEEEEE,0XDDDDDD };
int[] xInds = new int[]{0, Display.getWidth(), Display.getWidth(), 0};
int[] yInds = new int[]{focusRect.y, focusRect.y,
this.getRowHeight()+focusRect.y,this.getRowHeight()+focusRect.y};
graphics.drawShadedFilledPath(xInds, yInds,null, cols, null);
try this
int[] X_PTS = { 0, getPreferredWidth(),getPreferredWidth(),0};
int[] Y_PTS = { 0,0, getPreferredHeight(),getPreferredHeight()};
int[] drawColors = { Colors.CategoryFocusGradientStart, Colors.CategoryFocusGradientStart,
Colors.CategoryFocusGradientEnd, Colors.CategoryFocusGradientEnd };
try {
g.drawShadedFilledPath(X_PTS, Y_PTS, null, drawColors, null);
} catch (IllegalArgumentException e) {
Log.Error(e,this,"Bad arguments.");
}
Its not the #rfsk2010 answer , but nearly (you must change that 0's for y):
Into:
public void drawListRow(ListField listField, Graphics graphics,int index, int y, int width)
Do this:
int[] X_PTS = { 0, getPreferredWidth(),getPreferredWidth(),0};
int[] Y_PTS = { y, y, getPreferredHeight(),getPreferredHeight()};
int[] drawColors = { Colors.CategoryFocusGradientStart, Colors.CategoryFocusGradientStart,
Colors.CategoryFocusGradientEnd, Colors.CategoryFocusGradientEnd };
try {
graphics.drawShadedFilledPath(X_PTS, Y_PTS, null, drawColors, null);
} catch (IllegalArgumentException e) {
Log.Error(e,this,"Bad arguments.");
}
I can not find a way to stop the out of memory error. The error occurs when I move the brightness or contrast track bar a few times.
Here is the code from the main form design for the button_click event that that opens the brightness and contrast form and send the value of "foto".
private void menuItemBrightness_Click(object sender, EventArgs e)
{
BrightnessContrast bFrm = new BrightnessContrast();
bFrm.Foto = imageHandler.CurrentBitmap;
if (bFrm.ShowDialog() == DialogResult.OK)
{
this.Cursor = Cursors.WaitCursor;
imageHandler.RestorePrevious();// this is for a undo or redo ... ignore this part
imageHandler.CurrentBitmap = bFrm.Foto1;
pictureBox1.Refresh();
this.Cursor = Cursors.Default;
}
}
Here is the complete code for the brightness and contras form
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
namespace SMART {
public partial class BrightnessContrast : Form {
ImageHandler imageHandler = new ImageHandler();
public int B = 0;
private int C = 0;
private static Bitmap foto;
private static Bitmap foto1;
private static Bitmap NewBitmap;
public BrightnessContrast()
{
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
InitializeComponent();
btnOK.DialogResult = DialogResult.OK;
btnCancel.DialogResult = DialogResult.Cancel;
domainUpDownB.SelectedIndex = 255;
domainUpDownC.SelectedIndex = 75;
}
private void BrightnessForm_Load(object sender, EventArgs e)
{ pictureBox2.Image = foto; }
public Bitmap Foto
{
get { return foto; }
set { foto = value; }
}
public Bitmap Foto1
{
get { return foto1; }
set { foto1 = value; }
}
public void brightnesstrackBar1_ValueChanged(object sender, EventArgs e)
{
domainUpDownB.Text = ((int)brightnessTrackBar.Value).ToString();
B = ((int)brightnessTrackBar.Value);
pictureBox2.Image = AdjustBrightness(foto, B);
foto1 = (Bitmap)pictureBox2.Image;
}
private void domainUpDown_TextChanged(object sender, EventArgs e)
{
try
{
brightnessTrackBar.Value = Convert.ToInt32(domainUpDownB.Text);
brightnesstrackBar1_ValueChanged(sender, e);
}
catch (Exception)
{
MessageBox.Show("Incorect Input Value Format");
}
}
public static Bitmap AdjustBrightness(Bitmap Image, int Value)
{
Bitmap TempBitmap = Image;
float FinalValue = (float)Value / 255.0f;
NewBitmap = new Bitmap(TempBitmap.Width, TempBitmap.Height);
Graphics NewGraphics = Graphics.FromImage(NewBitmap);
float[][] FloatColorMatrix ={
new float[] {1, 0, 0, 0, 0},
new float[] {0, 1, 0, 0, 0},
new float[] {0, 0, 1, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {FinalValue, FinalValue, FinalValue, 1, 1
}
};
ColorMatrix NewColorMatrix = new ColorMatrix(FloatColorMatrix);
ImageAttributes Attributes = new ImageAttributes();
Attributes.SetColorMatrix(NewColorMatrix);
NewGraphics.DrawImage(TempBitmap, new Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height), 0, 0, TempBitmap.Width, TempBitmap.Height, GraphicsUnit.Pixel, Attributes);
Attributes.Dispose();
NewGraphics.Dispose();
return NewBitmap;
}
public void contrastrackBar1_ValueChanged(object sender, EventArgs e)
{
domainUpDownC.Text = ((int)contrastTrackBar.Value).ToString();
C = ((int)contrastTrackBar.Value);
pictureBox2.Image = AdjustContrast(foto, C);
foto1 = (Bitmap)pictureBox2.Image;
}
public static Bitmap AdjustContrast(Bitmap Image, int Value)
{
Bitmap TempBitmap = Image;
float FinalValue = (float)Value*0.04f;
NewBitmap = new Bitmap(TempBitmap.Width, TempBitmap.Height);
Graphics NewGraphics = Graphics.FromImage(NewBitmap);
ImageAttributes Attributes = new ImageAttributes();
ColorMatrix NewColorMatrix = new ColorMatrix (new float[][] {
new float[] {FinalValue, 0f, 0f, 0f, 0f},
new float[] {0f, FinalValue, 0f, 0f, 0f},
new float[] {0f, 0f, FinalValue, 0f, 0},
new float[] {0f, 0f, 0f, 1f, 0f},
new float[] {0.001f, 0.001f, 0.001f, 0f, 1f}
});
Attributes.SetColorMatrix(NewColorMatrix);
NewGraphics.DrawImage(TempBitmap, new Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height), 0, 0, TempBitmap.Width, TempBitmap.Height, GraphicsUnit.Pixel, Attributes);
Attributes.Dispose();
NewGraphics.Dispose();
return NewBitmap;
}
OK here is the problem ... if I load a big image(as in pixels for example) and start moving the track Bar after a few moves it will show the famous "Out of memory exception was unhanded" and the error points to this line
NewGraphics.DrawImage(TempBitmap,
new System.Drawing.Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height), 0, 0,
TempBitmap.Width, TempBitmap.Height, System.Drawing.GraphicsUnit.Pixel,
Attributes);
I am using Attributes.Dispose(); NewGraphics.Dispose(); way i still have the error what I am missing?
Dispose gets rid of the unmanged resources (handles, unmanaged memory). There is probably a managed memory footprint here as well and that only gets free when the garbage collect occurs. My guess is that you need to reuse some of these bigger memory objects rather than having to recreate them each time.