Index out of range exception while reading pixel data - c#-4.0

Related post: Stride of the BitmapData is different than the original
dimension.
I have taken the source code from here and modified it.
The code is generating a variety of exceptions in different occasions.
.
Error in BitmapLocker.cs
At the following line in Lock(),
// Copy data from IntegerPointer to _imageData
Marshal.Copy(IntegerPointer, _imageData, 0, _imageData.Length);
The following exception is being generated:
An unhandled exception of type 'System.AccessViolationException'
occurred in mscorlib.dll
Additional information: Attempted to read or write protected memory.
This is often an indication that other memory is corrupt.
For the following driver code,
double[,] mask = new double[,]
{
{ .11, .11, .11, },
{ .11, .11, .11, },
{ .11, .11, .11, },
};
Bitmap bitmap = ImageDataConverter.ToBitmap(mask);
BitmapLocker locker = new BitmapLocker(bitmap);
locker.Lock();
for (int i = 0; i < bitmap.Width; i++)
{
for (int j = 0; j < bitmap.Height; j++)
{
Color c = locker.GetPixel(i, j);
locker.SetPixel(i, j, c);
}
}
locker.Unlock();
At the following line in GetPixel(),
if (i > dataLength)
{
throw new IndexOutOfRangeException();
}
An unhandled exception of type 'System.IndexOutOfRangeException'
occurred in Simple.ImageProcessing.Framework.dll
Additional information: Index was outside the bounds of the array.
.
At the following line in SetPixel(),
if (ColorDepth == 8)
{
_imageData[i] = color.B;
}
An unhandled exception of type 'System.Exception' occurred in
Simple.ImageProcessing.Framework.dll
Additional information: (0, 0), 262144, Index was outside the bounds
of the array., i=262144
.
Error in Driver program
At the line,
Color c = bmp.GetPixel(i, j);
An unhandled exception of type 'System.InvalidOperationException'
occurred in System.Drawing.dll
Additional information: Bitmap region is already locked.
Source Code:
public class BitmapLocker : IDisposable
{
//private properties
Bitmap _bitmap = null;
bool _isLocked = false;
BitmapData _bitmapData = null;
private byte[] _imageData = null;
//public properties
public IntPtr IntegerPointer { get; private set; }
public int Width { get { return _bitmap.Width; } }
public int Height { get { return _bitmap.Height; } }
public int Stride { get { return _bitmapData.Stride; } }
public int ColorDepth { get { return Bitmap.GetPixelFormatSize(_bitmap.PixelFormat); } }
public int Channels { get { return ColorDepth / 8; } }
public int PaddingOffset { get { return _bitmapData.Stride - (_bitmap.Width * Channels); } }
public PixelFormat ImagePixelFormat { get { return _bitmap.PixelFormat; } }
public bool IsGrayscale { get { return Grayscale.IsGrayscale(_bitmap); } }
//Constructor
public BitmapLocker(Bitmap source)
{
IntegerPointer = IntPtr.Zero;
this._bitmap = source;
}
/// Lock bitmap
public void Lock()
{
if (_isLocked == false)
{
try
{
// Lock bitmap (so that no movement of data by .NET framework) and return bitmap data
_bitmapData = _bitmap.LockBits(
new Rectangle(0, 0, _bitmap.Width, _bitmap.Height),
ImageLockMode.ReadWrite,
_bitmap.PixelFormat);
// Create byte array to copy pixel values
int noOfBitsNeededForStorage = _bitmapData.Stride * _bitmapData.Height;
int noOfBytesNeededForStorage = noOfBitsNeededForStorage / 8;
_imageData = new byte[noOfBytesNeededForStorage * ColorDepth];//# of bytes needed for storage
IntegerPointer = _bitmapData.Scan0;
// Copy data from IntegerPointer to _imageData
Marshal.Copy(IntegerPointer, _imageData, 0, _imageData.Length);
_isLocked = true;
}
catch (Exception)
{
throw;
}
}
else
{
throw new Exception("Bitmap is already locked.");
}
}
/// Unlock bitmap
public void Unlock()
{
if (_isLocked == true)
{
try
{
// Copy data from _imageData to IntegerPointer
Marshal.Copy(_imageData, 0, IntegerPointer, _imageData.Length);
// Unlock bitmap data
_bitmap.UnlockBits(_bitmapData);
_isLocked = false;
}
catch (Exception)
{
throw;
}
}
else
{
throw new Exception("Bitmap is not locked.");
}
}
public Color GetPixel(int x, int y)
{
Color clr = Color.Empty;
// Get color components count
int channels = ColorDepth / 8;
// Get start index of the specified pixel
int i = (Height - y - 1) * Stride + x * channels;
int dataLength = _imageData.Length - channels;
if (i > dataLength)
{
throw new IndexOutOfRangeException();
}
if (ColorDepth == 32) // For 32 bpp get Red, Green, Blue and Alpha
{
byte b = _imageData[i];
byte g = _imageData[i + 1];
byte r = _imageData[i + 2];
byte a = _imageData[i + 3]; // a
clr = Color.FromArgb(a, r, g, b);
}
if (ColorDepth == 24) // For 24 bpp get Red, Green and Blue
{
byte b = _imageData[i];
byte g = _imageData[i + 1];
byte r = _imageData[i + 2];
clr = Color.FromArgb(r, g, b);
}
if (ColorDepth == 8)
// For 8 bpp get color value (Red, Green and Blue values are the same)
{
byte c = _imageData[i];
clr = Color.FromArgb(c, c, c);
}
return clr;
}
public void SetPixel(int x, int y, Color color)
{
// Get color components count
int cCount = ColorDepth / 8;
// Get start index of the specified pixel
int i = ((Height - y -1) * Stride + x * cCount);
try
{
if (ColorDepth == 32) // For 32 bpp set Red, Green, Blue and Alpha
{
_imageData[i] = color.B;
_imageData[i + 1] = color.G;
_imageData[i + 2] = color.R;
_imageData[i + 3] = color.A;
}
if (ColorDepth == 24) // For 24 bpp set Red, Green and Blue
{
_imageData[i] = color.B;
_imageData[i + 1] = color.G;
_imageData[i + 2] = color.R;
}
if (ColorDepth == 8)
// For 8 bpp set color value (Red, Green and Blue values are the same)
{
_imageData[i] = color.B;
}
}
catch(Exception ex)
{
throw new Exception("("+x+", "+y+"), "+_imageData.Length+", "+ ex.Message+", i=" + i);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// free managed resources
_bitmap = null;
_bitmapData = null;_imageData = null;IntegerPointer = IntPtr.Zero;
}
// free native resources if there are any.
//private properties
//public properties
}
}
.
ImageDataConverter.cs
public static Bitmap ToBitmap(double[,] input)
{
int width = input.GetLength(0);
int height = input.GetLength(1);
Bitmap output = Grayscale.CreateGrayscaleImage(width, height);
BitmapData data = output.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.WriteOnly,
output.PixelFormat);
int pixelSize = System.Drawing.Image.GetPixelFormatSize(PixelFormat.Format8bppIndexed) / 8;
int offset = data.Stride - width * pixelSize;
double Min = 0.0;
double Max = 255.0;
unsafe
{
byte* address = (byte*)data.Scan0.ToPointer();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
double v = 255 * (input[x, y] - Min) / (Max - Min);
byte value = unchecked((byte)v);
for (int c = 0; c < pixelSize; c++, address++)
{
*address = value;
}
}
address += offset;
}
}
output.UnlockBits(data);
return output;
}
Here is the picture I used for the test,

int noOfBitsNeededForStorage = _bitmapData.Stride * _bitmapData.Height;
That is the most essential bug in the code. Stride * Height are the number of bytes needed for storage. So it doesn't make the _imageData array large enough and IndexOutOfRangeException is the expected outcome.
int pixelSize = System.Drawing.Image.GetPixelFormatSize(PixelFormat.Format8bppIndexed) / 8;
Lots of possible mishaps from this statement. It hard-codes the pixel format to 8bpp but that is not the actual pixel format that the LockBits() call used. Which was output.PixelFormat. Notably fatal on the sample image, although it is not clear how it is used in the code, 8bpp is a very awkward pixel format since it requires a palette. The PNG codec will create a 32bpp image in memory, even though the original file uses 8bpp. You must use output.PixelFormat here to get a match with the locked data and adjust the pixel writing code accordingly. Not clear why it is being used at all, the SetPixel() method provided by the library code should already be good enough.
int dataLength = _imageData.Length - channels;
Unclear what that statement tries to do, subtracting the number of channels is not a sensible operation. It will generate a spurious IndexOutOfRangeException. There is no obvious reason to help, the CLR already provides array index checking on the _imageData array. So just delete that code.
Additional information: Bitmap region is already locked.
Exception handling in the code is not confident, a possible reason for this exception. In general it must be noted that the underlying bitmap is completely inaccessible, other than through _imageData, after the Lock() method was called and Unlock() wasn't called yet. Best way to do this is with try/finally with the Unlock() call in the finally block so you can always be sure that the bitmap doesn't remain locked by accident.
byte c = _imageData[i];
This is not correct, except in the corner case of an 8bpp image that has a palette that was explicitly created to handle grayscale images. The default palette for an 8bpp image does not qualify that requirement nor is it something you can blindly rely on when loading images from a file. Indexed pixel formats where a dreadful hack that was necessary in the early 1990s because video adapters where not yet powerful enough. It no longer makes any sense at all today. Note that SetPixel() also doesn't handle a 16-bit pixel formats. And that the PNG codec will never create an 8bpp memory image and cannot encode an 8bpp file. Best advice is to eliminate 8bpp support completely to arrive at more reliable code.
In fact, the point of directly accessing pixel data is to make image manipulation fast. There is only one pixel format that consistently produces fast code, it is Format32bppArgb. The pixels can now be accessed with an int* instead of a byte*, moving pixels ~4 times faster. And no special tweaks are necessary to deal with stride or special-case the code for methods like SetPixel(). So pass that format into LockBits(), the codec will do the work necessary if the actual image format is not 32bpp as well.
I should note that Format32bppPArgb is the fast pixel format for displaying images on the screen since it is compatible with the pixel format used by all modern video adapters. But that isn't the point of this code and dealing with the pre-multiplied alpha is awkward.

Related

Android studio load bitmap via activity result

In my android studio project I am trying to select an image from external storage via startActivityForResult and load the selected image into memory. Also I have a BitmapLoader helper class. Here is the piece of code where I call the activity to get the result
private void pickFromGallery() {
//Create an Intent with action as ACTION_PICK
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
// Sets the type as image/*. This ensures only components of type image are selected
intent.setType("image/*");
//We pass an extra array with the accepted mime types. This will ensure only components with these MIME types as targeted.
String[] mimeTypes = {"image/jpeg", "image/png"};
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
// Launching the Intent
startActivityForResult(intent, GALLERY_REQUEST_CODE); //GALLERY_REQUEST_CODE is a constant integer
}
And here is the activity result callback
public void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Result code is RESULT_OK only if the user selects an Image
if(resultCode == Activity.RESULT_OK) {
Bitmap imageBitmap = null;
switch(requestCode) {
case GALLERY_REQUEST_CODE:
//data.getData returns the content URI for the selected Image
File file = new File(data.getData().getPath());
try {
//BitmapLoader is my helper class
imageBitmap = BitmapLoader.decodeSampleBitmapFromFile(file, 100, 100);
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "Error while reading a file!", Toast.LENGTH_SHORT).show();
return;
}
userImage.setImageBitmap(imageBitmap);
break;
}
}
}
Finally, here is the BitmapLoader helper class.
public class BitmapLoader {
private BitmapLoader() {}
private static Bitmap rotateImageIfRequired(Bitmap img, File file) throws IOException {
ExifInterface ei = new ExifInterface(file);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
return rotateImage(img, 90);
case ExifInterface.ORIENTATION_ROTATE_180:
return rotateImage(img, 180);
case ExifInterface.ORIENTATION_ROTATE_270:
return rotateImage(img, 270);
default:
return img;
}
}
private static Bitmap rotateImage(Bitmap img, int degree) {
Matrix matrix = new Matrix();
matrix.postRotate(degree);
Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
img.recycle();
return rotatedImg;
}
private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize += 1;
}
}
return inSampleSize;
}
public static Bitmap decodeSampleBitmapFromFile(File file, int reqWidth, int reqHeight) throws IOException {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getPath(), options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(file.getPath(), options);
return rotateImageIfRequired(bitmap, file);
}
}
The problem is, when I call imageBitmap = BitmapLoader.decodeSampleBitmapFromFile(file, 100, 100); , it always throws an exception. I think the problem is the part where I create a File object from the Uri returned as a result. Can anyone please describe to me where the problem is coming from and help me write the correct code?
I found the right way to solve the problem. Here is the commented code describing the true way to solve the problem.
private void pickFromGallery() {
// intent with ACTION_OPEN_DOCUMENT to make
// content providers (gallery application, downloads application and so on)
// to show their files
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
//and that files must be openable
intent.addCategory(Intent.CATEGORY_OPENABLE);
//setting mime type to get only image files
intent.setType("image/*");
//just invoking intent
startActivityForResult(intent, GALLERY_REQUEST_CODE);
}
//getting picked image
public void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Result code is RESULT_OK only if the user selects an Image
if(resultCode == Activity.RESULT_OK) {
Bitmap imageBitmap;
switch(requestCode) {
case GALLERY_REQUEST_CODE:
if(data == null) return;
//ParcelFileDescriptor allowing you to close it when done with it.
ParcelFileDescriptor parcelFileDescriptor;
try {
//a method to get file descriptor via Uri(data.getData() returns a Uri, "r" means for reading)
parcelFileDescriptor = getContentResolver().openFileDescriptor(data.getData(), "r");
//getting a descriptor from ParcelFileDescriptor
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
// and sending that descriptor to BitmapLoader, which now takes only descriptor and required width and height to load bitmap
imageBitmap = BitmapLoader.decodeSampleBitmapFromDescriptor(fileDescriptor, 100, 100);
parcelFileDescriptor.close();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "Error while reading a file!", Toast.LENGTH_SHORT).show();
return;
}
//here you can use the loaded bitmap as you like
userImage.setImageBitmap(imageBitmap);
break;
}
}
}
Finally the Bitmaploader class
public class BitmapLoader {
private BitmapLoader() {}
//this method uses ExifInterface to figure out how to rotate the image to bring it back to normal orientation, but that's another story.
private static Bitmap rotateImageIfRequired(Bitmap img, FileDescriptor descriptor) throws IOException {
ExifInterface ei = new ExifInterface(descriptor);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
return rotateImage(img, 90);
case ExifInterface.ORIENTATION_ROTATE_180:
return rotateImage(img, 180);
case ExifInterface.ORIENTATION_ROTATE_270:
return rotateImage(img, 270);
default:
return img;
}
}
//just a helper for the previous method
private static Bitmap rotateImage(Bitmap img, int degree) {
Matrix matrix = new Matrix();
matrix.postRotate(degree);
Bitmap rotatedImg = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, true);
img.recycle();
return rotatedImg;
}
// calculates how many times a bitmap should be reduced
private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize += 1;
}
}
return inSampleSize;
}
//main working method that throws an IOException because there might be problems reading the file
public static Bitmap decodeSampleBitmapFromDescriptor(#NonNull FileDescriptor descriptor, int reqWidth, int reqHeight) throws IOException {
// BitmapFactory.Options helps to load only the image information first.
final BitmapFactory.Options options = new BitmapFactory.Options();
// must set to true to load only the image information
options.inJustDecodeBounds = true;
/**null is just a padding*/
//loading into options the information about image
BitmapFactory.decodeFileDescriptor(descriptor, null, options);
// Calculation of the dimensions of the image for loading in accordance with the required width and height
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// now setting to false to load real bitmap with required dimensions
options.inJustDecodeBounds = false;
//decoding an image and returning a bitmap
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(descriptor, null, options);
return rotateImageIfRequired(bitmap, descriptor);
}
}
It is very important to load the image into memory at a reduced size, because a bitmap of a real image may require over 500MB of RAM.

How can we get the Signature Properties details and put it as part of the visibleSignature in PDFBox 2.0.12

Below are the signature details as part of visible Signature
I am using Apache PDFBox 2.0.12 to sign a PDF document. I want to enable a visual signature also.
I am able to sign and create a visual signature. The problem is how can we get the details regarding the Signature properties(Such as SignedBy, SignedDate from the Certificate itself and mentioned in the Text as explained here:
public class VisibleSignature2 extends SignatureBase {
private SignatureOptions signatureOptions;
private VisibleSignatureOptions visibleSignatureOptions;
public VisibleSignature2(PrivateKey keystore, List<Certificate> pin, VisibleSignatureOptions visibleSignatureOptions) {
super(keystore, pin);
this.visibleSignatureOptions = visibleSignatureOptions;
}
public void signPDF(InputStream inputFile, OutputStream signedFile, Rectangle2D humanRect) throws IOException {
PDDocument doc = PDDocument.load(inputFile);
int accessPermissions = SigUtils.getMDPPermission(doc);
if (accessPermissions == 1) {
throw new IllegalStateException("No changes to the document are permitted due to DocMDP transform parameters dictionary");
}
PDSignature signature = new PDSignature();
PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
PDRectangle rect = createSignatureRectangle(doc, humanRect);
if (doc.getVersion() >= 1.5f && accessPermissions == 0) {
SigUtils.setMDPPermission(doc, signature, 2);
}
if (acroForm != null && acroForm.getNeedAppearances()) {
// PDFBOX-3738 NeedAppearances true results in visible signature becoming invisible
// with Adobe Reader
if (acroForm.getFields().isEmpty()) {
// we can safely delete it if there are no fields
acroForm.getCOSObject().removeItem(COSName.NEED_APPEARANCES);
// note that if you've set MDP permissions, the removal of this item
// may result in Adobe Reader claiming that the document has been changed.
// and/or that field content won't be displayed properly.
// ==> decide what you prefer and adjust your code accordingly.
}
}
// default filter
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
// the signing date, needed for valid signature
signature.setSignDate(Calendar.getInstance());
signatureOptions = new SignatureOptions();
System.out.println("Visible Signature Reason is " + visibleSignatureOptions.hasVisibleSignatureReason());
if (visibleSignatureOptions.isShouldSignatureBeVisible()) {
System.out.println("Inside VisualSignature");
signature.setReason(visibleSignatureOptions.getVisibleSignatureReason());
signatureOptions.setVisualSignature(createVisualSignatureTemplate(doc, 0, rect));
}
signatureOptions.setPage(0);
doc.addSignature(signature, this, signatureOptions);
// write incremental (only for signing purpose)
doc.saveIncremental(signedFile);
doc.close();
IOUtils.closeQuietly(signatureOptions);
}
private PDRectangle createSignatureRectangle(PDDocument doc, Rectangle2D humanRect) {
float x = (float) humanRect.getX();
float y = (float) humanRect.getY();
float width = (float) humanRect.getWidth();
float height = (float) humanRect.getHeight();
PDPage page = doc.getPage(0);
PDRectangle pageRect = page.getCropBox();
PDRectangle rect = new PDRectangle();
// signing should be at the same position regardless of page rotation.
switch (page.getRotation()) {
case 90:
rect.setLowerLeftY(x);
rect.setUpperRightY(x + width);
rect.setLowerLeftX(y);
rect.setUpperRightX(y + height);
break;
case 180:
rect.setUpperRightX(pageRect.getWidth() - x);
rect.setLowerLeftX(pageRect.getWidth() - x - width);
rect.setLowerLeftY(y);
rect.setUpperRightY(y + height);
break;
case 270:
rect.setLowerLeftY(pageRect.getHeight() - x - width);
rect.setUpperRightY(pageRect.getHeight() - x);
rect.setLowerLeftX(pageRect.getWidth() - y - height);
rect.setUpperRightX(pageRect.getWidth() - y);
break;
case 0:
default:
rect.setLowerLeftX(x);
rect.setUpperRightX(x + width);
rect.setLowerLeftY(pageRect.getHeight() - y - height);
rect.setUpperRightY(pageRect.getHeight() - y);
break;
}
return rect;
}
private InputStream createVisualSignatureTemplate(PDDocument srcDoc, int pageNum, PDRectangle rect) throws IOException {
PDDocument doc = new PDDocument();
PDPage page = new PDPage(srcDoc.getPage(pageNum).getMediaBox());
doc.addPage(page);
PDAcroForm acroForm = new PDAcroForm(doc);
doc.getDocumentCatalog().setAcroForm(acroForm);
PDSignatureField signatureField = new PDSignatureField(acroForm);
PDAnnotationWidget widget = signatureField.getWidgets().get(0);
List<PDField> acroFormFields = acroForm.getFields();
acroForm.setSignaturesExist(true);
acroForm.setAppendOnly(true);
acroForm.getCOSObject().setDirect(true);
acroFormFields.add(signatureField);
widget.setRectangle(rect);
// from PDVisualSigBuilder.createHolderForm()
PDStream stream = new PDStream(doc);
PDFormXObject form = new PDFormXObject(stream);
PDResources res = new PDResources();
form.setResources(res);
form.setFormType(1);
PDRectangle bbox = new PDRectangle(rect.getWidth(), rect.getHeight());
float height = bbox.getHeight();
Matrix initialScale = null;
switch (srcDoc.getPage(pageNum).getRotation()) {
case 90:
form.setMatrix(AffineTransform.getQuadrantRotateInstance(1));
initialScale = Matrix.getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
height = bbox.getWidth();
break;
case 180:
form.setMatrix(AffineTransform.getQuadrantRotateInstance(2));
break;
case 270:
form.setMatrix(AffineTransform.getQuadrantRotateInstance(3));
initialScale = Matrix.getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
height = bbox.getWidth();
break;
case 0:
default:
break;
}
form.setBBox(bbox);
PDFont font = PDType1Font.HELVETICA_BOLD;
// from PDVisualSigBuilder.createAppearanceDictionary()
PDAppearanceDictionary appearance = new PDAppearanceDictionary();
appearance.getCOSObject().setDirect(true);
PDAppearanceStream appearanceStream = new PDAppearanceStream(form.getCOSObject());
appearance.setNormalAppearance(appearanceStream);
widget.setAppearance(appearance);
PDPageContentStream cs = new PDPageContentStream(doc, appearanceStream);
if (initialScale != null) {
cs.transform(initialScale);
}
// show text
float fontSize = 10;
float leading = fontSize * 1.5f;
cs.beginText();
cs.setFont(font, fontSize);
cs.setNonStrokingColor(Color.black);
cs.newLineAtOffset(fontSize, height - leading);
cs.setLeading(leading);
cs.showText("(Signature very wide line 1)");
cs.newLine();
cs.showText("(Signature very wide line 2)");
cs.newLine();
cs.showText("(Signature very wide line 3)");
cs.endText();
cs.close();
cs.close();
// no need to set annotations and /P entry
ByteArrayOutputStream baos = new ByteArrayOutputStream();
doc.save(baos);
doc.close();
return new ByteArrayInputStream(baos.toByteArray());
}
}

How is a JPEG file formated?

I'm trying to write Bytes into a JPEG file, but I don't know the file's format and so the Bytes aren't in the right place of the image after writing into the file.
Does somebody know?
There are several markers that must appear in a JPEG file stream. I believe you can easily find the detailed description of the tags listed below on Internet.
SOI(0xFFD8) Start of Image
APP0(0xFFE0) Application
[APPn(0xFFEn)] (alternative)
DQT(0xFFDB) Define Quantization Table
SOF0(0xFFC0) Start of Frame
DHT(0xFFC4) Difine Huffman Table
SOS(0xFFDA) Start of Scan
DRI(0xFFDD) Define Restart Interval,(alternative)
...Image Stream
EOI(0xFFD9) End of Image
Those markers are followed by lengths in BIG ENDIAN format. You can decode Image Stream that exactly follows DRI using the huffman trees you decoded by DQT. For easier illustration, here are some functions I have written on my own in Java that decodes a header of JPEG, but without doubt there are many better JPEG Java projects on Github that you can refer to.
public int[][] cutX(byte[] x){
int s = x.length;int k = 1;int i = 2;int j;
d2[0][0]=Tool.unsignDecoder(x[1]);d2[1][0]=0;d2[2][0]=1;
while(d2[0][k-1]!=218){
d2[1][k]=i;
d2[0][k]=Tool.unsignDecoder(x[i+1]);
i=i+2+Tool.unsignDecoder(x[i+2])*256+Tool.unsignDecoder(x[i+3]);
d2[2][k]=i-1;
k=k+1;
}
for (j=s-1;j<i;j--){
if((Tool.unsignDecoder(x[j-1])==255)&&(Tool.unsignDecoder(x[j])==217)) break;
}
d2[0][k]=217;d2[1][k]=i;d2[2][k]=j+1;
return d2;
}
public void cutdata(byte[] x,int[][] d){
int a =Tool.indexOf_1(d[0],218);
int b =Tool.indexOf_1(d[0],217);
head = Arrays.copyOfRange(x, 0, d[2][a]+1);
byte[] im = Arrays.copyOfRange(x, d[1][b], d[2][b]-1);//-2:delete the last EOI message.
im1 = new byte[im.length];
int j=0;int i=0;//dynamically record the length of the revised sequence
while(i<im.length){
im1[j]=im[i];
j++;
if((i!=im.length-1)&&(Tool.unsignDecoder(im[i])==255)&&(Tool.unsignDecoder(im[i+1]))==0){
i++;//move rightward i
}
i++;
}
im1=Arrays.copyOfRange(im1, 0, j);//delete zeros in the end of the sequence
}
public void sof(byte[] x,int[][] d){
int z = Tool.indexOf_1(d[0],192);
int i = d[1][z];
int[] temp = new int[19];
for(int j=0;j<19;j++){
temp[j]=Tool.unsignDecoder(x[j+i]);
}
int ph=i+5;int pw=i+7;
size[0] = Tool.unsignDecoder(x[ph])*256+Tool.unsignDecoder(x[ph+1]);
size[1] = Tool.unsignDecoder(x[pw])*256+Tool.unsignDecoder(x[pw+1]);
i += 11;//skip some unused letters
for(int j=0;j<3;j++){
int k = Tool.unsignDecoder(x[i]);
Q[j][0] = (k & 0xF0)/16;
Q[j][1] = k & 0x0F;
i += 3;
}
}
public void hfm(byte[] x,int[][] d){
//the DHT marker may appear several times in a JPEG, or several huffman trees can be found in a single DHT.
ArrayList res =Tool.indexOf(d[0],196);int thisLength;int pointer;int pointerOrigin;
int a;int huffLength = 0;
for(int z=0;z<res.size();z++){
a=(int) res.get(z);
pointer = d[1][a];pointerOrigin = d[1][a]+2;//please follow the straight-forward moving of this pointer
thisLength = Tool.unsignDecoder(x[pointer+2])*256+Tool.unsignDecoder(x[pointer+3]);
int[] temp = new int[thisLength+4];
for(int i=0;i<thisLength;i++){
temp[i]=Tool.unsignDecoder(x[pointer+i]);
}
pointer += 4;
while(huffLength<thisLength){
int mode = Tool.unsignDecoder(x[pointer]);pointer += 1;
int[] huff_num = new int[16];int total=0;
for(int i=0;i<16;i++){//码字总个数
huff_num[i] = x[pointer+i];total+=huff_num[i];
}
pointer +=16;int codePointer=0;int code=0;
int[][] huffmanTree = new int[3][total];
for(int i=0;i<16;i++){
if(i!=0){
code *= 2;
}
for(int j=0;j<huff_num[i];j++){
huffmanTree[0][codePointer]=i+1;
huffmanTree[1][codePointer]=code;
huffmanTree[2][codePointer]=Tool.unsignDecoder(x[pointer+codePointer]);
code++;codePointer++;
}
}
huffLength += pointer + codePointer - pointerOrigin;pointer += codePointer;
pointerOrigin = pointer;
switch(mode){
case(0):d0 = huffmanTree;break;
case(1):d1 = huffmanTree;break;
case(16):a0 = huffmanTree;break;
case(17):a1 = huffmanTree;break;
}
}
}
}
public void dri(byte[] x,int[][] d){
int z = Tool.indexOf_1(d[0],221);
if(z!=-1){
int pointer = d[1][z];
int len = Tool.unsignDecoder(x[pointer+2])*256+Tool.unsignDecoder(x[pointer+3]);
int[] temp = new int[len+2];
for(int i=0;i<len;i++){
temp[i]=Tool.unsignDecoder(x[pointer+i]);
}
DRI = Tool.unsignDecoder(x[d[1][z]+4])*256+Tool.unsignDecoder(x[d[1][z]+5]);}
}
public void sos(byte[] x,int[][] d){
int z = Tool.indexOf_1(d[0],218);int a = d[1][z];
int len = Tool.unsignDecoder(x[a+2])*256+Tool.unsignDecoder(x[a+3]);
int[] temp = new int[len+2];
for(int j=0;j<len+2;j++){
temp[j]=Tool.unsignDecoder(x[j+a]);
}
int pointer = d[1][z]+6;
for(int j=0;j<3;j++){
treeSelect[j] = Tool.unsignDecoder(x[pointer]);
pointer += 2;
}
}

Issue with Photometric Interpretation tag even after inverting the image data

Note: Giving the background of my previous question once again so as to find all the related stuff at one source.
I'm capturing an image from an android mobile device and it’s in JPEG format. The image is of 72X72DPI and 24 bit. When I try to convert this JPEG image to TIFF using LibTiff.Net and to set the tag Photometric Interpretation = 0 for MinIsWhite, the image turns negative (the white becomes black and black becomes white). The environment is Windows 8.1 64 bit, Visual Studio 2012. The tag must have value 0, where 0 = white is zero.
I absolutely must use Photometric.MINISWHITE in images so tried inverting image data before writing it to TIFF as per the below code. But then the compression changes to LZW instead of CCITT4,Photometric is changed to MINISBLACK from MINISWHITE, FIllorder tag is removed, PlanarConfig tag is removed, New tag Predictor is added with value 1 and the image turns negative again.
public partial class Form1 : Form
{
private const TiffTag TIFFTAG_ASCIITAG = (TiffTag)666;
private const TiffTag TIFFTAG_LONGTAG = (TiffTag)667;
private const TiffTag TIFFTAG_SHORTTAG = (TiffTag)668;
private const TiffTag TIFFTAG_RATIONALTAG = (TiffTag)669;
private const TiffTag TIFFTAG_FLOATTAG = (TiffTag)670;
private const TiffTag TIFFTAG_DOUBLETAG = (TiffTag)671;
private const TiffTag TIFFTAG_BYTETAG = (TiffTag)672;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
using (Bitmap bmp = new Bitmap(#"D:\Projects\ITests\images\IMG_2.jpg"))
{
// convert jpg image to tiff
byte[] tiffBytes = GetTiffImageBytes(bmp, false);
File.WriteAllBytes(#"D:\Projects\ITests\images\output.tif", tiffBytes);
//Invert the tiff image
Bitmap bmpTiff = new Bitmap(#"D:\Projects\ITests\images\output.tif");
Bitmap FBitmap = Transform(bmpTiff);
FBitmap.Save(#"D:\Projects\ITests\images\invOutput1.tif");
}
}
public static byte[] GetTiffImageBytes(Bitmap img, bool byScanlines)
{
try
{
byte[] raster = GetImageRasterBytes(img);
using (MemoryStream ms = new MemoryStream())
{
using (Tiff tif = Tiff.ClientOpen("InMemory", "w", ms, new TiffStream()))
{
if (tif == null)
return null;
tif.SetField(TiffTag.IMAGEWIDTH, img.Width);
tif.SetField(TiffTag.IMAGELENGTH, img.Height);
tif.SetField(TiffTag.COMPRESSION, Compression.CCITTFAX4);
tif.SetField(TiffTag.PHOTOMETRIC, Photometric.MINISWHITE);
tif.SetField(TiffTag.ROWSPERSTRIP, img.Height);
tif.SetField(TiffTag.XRESOLUTION, 200);
tif.SetField(TiffTag.YRESOLUTION, 200);
tif.SetField(TiffTag.SUBFILETYPE, 0);
tif.SetField(TiffTag.BITSPERSAMPLE, 1);
tif.SetField(TiffTag.FILLORDER, FillOrder.LSB2MSB);
tif.SetField(TiffTag.ORIENTATION, BitMiracle.LibTiff.Classic.Orientation.TOPLEFT);
tif.SetField(TiffTag.SAMPLESPERPIXEL, 1);
tif.SetField(TiffTag.RESOLUTIONUNIT, ResUnit.INCH);
tif.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG);
int tiffStride = tif.ScanlineSize();
int stride = raster.Length / img.Height;
if (byScanlines)
{
// raster stride MAY be bigger than TIFF stride (due to padding in raster bits)
for (int i = 0, offset = 0; i < img.Height; i++)
{
bool res = tif.WriteScanline(raster, offset, i, 0);
if (!res)
return null;
offset += stride;
}
}
else
{
if (tiffStride < stride)
{
// raster stride is bigger than TIFF stride
// this is due to padding in raster bits
// we need to create correct TIFF strip and write it into TIFF
byte[] stripBits = new byte[tiffStride * img.Height];
for (int i = 0, rasterPos = 0, stripPos = 0; i < img.Height; i++)
{
System.Buffer.BlockCopy(raster, rasterPos, stripBits, stripPos, tiffStride);
rasterPos += stride;
stripPos += tiffStride;
}
// Write the information to the file
int n = tif.WriteEncodedStrip(0, stripBits, stripBits.Length);
if (n <= 0)
return null;
}
else
{
// Write the information to the file
int n = tif.WriteEncodedStrip(0, raster, raster.Length);
if (n <= 0)
return null;
}
}
}
return ms.GetBuffer();
}
}
catch (Exception)
{
return null;
}
}
public static byte[] GetImageRasterBytes(Bitmap img)
{
// Specify full image
Rectangle rect = new Rectangle(0, 0, img.Width, img.Height);
Bitmap bmp = img;
byte[] bits = null;
try
{
// Lock the managed memory
if (img.PixelFormat != PixelFormat.Format1bppIndexed)
bmp = convertToBitonal(img);
BitmapData bmpdata = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
// Declare an array to hold the bytes of the bitmap.
bits = new byte[bmpdata.Stride * bmpdata.Height];
// Copy the sample values into the array.
Marshal.Copy(bmpdata.Scan0, bits, 0, bits.Length);
// Release managed memory
bmp.UnlockBits(bmpdata);
}
finally
{
if (bmp != img)
bmp.Dispose();
}
return bits;
}
private static Bitmap convertToBitonal(Bitmap original)
{
int sourceStride;
byte[] sourceBuffer = extractBytes(original, out sourceStride);
// Create destination bitmap
Bitmap destination = new Bitmap(original.Width, original.Height,
PixelFormat.Format1bppIndexed);
destination.SetResolution(original.HorizontalResolution, original.VerticalResolution);
// Lock destination bitmap in memory
BitmapData destinationData = destination.LockBits(
new Rectangle(0, 0, destination.Width, destination.Height),
ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
// Create buffer for destination bitmap bits
int imageSize = destinationData.Stride * destinationData.Height;
byte[] destinationBuffer = new byte[imageSize];
int sourceIndex = 0;
int destinationIndex = 0;
int pixelTotal = 0;
byte destinationValue = 0;
int pixelValue = 128;
int height = destination.Height;
int width = destination.Width;
int threshold = 500;
for (int y = 0; y < height; y++)
{
sourceIndex = y * sourceStride;
destinationIndex = y * destinationData.Stride;
destinationValue = 0;
pixelValue = 128;
for (int x = 0; x < width; x++)
{
// Compute pixel brightness (i.e. total of Red, Green, and Blue values)
pixelTotal = sourceBuffer[sourceIndex + 1] + sourceBuffer[sourceIndex + 2] +
sourceBuffer[sourceIndex + 3];
if (pixelTotal > threshold)
destinationValue += (byte)pixelValue;
if (pixelValue == 1)
{
destinationBuffer[destinationIndex] = destinationValue;
destinationIndex++;
destinationValue = 0;
pixelValue = 128;
}
else
{
pixelValue >>= 1;
}
sourceIndex += 4;
}
if (pixelValue != 128)
destinationBuffer[destinationIndex] = destinationValue;
}
Marshal.Copy(destinationBuffer, 0, destinationData.Scan0, imageSize);
destination.UnlockBits(destinationData);
return destination;
}
private static byte[] extractBytes(Bitmap original, out int stride)
{
Bitmap source = null;
try
{
// If original bitmap is not already in 32 BPP, ARGB format, then convert
if (original.PixelFormat != PixelFormat.Format32bppArgb)
{
source = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
source.SetResolution(original.HorizontalResolution, original.VerticalResolution);
using (Graphics g = Graphics.FromImage(source))
{
g.DrawImageUnscaled(original, 0, 0);
}
}
else
{
source = original;
}
// Lock source bitmap in memory
BitmapData sourceData = source.LockBits(
new Rectangle(0, 0, source.Width, source.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
// Copy image data to binary array
int imageSize = sourceData.Stride * sourceData.Height;
byte[] sourceBuffer = new byte[imageSize];
Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, imageSize);
// Unlock source bitmap
source.UnlockBits(sourceData);
stride = sourceData.Stride;
return sourceBuffer;
}
finally
{
if (source != original)
source.Dispose();
}
}
public Bitmap Transform(Bitmap bitmapImage)
{
var bitmapRead = bitmapImage.LockBits(new Rectangle(0, 0, bitmapImage.Width, bitmapImage.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);
var bitmapLength = bitmapRead.Stride * bitmapRead.Height;
var bitmapBGRA = new byte[bitmapLength];
Marshal.Copy(bitmapRead.Scan0, bitmapBGRA, 0, bitmapLength);
bitmapImage.UnlockBits(bitmapRead);
for (int i = 0; i < bitmapLength; i += 4)
{
bitmapBGRA[i] = (byte)(255 - bitmapBGRA[i]);
bitmapBGRA[i + 1] = (byte)(255 - bitmapBGRA[i + 1]);
bitmapBGRA[i + 2] = (byte)(255 - bitmapBGRA[i + 2]);
// [i + 3] = ALPHA.
}
var bitmapWrite = bitmapImage.LockBits(new Rectangle(0, 0, bitmapImage.Width, bitmapImage.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppPArgb);
Marshal.Copy(bitmapBGRA, 0, bitmapWrite.Scan0, bitmapLength);
bitmapImage.UnlockBits(bitmapWrite);
return bitmapImage;
}
}
You should invert image bytes in GetTiffImageBytes method, before writing them to TIFF. Also, the Transform method converts bi-level image to 32bpp one and that is why you get LZW compressed image in the end.
So, add the following code
for (int k = 0; k < raster.Length; k++)
raster[k] = (byte)(~raster[k]);
after byte[] raster = GetImageRasterBytes(img); in GetTiffImageBytes method. This will invert image bytes. And don't use the following code
//Invert the tiff image
Bitmap bmpTiff = new Bitmap(#"D:\Projects\ITests\images\output.tif");
Bitmap FBitmap = Transform(bmpTiff);
FBitmap.Save(#"D:\Projects\ITests\images\invOutput1.tif");

Continuously Updating Silverlight Image control from Byte[] in Backgroundworker (about every 100 ms)

I am new to posting in forums, as I usually can help my self through searches, but am really stuck here...
I have written an ASP.NET C# WebApp, which also needs to incorporate a client side serial com interface to a device that basically does live scanning, and streams the images scanned through the serial (USB) interface (These images need to be refreshed Continuously, approximately every 100ms, therefore basically creating an "animation" effect)
To get this to run Client side, I have written a small Silverlight App. running In Browser, with the following code, using a Backgroundworker trying to separate Serial Comms from UI, and refreshing the Silverlight image control from the Byte Arrays received.
I have VERY SIMILAR code working 100% in WinForms, my only issue in Silverlight, is that no image ever gets displayed in my image control.
Below is the relevant WinForms code followed by the corresponding Silverlight code.
My current suspicion is that the image is never rendered as Silverlight only allows for PixelFormat of 32bppArgb where I need to use PixelFormat.Format8bppIndexed, as per my WinForms CreateBitmap() method below.
If this is indeed the issue, I cannot find any way to create this format of Bitmap in Silverlight.
/////////////// WINFORMS CODE (Timer1 Interval = 100ms) //////////////////
private void timer1_Tick(object sender, EventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(
delegate(object o, DoWorkEventArgs args)
{
BackgroundWorker b = o as BackgroundWorker;
int BytesToRead = COMport.Read(ReceiveBuffer);
for (int i = 0; i < BytesToRead; i++)
{
//Code that copies ReceiveBuffer to byte[] LiveImgArr
Bitmap liveBMP = CreateBitmap(LiveImgArr, imgWidth, imgHeight);
bw.ReportProgress(i, liveBMP);
}
});
bw.ProgressChanged += new ProgressChangedEventHandler(
delegate(object o, ProgressChangedEventArgs args)
{
pictureBox1.Image = (Bitmap)args.UserState;
});
bw.RunWorkerAsync();
}
private Bitmap CreateBitmap(byte[] buffer, int width, int height)
{
Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
System.Runtime.InteropServices.Marshal.Copy(buffer, 0, bmpData.Scan0, width * height);
bmp.UnlockBits(bmpData);
ColorPalette pal = bmp.Palette;
for (int i = 0; i < 256; i++)
{
pal.Entries[i] = Color.FromArgb(i, i, i);
}
bmp.Palette = pal;
return bmp;
}
//////////////////////////////////////// END /////////////////////////////////////////
//////////////////////////////// SilverLight Code ///////////////////////////////////
public void StartTimer()//object o, RoutedEventArgs sender)
{
System.Windows.Threading.DispatcherTimer CommsTimer = new System.Windows.Threading.DispatcherTimer();
CommsTimer.Interval = new TimeSpan(0, 0, 0, 0, 100); // 100 Milliseconds
CommsTimer.Tick += new EventHandler(CommsTimer_Tick);
CommsTimer.Start();
}
public void CommsTimer_Tick(object o, EventArgs sender)
{
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(
delegate(object b, DoWorkEventArgs args)
{
BackgroundWorker obw = b as BackgroundWorker;
int BytesToRead = COMport.Read(ReceiveBuffer);
for (int i = 0; i < BytesToRead; i++)
{
//Code that copies ReceiveBuffer to byte[] LiveImgArr
bw.ReportProgress(i, LiveImgArr);
}
}
});
bw.ProgressChanged += new ProgressChangedEventHandler(
delegate(object j, ProgressChangedEventArgs args)
{
byte[] imgByte = (byte[])args.UserState;
using (MemoryStream ms = new MemoryStream(imgByte, 0, imgByte.Length))
{
BitmapImage bmp = new BitmapImage();
bmp.SetSource(ms);
this.image1.Source = bmp;
}
});
//////////////////////////////////////// END //////////////////////////////////////////
///////////////////////////////// NEW CODE AS PER CLEMENTS/////////////////////////////
bw.ProgressChanged += new ProgressChangedEventHandler(
delegate(object j, ProgressChangedEventArgs args)
{
byte[] imgByte = (byte[])args.UserState;
WriteableBitmap wbmp = new WriteableBitmap(208, 208);
int[] wbmpArray = wbmp.Pixels;
for (int pixelIndex = 0; pixelIndex < imgByte.Length; pixelIndex++)
{
byte alpha = 128;
byte red = 255;
byte green = 255;
byte blue = 255;
double scaleAlpha = alpha / 255.0;
// we are not using scaleAlpha here
//wbmp.Pixels[pixelIndex] =
// (alpha << 24)
// | (red << 16)
// | (green << 8)
// | blue;
// notice the alpha value is NOT scaled
// it’s also very important to scale BEFORE
// shifting the values
wbmp.Pixels[pixelIndex] =
(alpha << 24)
| ((byte)(red * scaleAlpha) << 16)
| ((byte)(green * scaleAlpha) << 8)
| (byte)(blue * scaleAlpha);
}
wbmp.Invalidate();
this.image1.Source = wbmp;
//ImageBrush imgBrush = new ImageBrush();
//imgBrush.ImageSource = wbmp;
//imgRect.Fill = imgBrush;
});
BitmapImage.SetSource only accepts streams that contain an encoded image buffer (PNG or JPEG).
In order to set pixel data directly, you would have to use WriteableBitmap instead of BitmapImage. From the Remarks section in the class documentation:
• Construct an initially empty but dimensioned WriteableBitmap using
WriteableBitmap(Int32, Int32).
• Get the pixel array from Pixels.
• Loop through the array, setting the individual pixel values as
integer values that are evaluated as premultiplied ARGB32.
• Call Invalidate.
• To display the image in UI, use the WriteableBitmap as the source
for an imaging control such as Image, or as the source image for an
ImageBrush.
The following code fills a WriteableBitmap with a blue color and an opacity gradient from 0 to 100% from left to right by using only integer arithmetics:
var bitmap = new WriteableBitmap(500, 500);
int red = 0;
int green = 0;
int blue = 255;
for (int i = 0; i < bitmap.PixelWidth * bitmap.PixelHeight; i++)
{
int x = i % bitmap.PixelWidth;
int alpha = x * 256 / bitmap.PixelWidth;
bitmap.Pixels[i] =
(alpha << 24) |
((red * alpha / 256) << 16) |
((green * alpha / 256) << 8) |
(blue * alpha / 256);
}
bitmap.Invalidate();
image.Source = bitmap;

Resources