Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Convert from Bitmap to Pix - #1

Added support for converting from a 32bpp or 24bpp Bitmap to Leptonicas
Pix structure. Also did some reworked Pix to make data access more like
System.Drawing.Bitmap.
  • Loading branch information...
commit ee2f606fe1bc5089e2b143318b64e7ba336172b0 1 parent 5621d6e
@charlesw authored
Showing with 1,650 additions and 399 deletions.
  1. +13 −0 Tesseract.Net20/AddNewColorStatus.cs
  2. +162 −0 Tesseract.Net20/BitmapHelper.cs
  3. +97 −0 Tesseract.Net20/BitmapToPixConverter.cs
  4. +94 −0 Tesseract.Net20/Color.cs
  5. +2 −2 Tesseract.Net20/Engine.cs
  6. +339 −10 Tesseract.Net20/Interop/LeptonicaApi.cs
  7. +0 −12 Tesseract.Net20/Interop/PixColormap.cs
  8. +18 −0 Tesseract.Net20/LeptonicaException.cs
  9. +2 −2 Tesseract.Net20/PageIterator.cs
  10. +25 −261 Tesseract.Net20/Pix.cs
  11. +143 −0 Tesseract.Net20/PixColorMap.cs
  12. +16 −0 Tesseract.Net20/PixConverter.cs
  13. +198 −0 Tesseract.Net20/PixData.cs
  14. +8 −1 Tesseract.Net20/Tesseract.Net20.csproj
  15. +16 −3 Tesseract.Net40/Tesseract.Net40.csproj
  16. +16 −3 Tesseract.Net45/Tesseract.Net45.csproj
  17. +6 −0 Tesseract.Tests.Console/App.config
  18. +17 −0 Tesseract.Tests.Console/Program.cs
  19. +36 −0 Tesseract.Tests.Console/Properties/AssemblyInfo.cs
  20. +68 −0 Tesseract.Tests.Console/Tesseract.Tests.Console.csproj
  21. BIN  Tesseract.Tests/Data/Conversion/photo.bmp
  22. BIN  Tesseract.Tests/Data/Conversion/photo.gif
  23. BIN  Tesseract.Tests/Data/Conversion/photo.jpg
  24. BIN  Tesseract.Tests/Data/Conversion/photo.png
  25. BIN  Tesseract.Tests/Data/Conversion/photo.tif
  26. BIN  Tesseract.Tests/Data/Conversion/photo_24.bmp
  27. BIN  Tesseract.Tests/Data/Conversion/photo_24.png
  28. BIN  Tesseract.Tests/Data/Conversion/photo_32.png
  29. BIN  Tesseract.Tests/Data/Conversion/photo_8.bmp
  30. BIN  Tesseract.Tests/Data/Conversion/photo_8.png
  31. +38 −0 Tesseract.Tests/Leptonica/BitmapHelperTests.cs
  32. +71 −0 Tesseract.Tests/Leptonica/ConvertBitmapToPixTests.cs
  33. +42 −104 Tesseract.Tests/Leptonica/PixDataAccessTests.cs
  34. +37 −1 Tesseract.Tests/Tesseract.Tests.csproj
  35. +12 −0 Tesseract.sln
  36. +174 −0 hacks/BitmapToPixConverter.cs
View
13 Tesseract.Net20/AddNewColorStatus.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tesseract
+{
+ public enum AddNewColorStatus
+ {
+ Ok = 0,
+ Error = 1,
+ NotEnoughSpace = 2
+ }
+}
View
162 Tesseract.Net20/BitmapHelper.cs
@@ -0,0 +1,162 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace Tesseract
+{
+ /// <summary>
+ /// Description of BitmapHelper.
+ /// </summary>
+ public static unsafe class BitmapHelper
+ {
+ #region Bitmap Data Access
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static byte GetDataBit(byte* data, int index)
+ {
+ return (byte)((*(data + (index >> 3)) >> (index & 0x7)) & 1);
+ }
+
+ #if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public static void SetDataBit(byte* data, int index, byte value)
+ {
+ byte* wordPtr = data + (index >> 3);
+ *wordPtr &= (byte)~(0x80 >> (index & 7)); // clear bit, note first pixel in the byte is most significant (1000 0000)
+ *wordPtr |= (byte)((value & 1) << (7 - (index & 7))); // set bit, if value is 1
+ }
+
+ #if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public static byte GetDataQBit(byte* data, int index)
+ {
+ return (byte)((*(data + (index >> 1)) >> (4 * (index & 1))) & 0xF);
+ }
+
+ #if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public static void SetDataQBit(byte* data, int index, byte value)
+ {
+ byte* wordPtr = data + (index >> 1);
+ *wordPtr &= (byte)~(0xF0 >> (4 * (index & 1))); // clears qbit located at index, note like bit the qbit corresponding to the first pixel is the most significant (0xF0)
+ *wordPtr |= (byte)((value & 0x0F) << (4 - (4 * (index & 1)))); // applys qbit to n
+ }
+
+ #if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public static byte GetDataByte(byte* data, int index)
+ {
+ return *(data + index);
+ }
+
+ #if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public static void SetDataByte(byte* data, int index, byte value)
+ {
+ *(data + index) = value;
+ }
+
+ #if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public static ushort GetDataUInt16(ushort* data, int index)
+ {
+ return *(data + index);
+ }
+
+ #if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public static void SetDataUInt16(ushort* data, int index, ushort value)
+ {
+ *(data + index) = value;
+ }
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public static uint GetDataUInt32(uint* data, int index)
+ {
+ return *(data + index);
+ }
+
+ #if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ #endif
+ public static void SetDataUInt32(uint* data, int index, uint value)
+ {
+ *(data + index) = value;
+ }
+
+
+ #endregion
+
+ #region PixelFormat conversion
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static uint ConvertRgb555ToRGBA(uint val)
+ {
+ var red = ((val & 0x7C00) >> 10);
+ var green = ((val & 0x3E0) >> 5);
+ var blue = (val & 0x1F);
+
+ return ((red << 3 | red >> 2) << 24) |
+ ((green << 3 | green >> 2) << 16) |
+ ((blue << 3 | blue >> 2) << 8) |
+ 0xFF;
+ }
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static uint ConvertRgb565ToRGBA(uint val)
+ {
+ var red = ((val & 0xF800) >> 11);
+ var green = ((val & 0x7E0) >> 5);
+ var blue = (val & 0x1F);
+
+ return ((red << 3 | red >> 2) << 24) |
+ ((green << 2 | green >> 4) << 16) |
+ ((blue << 3 | blue >> 2) << 8) |
+ 0xFF;
+ }
+
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static uint ConvertArgb1555ToRGBA(uint val)
+ {
+ var alpha = ((val & 0x8000) >> 15);
+ var red = ((val & 0x7C00) >> 10);
+ var green = ((val & 0x3E0) >> 5);
+ var blue = (val & 0x1F);
+
+ return ((red << 3 | red >> 2) << 24) |
+ ((green << 3 | green >> 2) << 16) |
+ ((blue << 3 | blue >> 2) << 8) |
+ ((alpha << 8) - alpha); // effectively alpha * 255, only works as alpha will be either 0 or 1
+ }
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static uint EncodeAsRGBA(byte red, byte green, byte blue, byte alpha)
+ {
+ return (uint)((red << 24) |
+ (green << 16) |
+ (blue << 8) |
+ alpha);
+ }
+
+ #endregion
+ }
+}
View
97 Tesseract.Net20/BitmapToPixConverter.cs
@@ -0,0 +1,97 @@
+
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+
+namespace Tesseract
+{
+ /// <summary>
+ /// Description of BitmapToPixConverter.
+ /// </summary>
+ public class BitmapToPixConverter
+ {
+ public BitmapToPixConverter()
+ {
+ }
+
+ public Pix Convert(Bitmap img)
+ {
+ var pixDepth = GetPixDepth(img.PixelFormat);
+ var pix = Pix.Create(img.Width, img.Height, pixDepth);
+ // TODO: Set X and Y resolution
+ BitmapData imgData = null;
+ PixData pixData = null;
+ try {
+ // transfer data
+ imgData = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadOnly, img.PixelFormat);
+ pixData = pix.GetData();
+
+ if (imgData.PixelFormat == PixelFormat.Format32bppArgb) {
+ TransferDataFormat32bppArgb(imgData, pixData);
+ } else if (imgData.PixelFormat == PixelFormat.Format24bppRgb) {
+ TransferDataFormat24bppRgb(imgData, pixData);
+ }
+ return pix;
+ } catch (Exception) {
+ pix.Dispose();
+ throw;
+ } finally {
+ if (imgData != null) {
+ img.UnlockBits(imgData);
+ }
+ }
+ }
+
+ private int GetPixDepth(PixelFormat pixelFormat)
+ {
+ switch (pixelFormat) {
+ case PixelFormat.Format32bppArgb:
+ case PixelFormat.Format24bppRgb:
+ return 32;
+ default:
+ throw new InvalidOperationException(String.Format("Source bitmap's pixel format {0} is not supported.", pixelFormat));
+ }
+ }
+
+ private unsafe void TransferDataFormat32bppArgb(BitmapData imgData, PixData pixData)
+ {
+ var imgFormat = imgData.PixelFormat;
+ var height = imgData.Height;
+ var width = imgData.Width;
+
+ for (int y = 0; y < height; y++) {
+ byte* imgLine = (byte*)imgData.Scan0 + (y * imgData.Stride);
+ uint* pixLine = (uint*)pixData.Data + (y * pixData.WordsPerLine);
+
+ for (int x = 0; x < width; x++) {
+ byte* pixelPtr = imgLine + (x << 2);
+ byte blue = *pixelPtr;
+ byte green = *(pixelPtr + 1);
+ byte red = *(pixelPtr + 2);
+ byte alpha = *(pixelPtr + 3);
+ PixData.SetDataFourByte(pixLine, x, BitmapHelper.EncodeAsRGBA(red, green, blue, alpha));
+ }
+ }
+ }
+
+ private unsafe void TransferDataFormat24bppRgb(BitmapData imgData, PixData pixData)
+ {
+ var imgFormat = imgData.PixelFormat;
+ var height = imgData.Height;
+ var width = imgData.Width;
+
+ for (int y = 0; y < height; y++) {
+ byte* imgLine = (byte*)imgData.Scan0 + (y * imgData.Stride);
+ uint* pixLine = (uint*)pixData.Data + (y * pixData.WordsPerLine);
+
+ for (int x = 0; x < width; x++) {
+ byte* pixelPtr = imgLine + x*3;
+ byte blue = pixelPtr[0];
+ byte green = pixelPtr[1];
+ byte red = pixelPtr[2];
+ PixData.SetDataFourByte(pixLine, x, BitmapHelper.EncodeAsRGBA(red, green, blue, 255));
+ }
+ }
+ }
+ }
+}
View
94 Tesseract.Net20/Color.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Tesseract
+{
+ [StructLayout(LayoutKind.Sequential, Pack=1)]
+ public struct Color : IEquatable<Color>
+ {
+ private byte red;
+ private byte blue;
+ private byte green;
+ private byte alpha;
+
+ public Color(byte red, byte green, byte blue, byte alpha = 255)
+ {
+ this.red = red;
+ this.green = green;
+ this.blue = blue;
+ this.alpha = alpha;
+ }
+
+ public byte Red { get { return red; } }
+ public byte Green { get { return green; } }
+ public byte Blue { get { return blue; } }
+ public byte Alpha { get { return alpha; } }
+
+ public static Color FromRgba(uint value)
+ {
+ return new Color(
+ (byte)((value >> 24) & 0xFF),
+ (byte)((value >> 16) & 0xFF),
+ (byte)((value >> 8) & 0xFF),
+ (byte)(value & 0xFF));
+ }
+
+ public uint ToRGBA()
+ {
+ return BitmapHelper.EncodeAsRGBA(red, green, blue, alpha);
+ }
+
+ public static explicit operator System.Drawing.Color(Color color)
+ {
+ return System.Drawing.Color.FromArgb(color.alpha, color.red, color.green, color.blue);
+ }
+
+ public static explicit operator Color(System.Drawing.Color color)
+ {
+ return new Color(color.R, color.G, color.B, color.A);
+ }
+
+
+ #region Equals and GetHashCode implementation
+ public override bool Equals(object obj)
+ {
+ return (obj is Color) && Equals((Color)obj);
+ }
+
+ public bool Equals(Color other)
+ {
+ return this.red == other.red && this.blue == other.blue && this.green == other.green && this.alpha == other.alpha;
+ }
+
+ public override int GetHashCode()
+ {
+ int hashCode = 0;
+ unchecked {
+ hashCode += 1000000007 * red.GetHashCode();
+ hashCode += 1000000009 * blue.GetHashCode();
+ hashCode += 1000000021 * green.GetHashCode();
+ hashCode += 1000000033 * alpha.GetHashCode();
+ }
+ return hashCode;
+ }
+
+ public static bool operator ==(Color lhs, Color rhs)
+ {
+ return lhs.Equals(rhs);
+ }
+
+ public static bool operator !=(Color lhs, Color rhs)
+ {
+ return !(lhs == rhs);
+ }
+ #endregion
+
+ public override string ToString()
+ {
+ return String.Format("Color(0x{0:X})", ToRGBA());
+ }
+
+ }
+}
View
4 Tesseract.Net20/Engine.cs
@@ -89,7 +89,7 @@ private void Initialise(string datapath, string language, EngineMode engineMode)
}
}
- public Page Process(IPix image, PageSegMode? pageSegMode = null)
+ public Page Process(Pix image, PageSegMode? pageSegMode = null)
{
return Process(image, new Rect(0, 0, image.Width, image.Height), pageSegMode);
}
@@ -103,7 +103,7 @@ public Page Process(IPix image, PageSegMode? pageSegMode = null)
/// <param name="image">The image to process.</param>
/// <param name="region">The image region to process.</param>
/// <returns>A result iterator</returns>
- public Page Process(IPix image, Rect region, PageSegMode? pageSegMode = null)
+ public Page Process(Pix image, Rect region, PageSegMode? pageSegMode = null)
{
if (image == null) throw new ArgumentNullException("image");
if (region.X1 < 0 || region.Y1 < 0 || region.X2 > image.Width || region.Y2 > image.Height)
View
349 Tesseract.Net20/Interop/LeptonicaApi.cs
@@ -7,7 +7,6 @@ namespace Tesseract.Interop
{
public unsafe static class LeptonicaApi
{
-
static LeptonicaApi()
{
var type = typeof(LeptonicaApi);
@@ -15,28 +14,358 @@ static LeptonicaApi()
EmbeddedDllLoader.Instance.LoadEmbeddedDll(type.Assembly, type.Namespace, "liblept168.dll");
}
+
+ #region Pix
+
[DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixCreate")]
- public static unsafe extern IntPtr Create(int width, int height, int depth);
+ public static unsafe extern IntPtr pixCreate(int width, int height, int depth);
[DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixDestroy")]
- public static extern void Destroy(ref IntPtr handle);
+ public static extern void pixDestroy(ref IntPtr pix);
[DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetWidth")]
- public static extern int GetWidth(IntPtr handle);
+ public static extern int pixGetWidth(IntPtr pix);
[DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetHeight")]
- public static extern int GetHeight(IntPtr handle);
+ public static extern int pixGetHeight(IntPtr pix);
+
[DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetDepth")]
- public static extern int GetDepth(IntPtr handle);
+ public static extern int pixGetDepth(IntPtr pix);
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetXRes")]
+ public static extern int pixGetXRes(IntPtr pix);
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetYRes")]
+ public static extern int pixGetYRes(IntPtr pix);
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetResolution")]
+ public static extern int pixGetResolution(IntPtr pix, out int xres, out int yres);
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetWpl")]
+ public static extern int pixGetWpl(IntPtr pix);
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixSetXRes")]
+ public static extern int pixSetXRes(IntPtr pix, int xres);
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixSetYRes")]
+ public static extern int pixSetYRes(IntPtr pix, int yres);
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixSetResolution")]
+ public static extern int pixSetResolution(IntPtr pix, int xres, int yres);
+
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixScaleResolution")]
+ public static extern int pixScaleResolution(IntPtr pix, float xscale, float yscale);
[DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetData")]
- public static extern uint* GetData(IntPtr handle);
+ public static extern IntPtr pixGetData(IntPtr pix);
- [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixRead")]
- public static extern IntPtr LoadFromFile(string filename);
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixGetInputFormat")]
+ public static extern ImageFormat pixGetInputFormat(IntPtr pix);
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixSetInputFormat")]
+ public static extern int pixSetInputFormat(IntPtr pix, ImageFormat inputFormat);
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixEndianByteSwap")]
+ public static extern int pixEndianByteSwap(IntPtr pix);
[DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixRead")]
- public static extern int Save(string filename, IntPtr handle, ImageFormat format);
+ public static extern IntPtr pixRead(string filename);
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixWrite")]
+ public static extern int pixWrite(string filename, IntPtr handle, ImageFormat format);
+
+
+ #endregion
+
+ #region Color map
+
+ // Color map creation and deletion
+
+ /// <summary>
+ /// Creates a new colormap with the specified <paramref name="depth"/>.
+ /// </summary>
+ /// <param name="depth">The depth of the pix in bpp, can be 2, 4, or 8</param>
+ /// <returns>The pointer to the color map, or null on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCreate")]
+ public static extern IntPtr pixcmapCreate(int depth);
+
+ /// <summary>
+ /// Creates a new colormap of the specified <paramref name="depth"/> with random colors where the first color can optionally be set to black, and the last optionally set to white.
+ /// </summary>
+ /// <param name="depth">The depth of the pix in bpp, can be 2, 4, or 8</param>
+ /// <param name="hasBlack">If set to 1 the first color will be black.</param>
+ /// <param name="hasWhite">If set to 1 the last color will be white.</param>
+ /// <returns>The pointer to the color map, or null on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCreateRandom")]
+ public static extern IntPtr pixcmapCreateRandom(int depth, int hasBlack, int hasWhite);
+
+ /// <summary>
+ /// Creates a new colormap of the specified <paramref name="depth"/> with equally spaced gray color values.
+ /// </summary>
+ /// <param name="depth">The depth of the pix in bpp, can be 2, 4, or 8</param>
+ /// <param name="levels">The number of levels (must be between 2 and 2^<paramref name="depth"/></param>
+ /// <returns>The pointer to the colormap, or null on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCreateLinear")]
+ public static extern IntPtr pixcmapCreateLinear(int depth, int levels);
+
+ /// <summary>
+ /// Performs a deep copy of the color map.
+ /// </summary>
+ /// <param name="cmap">The pointer to the colormap instance.</param>
+ /// <returns>The pointer to the colormap, or null on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCopy")]
+ public static extern IntPtr pixcmapCopy(IntPtr cmaps);
+
+ /// <summary>
+ /// Destorys and cleans up any memory used by the color map.
+ /// </summary>
+ /// <param name="cmap">The pointer to the colormap instance, set to null on success.</param>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapDestroy")]
+ public static extern void pixcmapDestroy(ref IntPtr cmap);
+
+ // colormap metadata (depth, count, etc)
+
+ /// <summary>
+ /// Gets the number of color entries in the color map.
+ /// </summary>
+ /// <param name="cmap">The pointer to the colormap instance.</param>
+ /// <returns>Returns the number of color entries in the color map, or 0 on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetCount")]
+ public static extern int pixcmapGetCount(IntPtr cmap);
+
+ /// <summary>
+ /// Gets the number of free color entries in the color map.
+ /// </summary>
+ /// <param name="cmap">The pointer to the colormap instance.</param>
+ /// <returns>Returns the number of free color entries in the color map, or 0 on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetFreeCount")]
+ public static extern int pixcmapGetFreeCount(IntPtr cmap);
+
+
+ /// <returns>Returns color maps depth, or 0 on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetDepth")]
+ public static extern int pixcmapGetDepth(IntPtr cmap);
+
+ /// <summary>
+ /// Gets the minimum pix depth required to support the color map.
+ /// </summary>
+ /// <param name="cmap">The pointer to the colormap instance.</param>
+ /// <param name="minDepth">Returns the minimum depth to support the colormap</param>
+ /// <returns>Returns 0 if OK, 1 on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetMinDepth")]
+ public static extern int pixcmapGetMinDepth(IntPtr cmap, out int minDepth);
+
+ // colormap - color addition\clearing
+
+ /// <summary>
+ /// Removes all colors from the color map by setting the count to zero.
+ /// </summary>
+ /// <param name="cmap">The pointer to the colormap instance.</param>
+ /// <returns>Returns 0 if OK, 1 on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapClear")]
+ public static extern int pixcmapClear(IntPtr cmap);
+
+ /// <summary>
+ /// Adds the color to the pix color map if their is room.
+ /// </summary>
+ /// <returns>Returns 0 if OK, 1 on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapAddColor")]
+ public static extern int pixcmapAddColor(IntPtr cmap, int redValue, int greenValue, int blueValue);
+
+ /// <summary>
+ /// Adds the specified color if it doesn't already exist, returning the colors index in the data array.
+ /// </summary>
+ /// <param name="cmap">The pointer to the colormap instance.</param>
+ /// <param name="redValue">The red value</param>
+ /// <param name="greenValue">The green value</param>
+ /// <param name="blueValue">The blue value</param>
+ /// <param name="colorIndex">The index of the new color if it was added, or the existing color if it already existed.</param>
+ /// <returns>Returns 0 for success, 1 for error, 2 for not enough space.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapAddNewColor")]
+ public static extern int pixcmapAddNewColor(IntPtr cmap, int redValue, int greenValue, int blueValue, out int colorIndex);
+
+ /// <summary>
+ /// Adds the specified color if it doesn't already exist, returning the color's index in the data array.
+ /// </summary>
+ /// <remarks>
+ /// If the color doesn't exist and there is not enough room to add a new color return the nearest color.
+ /// </remarks>
+ /// <param name="cmap">The pointer to the colormap instance.</param>
+ /// <param name="redValue">The red value</param>
+ /// <param name="greenValue">The green value</param>
+ /// <param name="blueValue">The blue value</param>
+ /// <param name="colorIndex">The index of the new color if it was added, or the existing color if it already existed.</param>
+ /// <returns>Returns 0 for success, 1 for error, 2 for not enough space.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapAddNearestColor")]
+ public static extern int pixcmapAddNearestColor(IntPtr cmap, int redValue, int greenValue, int blueValue, out int colorIndex);
+
+ /// <summary>
+ /// Checks if the color already exists or if their is enough room to add it.
+ /// </summary>
+ /// <param name="cmap">The pointer to the colormap instance.</param>
+ /// <param name="redValue">The red value</param>
+ /// <param name="greenValue">The green value</param>
+ /// <param name="blueValue">The blue value</param>
+ /// <param name="usable">Returns 1 if usable; 0 if not.</param>
+ /// <returns>Returns 0 if OK, 1 on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapUsableColor")]
+ public static extern int pixcmapUsableColor(IntPtr cmap, int redValue, int greenValue, int blueValue, out int usable);
+
+ /// <summary>
+ /// Adds a color (black\white) if not already there returning it's index through <paramref name="index"/>.
+ /// </summary>
+ /// <param name="cmap">The pointer to the colormap instance.</param>
+ /// <param name="color">The color to add (0 for black; 1 for white)</param>
+ /// <param name="index">The index of the color.</param>
+ /// <returns>Returns 0 if OK; 1 on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapAddBlackOrWhite")]
+ public static extern int pixcmapAddBlackOrWhite(IntPtr cmap, int color, out int index);
+
+ /// <summary>
+ /// Sets the darkest color in the colormap to black, if <paramref name="setBlack"/> is 1.
+ /// Sets the lightest color in the colormap to white if <paramref name="setWhite"/> is 1.
+ /// </summary>
+ /// <param name="cmap">The pointer to the colormap instance.</param>
+ /// <param name="setBlack">0 for no operation; 1 to set darket color to black</param>
+ /// <param name="setBlack">0 for no operation; 1 to set lightest color to white</param>
+ /// <returns>Returns 0 if OK; 1 on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapSetBlackAndWhite")]
+ public static extern int pixcmapSetBlackAndWhite(IntPtr cmap, int setBlack, int setWhite);
+
+ // color access - color entry access
+
+ /// <summary>
+ /// Gets the color at the specified index.
+ /// </summary>
+ /// <param name="cmap">The pointer to the colormap instance.</param>
+ /// <param name="index">The index of the color entry.</param>
+ /// <param name="redValue">The color entry's red value.</param>
+ /// <param name="blueValue">The color entry's blue value.</param>
+ /// <param name="greenValue">The color entry's green value.</param>
+ /// <returns>Returns 0 if OK; 1 if not accessable (caller should check).</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetColor")]
+ public static extern int pixcmapGetColor(IntPtr cmap, int index, out int redValue, out int blueValue, out int greenValue);
+
+ /// <summary>
+ /// Gets the color at the specified index.
+ /// </summary>
+ /// <remarks>
+ /// The alpha channel will always be zero as it is not used in Leptonica color maps.
+ /// </remarks>
+ /// <param name="cmap">The pointer to the colormap instance.</param>
+ /// <param name="index">The index of the color entry.</param>
+ /// <param name="color">The color entry as 32 bit value</param>
+ /// <returns>Returns 0 if OK; 1 if not accessable (caller should check).</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetColor32")]
+ public static extern int pixcmapGetColor32(IntPtr cmap, int index, out int color);
+
+ /// <summary>
+ /// Sets a previously allocated color entry.
+ /// </summary>
+ /// <param name="cmap">The pointer to the colormap instance.</param>
+ /// <param name="index">The index of the colormap entry</param>
+ /// <param name="redValue"></param>
+ /// <param name="blueValue"></param>
+ /// <param name="greenValue"></param>
+ /// <returns>Returns 0 if OK; 1 if not accessable (caller should check).</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapResetColor")]
+ public static extern int pixcmapResetColor(IntPtr cmap, int index, int redValue, int blueValue, int greenValue);
+
+ /// <summary>
+ /// Gets the index of the color entry with the specified color, return 0 if found; 1 if not.
+ /// </summary>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetIndex")]
+ public static extern int pixcmapGetIndex(IntPtr cmap, int redValue, int blueValue, int greenValue, out int index);
+
+
+ /// <summary>
+ /// Returns 0 if the color exists in the color map; otherwise 1.
+ /// </summary>
+ /// <returns>Returns 0 if OK; 1 on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapHasColor")]
+ public static extern int pixcmapHasColor(IntPtr cmap, int color);
+
+
+ /// <summary>
+ /// Returns the number of unique grey colors including black and white.
+ /// </summary>
+ /// <returns>Returns 0 if OK; 1 on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCountGrayColors")]
+ public static extern int pixcmapCountGrayColors(IntPtr cmap, out int ngray);
+
+ /// <summary>
+ /// Finds the index of the color entry with the rank intensity.
+ /// </summary>
+ /// <returns>Returns 0 if OK; 1 on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapCountGrayColors")]
+ public static extern int pixcmapGetRankIntensity(IntPtr cmap, float rankVal, out int index);
+
+
+ /// <summary>
+ /// Finds the index of the color entry closest to the specified color.
+ /// </summary>
+ /// <returns>Returns 0 if OK; 1 on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetNearestIndex")]
+ public static extern int pixcmapGetNearestIndex(IntPtr cmap, int rVal, int bVal, int gVal, out int index);
+
+ /// <summary>
+ /// Finds the index of the color entry closest to the specified color.
+ /// </summary>
+ /// <remarks>
+ /// Should only be used on gray colormaps.
+ /// </remarks>
+ /// <returns>Returns 0 if OK; 1 on error.</returns>
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetNearestGrayIndex")]
+ public static extern int pixcmapGetNearestGrayIndex(IntPtr cmap, int val, out int index);
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetComponentRange")]
+ public static extern int pixcmapGetComponentRange(IntPtr cmap, int component, out int minVal, out int maxVal);
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGetExtremeValue")]
+ public static extern int pixcmapGetExtremeValue(IntPtr cmap, int type, out int rVal, out int gVal, out int bVal);
+
+ // color map conversion
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGrayToColor")]
+ public static extern IntPtr pixcmapGrayToColor(int color);
+
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapColorToGray")]
+ public static extern IntPtr pixcmapColorToGray(IntPtr cmaps, float redWeight, float greenWeight, float blueWeight);
+
+ // colormap serialization
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapColorToGray")]
+ public static extern int pixcmapToArrays(IntPtr cmap, out IntPtr redMap, out IntPtr blueMap, out IntPtr greenMap);
+
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapToRGBTable")]
+ public static extern int pixcmapToRGBTable(IntPtr cmap, out IntPtr colorTable, out int colorCount);
+
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapSerializeToMemory")]
+ public static extern int pixcmapSerializeToMemory(IntPtr cmap, out int components, out int colorCount, out IntPtr colorData, out int colorDataLength);
+
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapSerializeToMemory")]
+ public static extern IntPtr pixcmapSerializeToMemory(IntPtr colorData, int colorCount, int colorDataLength);
+
+ // colormap transformations
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapGammaTRC")]
+ public static extern int pixcmapGammaTRC(IntPtr cmap, float gamma, int minVal, int maxVal);
+
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapContrastTRC")]
+ public static extern int pixcmapContrastTRC(IntPtr cmap, float factor);
+
+ [DllImport(Constants.LeptonicaDllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pixcmapShiftIntensity")]
+ public static extern int pixcmapShiftIntensity(IntPtr cmap, float fraction);
+
+
+ #endregion
}
}
View
12 Tesseract.Net20/Interop/PixColormap.cs
@@ -1,12 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-using System.Text;
-
-namespace Tesseract.Interop
-{
- [StructLayout(LayoutKind.Sequential)]
- public struct PixColormap
- {
- }
-}
View
18 Tesseract.Net20/LeptonicaException.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tesseract
+{
+ [Serializable]
+ public class LeptonicaException : Exception
+ {
+ public LeptonicaException() { }
+ public LeptonicaException(string message) : base(message) { }
+ public LeptonicaException(string message, Exception inner) : base(message, inner) { }
+ protected LeptonicaException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context)
+ : base(info, context) { }
+ }
+}
View
4 Tesseract.Net20/PageIterator.cs
@@ -86,12 +86,12 @@ public PolyBlockType BlockType
get { return Interop.TessApi.PageIteratorBlockType(handle); }
}
- public IPix GetBinaryImage(PageIteratorLevel level)
+ public Pix GetBinaryImage(PageIteratorLevel level)
{
return Pix.Create(Interop.TessApi.PageIteratorGetBinaryImage(handle, level));
}
- public IPix GetImage(PageIteratorLevel level, int padding, out int x, out int y)
+ public Pix GetImage(PageIteratorLevel level, int padding, out int x, out int y)
{
return Pix.Create(Interop.TessApi.PageIteratorGetImage(handle, level, padding, out x, out y));
}
View
286 Tesseract.Net20/Pix.cs
@@ -1,81 +1,49 @@
using System;
using System.Collections.Generic;
using System.IO;
-using System.Runtime.CompilerServices;
-using System.Text;
namespace Tesseract
{
- /// <summary>
- /// Wraps leptonica's pix structure.
- /// </summary>
- public interface IPix : IDisposable
- {
- IntPtr Handle { get; }
- int Width { get; }
- int Height { get; }
- int Depth { get; }
-
- void Save(string filename, ImageFormat format = ImageFormat.Default);
- }
-
- public unsafe abstract class Pix : DisposableBase, IPix
+ public unsafe sealed class Pix : DisposableBase
{
private static readonly List<int> AllowedDepths = new List<int> { 1, 2, 4, 8, 16, 32 };
- protected IntPtr handle;
- protected readonly int width;
- protected readonly int height;
- protected readonly int depth;
- protected readonly int pixelCount;
- protected readonly uint* data;
+ private IntPtr handle;
+ private readonly int width;
+ private readonly int height;
+ private readonly int depth;
- public static IPix Create(int width, int height, int depth)
+ public static Pix Create(int width, int height, int depth)
{
- if(!AllowedDepths.Contains(depth))
+ if (!AllowedDepths.Contains(depth))
throw new ArgumentException("Depth must be 1, 2, 4, 8, 16, or 32 bits.", "depth");
if (width <= 0) throw new ArgumentException("Width must be greater than zero", "width");
if (height <= 0) throw new ArgumentException("Height must be greater than zero", "height");
- var handle = Interop.LeptonicaApi.Create(width, height, depth);
+ var handle = Interop.LeptonicaApi.pixCreate(width, height, depth);
if (handle == IntPtr.Zero) throw new InvalidOperationException("Failed to create pix, this normally occurs because the requested image size is too large, please check Standard Error Output.");
return Create(handle);
}
- public static IPix Create(IntPtr handle)
+ public static Pix Create(IntPtr handle)
{
if (handle == IntPtr.Zero) throw new ArgumentException("Pix handle must not be zero (null).", "handle");
- var depth = Interop.LeptonicaApi.GetDepth(handle);
- if (depth == 1) {
- return new Pix1Bit(handle);
- } else if (depth == 2) {
- return new Pix2Bit(handle);
- } else if(depth == 4) {
- return new Pix4Bit(handle);
- } else if(depth == 8) {
- return new Pix8Bit(handle);
- } else if (depth == 16) {
- return new Pix16Bit(handle);
- } else if (depth == 32) {
- return new Pix32Bit(handle);
- } else {
- throw new ArgumentException("Depth must be 1, 2, 4, 8, 16, or 32 bits.");
- }
+ return new Pix(handle);
}
- public static IPix LoadFromFile(string filename)
+ public static Pix LoadFromFile(string filename)
{
- var pixHandle = Interop.LeptonicaApi.LoadFromFile(filename);
+ var pixHandle = Interop.LeptonicaApi.pixRead(filename);
if (pixHandle == IntPtr.Zero) {
throw new IOException(String.Format("Failed to load image '{0}'.", filename));
}
return Create(pixHandle);
}
-
+
/// <summary>
/// Creates a new pix instance using an existing handle to a pix structure.
/// </summary>
@@ -83,21 +51,19 @@ public static IPix LoadFromFile(string filename)
/// Note that the resulting instance takes ownership of the data structure.
/// </remarks>
/// <param name="handle"></param>
- public Pix(IntPtr handle)
+ private Pix(IntPtr handle)
{
if (handle == IntPtr.Zero) throw new ArgumentNullException("handle");
this.handle = handle;
- this.width = Interop.LeptonicaApi.GetWidth(handle);
- this.height = Interop.LeptonicaApi.GetHeight(handle);
- this.depth = Interop.LeptonicaApi.GetDepth(handle);
- this.data = Interop.LeptonicaApi.GetData(handle);
- this.pixelCount = width * height;
+ this.width = Interop.LeptonicaApi.pixGetWidth(handle);
+ this.height = Interop.LeptonicaApi.pixGetHeight(handle);
+ this.depth = Interop.LeptonicaApi.pixGetHeight(handle);
}
-
+
public void Save(string filename, ImageFormat format = ImageFormat.Default)
{
- if (Interop.LeptonicaApi.Save(filename, handle, format) != 0) {
+ if (Interop.LeptonicaApi.pixWrite(filename, handle, format) != 0) {
throw new IOException(String.Format("Failed to save image '{0}'.", filename));
}
}
@@ -117,221 +83,19 @@ public int Depth
get { return depth; }
}
- public IntPtr Handle
- {
- get { return handle; }
- }
-
- protected override void Dispose(bool disposing)
- {
- Interop.LeptonicaApi.Destroy(ref handle);
- }
- }
-
- public unsafe sealed class Pix1Bit : Pix
- {
- public Pix1Bit(IntPtr handle)
- : base(handle)
- {
- if (Depth != 1) throw new TesseractException("Pix must have a depth of 1 bit.");
- }
-
- // data access routines
- public uint this[int index]
- {
-#if Net45
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
-#endif
- get
- {
- return (*(this.data + ((index) >> 5)) >> (31 - ((index) & 31))) & 1;
- }
-#if Net45
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
-#endif
- set
- {
- uint* wordPtr = data + ((index) >> 5);
- *wordPtr &= ~(0x80000000 >> ((index) & 31));
- *wordPtr |= (value << (31 - ((index) & 31)));
- }
- }
- }
-
- public unsafe sealed class Pix2Bit : Pix
- {
- public Pix2Bit(IntPtr handle)
- : base(handle)
- {
- if (Depth != 2) throw new TesseractException("Pix must have a depth of 2 bits.");
- }
-
- // data access routines
- public uint this[int index]
+ public PixData GetData()
{
-#if Net45
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
-#endif
- get
- {
- return (*(data + ((index) >> 4)) >> (2 * (15 - ((index) & 15)))) & 3;
- }
-#if Net45
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
-#endif
- set
- {
- uint* wordPtr = data + ((index) >> 4);
- *wordPtr &= ~(0xc0000000 >> (2 * ((index) & 15)));
- *wordPtr |= (((value) & 3) << (30 - 2 * ((index) & 15)));
- }
+ return new PixData(this);
}
- }
- public unsafe sealed class Pix4Bit : Pix
- {
- public Pix4Bit(IntPtr handle)
- : base(handle)
- {
- if (Depth != 4) throw new TesseractException("Pix must have a depth of 4 bits.");
- }
-
- // data access routines
- public uint this[int index]
- {
-#if Net45
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
-#endif
- get
- {
- return (*(data + ((index) >> 3)) >> (4 * (7 - ((index) & 7)))) & 0xf;
- }
-#if Net45
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
-#endif
- set
- {
- uint* wordPtr = data + ((index) >> 3);
- *wordPtr &= ~(0xf0000000 >> (4 * ((index) & 7)));
- *wordPtr |= (((value) & 15) << (28 - 4 * ((index) & 7)));
- }
- }
- }
-
-
- public unsafe sealed class Pix8Bit : Pix
- {
- public Pix8Bit(IntPtr handle)
- : base(handle)
- {
- if (Depth != 8) throw new TesseractException("Pix must have a depth of 8 bits.");
- }
-
- // data access routines
- public byte this[int index]
- {
-#if Net45
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
-#endif
- get
- {
-#if LittleEndian
- #if X64
- return *((byte*)((ulong)((byte*)data + index) ^ 3));
- #else
- return *((byte*)((uint)((byte*)data + index) ^ 3));
- #endif
-#else
- return *((byte*)data + index);
-#endif
- }
-#if Net45
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
-#endif
- set
- {
-#if LittleEndian
- #if X64
- *(byte*)((ulong)((byte*)data + index) ^ 3) = value;
- #else
- *(byte*)((uint)((byte*)data + index) ^ 3) = value;
- #endif
-#else
- *((byte*)data + index) = value;
-#endif
- }
- }
- }
-
- public unsafe sealed class Pix16Bit : Pix
- {
- public Pix16Bit(IntPtr handle)
- : base(handle)
- {
- if (Depth != 16) throw new TesseractException("Pix must have a depth of 16 bits.");
- }
-
- // data access routines
- public ushort this[int index]
- {
-#if Net45
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
-#endif
- get
- {
-#if LittleEndian
- #if X64
- return *(ushort*)((ulong)((ushort*)data + index) ^ 2);
- #else
- return *(ushort*)((uint)((ushort*)data + index) ^ 2);
- #endif
-#else
- return *((ushort*)data + index);
-#endif
- }
-#if Net45
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
-#endif
- set
- {
-#if LittleEndian
- #if X64
- *(ushort*)((ulong)((ushort*)data + index) ^ 2) = value;
- #else
- *(ushort*)((uint)((ushort*)data + index) ^ 2) = value;
- #endif
-#else
- *((ushort*)data + index) = value;
-#endif
- }
- }
- }
-
- public unsafe sealed class Pix32Bit : Pix
- {
- public Pix32Bit(IntPtr handle)
- : base(handle)
+ public IntPtr Handle
{
- if (Depth != 32) throw new TesseractException("Pix must have a depth of 32 bits.");
+ get { return handle; }
}
- // data access routines
- public uint this[int index]
+ protected override void Dispose(bool disposing)
{
-#if Net45
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
-#endif
- get
- {
- return *(data + index);
- }
-#if Net45
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
-#endif
- set
- {
- *(data + index) = value;
- }
+ Interop.LeptonicaApi.pixDestroy(ref handle);
}
}
}
View
143 Tesseract.Net20/PixColorMap.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tesseract
+{
+ public sealed class PixColormap : DisposableBase
+ {
+ private IntPtr handle;
+
+ internal PixColormap(IntPtr handle)
+ {
+ this.handle = handle;
+ }
+
+ public static PixColormap Create(int depth)
+ {
+ if (depth != 2 || depth != 4 || depth != 8) {
+ throw new ArgumentOutOfRangeException("depth", "Depth must be 2, 4, or 8 bpp.");
+ }
+
+ var handle = Interop.LeptonicaApi.pixcmapCreate(depth);
+ if (handle == IntPtr.Zero) {
+ throw new InvalidOperationException("Failed to create colormap.");
+ }
+ return new PixColormap(handle);
+ }
+
+ public static PixColormap CreateLinear(int depth, int levels)
+ {
+ if (depth != 2 || depth != 4 || depth != 8) {
+ throw new ArgumentOutOfRangeException("depth", "Depth must be 2, 4, or 8 bpp.");
+ }
+ if (levels < 2 || levels > (2 << depth))
+ throw new ArgumentOutOfRangeException("levels", "Depth must be 2 and 2^depth (inclusive).");
+
+ var handle = Interop.LeptonicaApi.pixcmapCreateLinear(depth, levels);
+ if (handle == IntPtr.Zero) {
+ throw new InvalidOperationException("Failed to create colormap.");
+ }
+ return new PixColormap(handle);
+ }
+
+ public static PixColormap CreateLinear(int depth, bool firstIsBlack, bool lastIsWhite)
+ {
+ if (depth != 2 || depth != 4 || depth != 8) {
+ throw new ArgumentOutOfRangeException("depth", "Depth must be 2, 4, or 8 bpp.");
+ }
+
+ var handle = Interop.LeptonicaApi.pixcmapCreateRandom(depth, firstIsBlack ? 1 : 0, lastIsWhite ? 1 : 0);
+ if (handle == IntPtr.Zero) {
+ throw new InvalidOperationException("Failed to create colormap.");
+ }
+ return new PixColormap(handle);
+ }
+
+ public IntPtr Handle
+ {
+ get { return handle; }
+ }
+
+ public int Depth
+ {
+ get { return Interop.LeptonicaApi.pixcmapGetDepth(handle); }
+ }
+
+ public int Count
+ {
+ get { return Interop.LeptonicaApi.pixcmapGetCount(handle); }
+ }
+
+ public int FreeCount
+ {
+ get { return Interop.LeptonicaApi.pixcmapGetFreeCount(handle); }
+ }
+
+ public bool AddColor(Color color)
+ {
+ return Interop.LeptonicaApi.pixcmapAddColor(handle, color.Red, color.Green, color.Blue) == 0;
+ }
+
+ public bool AddNewColor(Color color, out int index)
+ {
+ return Interop.LeptonicaApi.pixcmapAddNewColor(handle, color.Red, color.Green, color.Blue, out index) == 0;
+ }
+
+ public bool AddNearestColor(Color color, out int index)
+ {
+ return Interop.LeptonicaApi.pixcmapAddNearestColor(handle, color.Red, color.Green, color.Blue, out index) == 0;
+ }
+
+ public bool AddBlackOrWhite(int color, out int index)
+ {
+ return Interop.LeptonicaApi.pixcmapAddBlackOrWhite(handle, color, out index) == 0;
+ }
+
+ public bool SetBlackOrWhite(bool setBlack, bool setWhite)
+ {
+ return Interop.LeptonicaApi.pixcmapSetBlackAndWhite(handle, setBlack ? 1 : 0, setWhite ? 1 : 0) == 0;
+ }
+
+ public bool IsUsableColor(Color color)
+ {
+ int usable;
+ if (Interop.LeptonicaApi.pixcmapUsableColor(handle, color.Red, color.Green, color.Blue, out usable) == 0) {
+ return usable == 1;
+ } else {
+ throw new LeptonicaException("Failed to detect if color was usable or not.");
+ }
+ }
+
+ public void Clear()
+ {
+ if (Interop.LeptonicaApi.pixcmapClear(handle) != 0) {
+ throw new LeptonicaException("Failed to clear color map.");
+ }
+ }
+
+ public Color this[int index]
+ {
+ get
+ {
+ int red, green, blue;
+ if (Interop.LeptonicaApi.pixcmapGetColor(handle, index, out red, out green, out blue) == 0) {
+ return new Color((byte)red, (byte)green, (byte)blue);
+ } else {
+ throw new LeptonicaException("Failed to retrieve color.");
+ }
+ }
+ set
+ {
+ if (Interop.LeptonicaApi.pixcmapResetColor(handle, index, value.Red, value.Green, value.Blue) != 0) {
+ throw new LeptonicaException("Failed to reset color.");
+ }
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ Interop.LeptonicaApi.pixcmapDestroy(ref handle);
+ }
+ }
+}
View
16 Tesseract.Net20/PixConverter.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Text;
+
+namespace Tesseract
+{
+ public static class PixConverter
+ {
+ public static Pix ToPix(Bitmap img)
+ {
+ var converter = new BitmapToPixConverter();
+ return converter.Convert(img);
+ }
+ }
+}
View
198 Tesseract.Net20/PixData.cs
@@ -0,0 +1,198 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace Tesseract
+{
+ public unsafe class PixData
+ {
+
+ public delegate uint Getter(uint* data, int index);
+ public delegate uint Setter(uint* data, int index);
+
+ public Pix Pix { get; private set; }
+
+ internal PixData(Pix pix)
+ {
+ Pix = pix;
+ Data = Interop.LeptonicaApi.pixGetData(pix.Handle);
+ WordsPerLine = Interop.LeptonicaApi.pixGetWpl(pix.Handle);
+ }
+
+ /// <summary>
+ /// Pointer to the data.
+ /// </summary>
+ public IntPtr Data { get; private set; }
+
+ /// <summary>
+ /// Number of 32-bit words per line.
+ /// </summary>
+ public int WordsPerLine { get; private set; }
+
+ /// <summary>
+ /// Swaps the bytes on little-endian platforms within a word; bytes 0 and 3 swapped, and bytes `1 and 2 are swapped.
+ /// </summary>
+ /// <remarks>
+ /// This is required for little-endians in situations where we convert from a serialized byte order that is in raster order,
+ /// as one typically has in file formats, to one with MSB-to-the-left in each 32-bit word, or v.v. See <seealso cref="http://www.leptonica.com/byte-addressing.html"/>
+ /// </remarks>
+ public void EndianByteSwap()
+ {
+ Interop.LeptonicaApi.pixEndianByteSwap(Pix.Handle);
+ }
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static uint GetDataBit(uint* data, int index)
+ {
+ return (*(data + ((index) >> 5)) >> (31 - ((index) & 31))) & 1;
+ }
+
+
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static uint EncodeAsRGBA(byte red, byte green, byte blue, byte alpha)
+ {
+ return (uint)((red << 24) |
+ (green << 16) |
+ (blue << 8) |
+ alpha);
+ }
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static void SetDataBit(uint* data, int index, uint value)
+ {
+ uint* wordPtr = data + ((index) >> 5);
+ *wordPtr &= ~(0x80000000 >> ((index) & 31));
+ *wordPtr |= (value << (31 - ((index) & 31)));
+ }
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static uint GetDataDIBit(uint* data, int index)
+ {
+ return (*(data + ((index) >> 4)) >> (2 * (15 - ((index) & 15)))) & 3;
+ }
+
+
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static void SetDataDIBit(uint* data, int index, uint value)
+ {
+ uint* wordPtr = data + ((index) >> 4);
+ *wordPtr &= ~(0xc0000000 >> (2 * ((index) & 15)));
+ *wordPtr |= (((value) & 3) << (30 - 2 * ((index) & 15)));
+ }
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static uint GetDataQBit(uint* data, int index)
+ {
+ return (*(data + ((index) >> 3)) >> (4 * (7 - ((index) & 7)))) & 0xf;
+ }
+
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static void SetDataQBit(uint* data, int index, uint value)
+ {
+ uint* wordPtr = data + ((index) >> 3);
+ *wordPtr &= ~(0xf0000000 >> (4 * ((index) & 7)));
+ *wordPtr |= (((value) & 15) << (28 - 4 * ((index) & 7)));
+ }
+
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static uint GetDataByte(uint* data, int index)
+ {
+#if LittleEndian
+ #if X64
+ return *((byte*)((ulong)((byte*)data + index) ^ 3));
+ #else
+ return *((byte*)((uint)((byte*)data + index) ^ 3));
+ #endif
+#else
+ return *((byte*)data + index);
+#endif
+ }
+
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static void SetDataByte(uint* data, int index, uint value)
+ {
+#if LittleEndian
+ #if X64
+ *(byte*)((ulong)((byte*)data + index) ^ 3) = (byte)value;
+ #else
+ *(byte*)((uint)((byte*)data + index) ^ 3) = (byte)value;
+ #endif
+#else
+ *((byte*)data + index) = (byte)value;
+#endif
+ }
+
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static uint GetDataTwoByte(uint* data, int index)
+ {
+#if LittleEndian
+ #if X64
+ return *(ushort*)((ulong)((ushort*)data + index) ^ 2);
+ #else
+ return *(ushort*)((uint)((ushort*)data + index) ^ 2);
+ #endif
+#else
+ return *((ushort*)data + index);
+#endif
+ }
+
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static void SetDataTwoByte(uint* data, int index, uint value)
+ {
+#if LittleEndian
+ #if X64
+ *(ushort*)((ulong)((ushort*)data + index) ^ 2) = (ushort)value;
+ #else
+ *(ushort*)((uint)((ushort*)data + index) ^ 2) = (ushort)value;
+ #endif
+#else
+ *((ushort*)data + index) = (ushort)value;
+#endif
+ }
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static uint GetDataFourByte(uint* data, int index)
+ {
+ return *(data + index);
+ }
+
+
+#if Net45
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+#endif
+ public static void SetDataFourByte(uint* data, int index, uint value)
+ {
+ *(data + index) = value;
+ }
+ }
+}
View
9 Tesseract.Net20/Tesseract.Net20.csproj
@@ -52,9 +52,14 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
+ <Reference Include="System.Drawing" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="AddNewColorStatus.cs" />
+ <Compile Include="BitmapHelper.cs" />
+ <Compile Include="BitmapToPixConverter.cs" />
+ <Compile Include="Color.cs" />
<Compile Include="DisposableBase.cs" />
<Compile Include="Engine.cs" />
<Compile Include="EngineConfig.cs" />
@@ -65,7 +70,7 @@
<Compile Include="Interop\EmbeddedDllLoader.cs" />
<Compile Include="Interop\LeptonicaApi.cs" />
<Compile Include="Interop\MarshalHelper.cs" />
- <Compile Include="Interop\PixColormap.cs" />
+ <Compile Include="LeptonicaException.cs" />
<Compile Include="MathHelper.cs" />
<Compile Include="Orientation.cs" />
<Compile Include="Page.cs" />
@@ -74,6 +79,8 @@
<Compile Include="ElementProperties.cs" />
<Compile Include="PageSegMode.cs" />
<Compile Include="Pix.cs" />
+ <Compile Include="PixConverter.cs" />
+ <Compile Include="PixData.cs" />
<Compile Include="PolyBlockType.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Rect.cs" />
View
19 Tesseract.Net40/Tesseract.Net40.csproj
@@ -36,6 +36,7 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
+ <Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@@ -51,6 +52,15 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
+ <Compile Include="..\Tesseract.Net20\BitmapHelper.cs">
+ <Link>BitmapHelper.cs</Link>
+ </Compile>
+ <Compile Include="..\Tesseract.Net20\BitmapToPixConverter.cs">
+ <Link>BitmapToPixConverter.cs</Link>
+ </Compile>
+ <Compile Include="..\Tesseract.Net20\Color.cs">
+ <Link>Color.cs</Link>
+ </Compile>
<Compile Include="..\Tesseract.Net20\DisposableBase.cs">
<Link>DisposableBase.cs</Link>
</Compile>
@@ -84,9 +94,6 @@
<Compile Include="..\Tesseract.Net20\Interop\MarshalHelper.cs">
<Link>Interop\MarshalHelper.cs</Link>
</Compile>
- <Compile Include="..\Tesseract.Net20\Interop\PixColormap.cs">
- <Link>Interop\PixColormap.cs</Link>
- </Compile>
<Compile Include="..\Tesseract.Net20\MathHelper.cs">
<Link>MathHelper.cs</Link>
</Compile>
@@ -108,6 +115,12 @@
<Compile Include="..\Tesseract.Net20\Pix.cs">
<Link>Pix.cs</Link>
</Compile>
+ <Compile Include="..\Tesseract.Net20\PixConverter.cs">
+ <Link>PixConverter.cs</Link>
+ </Compile>
+ <Compile Include="..\Tesseract.Net20\PixData.cs">
+ <Link>PixData.cs</Link>
+ </Compile>
<Compile Include="..\Tesseract.Net20\PolyBlockType.cs">
<Link>PolyBlockType.cs</Link>
</Compile>
View
19 Tesseract.Net45/Tesseract.Net45.csproj
@@ -35,6 +35,7 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
+ <Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@@ -50,6 +51,15 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
+ <Compile Include="..\Tesseract.Net20\BitmapHelper.cs">
+ <Link>BitmapHelper.cs</Link>
+ </Compile>
+ <Compile Include="..\Tesseract.Net20\BitmapToPixConverter.cs">
+ <Link>BitmapToPixConverter.cs</Link>
+ </Compile>
+ <Compile Include="..\Tesseract.Net20\Color.cs">
+ <Link>Color.cs</Link>
+ </Compile>
<Compile Include="..\Tesseract.Net20\DisposableBase.cs">
<Link>DisposableBase.cs</Link>
</Compile>
@@ -83,9 +93,6 @@
<Compile Include="..\Tesseract.Net20\Interop\MarshalHelper.cs">
<Link>Interop\MarshalHelper.cs</Link>
</Compile>
- <Compile Include="..\Tesseract.Net20\Interop\PixColormap.cs">
- <Link>Interop\PixColormap.cs</Link>
- </Compile>
<Compile Include="..\Tesseract.Net20\MathHelper.cs">
<Link>MathHelper.cs</Link>
</Compile>
@@ -107,6 +114,12 @@
<Compile Include="..\Tesseract.Net20\Pix.cs">
<Link>Pix.cs</Link>
</Compile>
+ <Compile Include="..\Tesseract.Net20\PixConverter.cs">
+ <Link>PixConverter.cs</Link>
+ </Compile>
+ <Compile Include="..\Tesseract.Net20\PixData.cs">
+ <Link>PixData.cs</Link>
+ </Compile>
<Compile Include="..\Tesseract.Net20\PolyBlockType.cs">
<Link>PolyBlockType.cs</Link>
</Compile>
View
6 Tesseract.Tests.Console/App.config
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+ <startup>
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
+ </startup>
+</configuration>
View
17 Tesseract.Tests.Console/Program.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tesseract.Tests.Console
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ var testFixture = new Tesseract.Tests.Leptonica.ConvertBitmapToPixTests();
+ testFixture.Convert_BitmapToPix("photo_8.png");
+ }
+ }
+}
View
36 Tesseract.Tests.Console/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Tesseract.Tests.Console")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Tesseract.Tests.Console")]
+[assembly: AssemblyCopyright("Copyright © 2013")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("38fc73b0-827c-45ec-b5aa-e8f623c80d40")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
View
68 Tesseract.Tests.Console/Tesseract.Tests.Console.csproj
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{4D635942-9F51-45C6-BAB0-23AD2E42C99F}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Tesseract.Tests.Console</RootNamespace>
+ <AssemblyName>Tesseract.Tests.Console</AssemblyName>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <PlatformTarget>x86</PlatformTarget>
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <PlatformTarget>x86</PlatformTarget>
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Program.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="App.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Tesseract.Net45\Tesseract.Net45.csproj">
+ <Project>{1868cac0-d64c-4092-9108-59d562321056}</Project>
+ <Name>Tesseract.Net45</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Tesseract.Tests\Tesseract.Tests.csproj">
+ <Project>{abf7dbf7-2358-47c5-9e42-6f1c04e658a5}</Project>
+ <Name>Tesseract.Tests</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
View
BIN  Tesseract.Tests/Data/Conversion/photo.bmp
Binary file not shown
View
BIN  Tesseract.Tests/Data/Conversion/photo.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  Tesseract.Tests/Data/Conversion/photo.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  Tesseract.Tests/Data/Conversion/photo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  Tesseract.Tests/Data/Conversion/photo.tif
Binary file not shown
View
BIN  Tesseract.Tests/Data/Conversion/photo_24.bmp
Binary file not shown
View
BIN  Tesseract.Tests/Data/Conversion/photo_24.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  Tesseract.Tests/Data/Conversion/photo_32.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  Tesseract.Tests/Data/Conversion/photo_8.bmp
Binary file not shown
View
BIN  Tesseract.Tests/Data/Conversion/photo_8.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
38 Tesseract.Tests/Leptonica/BitmapHelperTests.cs
@@ -0,0 +1,38 @@
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tesseract.Tests.Leptonica
+{
+ [TestFixture]
+ public class BitmapHelperTests
+ {
+ [Test]
+ public void ConvertRgb555ToPixColor()
+ {
+ ushort originalVal = 0x39EC;
+ var convertedValue = BitmapHelper.ConvertRgb555ToRGBA(originalVal);
+ Assert.That(convertedValue, Is.EqualTo(0x737B63FF));
+ }
+
+ [Test]
+ [TestCase(0xB9EC, 0x737B63FF)]
+ [TestCase(0x39EC, 0x737B6300)]
+ public void ConvertArgb555ToPixColor(int originalVal, int expectedVal)
+ {
+ var convertedValue = BitmapHelper.ConvertArgb1555ToRGBA((ushort)originalVal);
+ Assert.That(convertedValue, Is.EqualTo((uint)expectedVal));
+ }
+
+ [Test]
+ public void ConvertRgb565ToPixColor()
+ {
+ ushort originalVal = 0x73CC;
+ var convertedValue = BitmapHelper.ConvertRgb565ToRGBA(originalVal);
+ Assert.That(convertedValue, Is.EqualTo(0x737963FF));
+ }
+ }
+}
View
71 Tesseract.Tests/Leptonica/ConvertBitmapToPixTests.cs
@@ -0,0 +1,71 @@
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tesseract.Tests.Leptonica
+{
+ public class ConvertBitmapToPixTests
+ {
+ const string DataDirectory = @"Data\Conversion\";
+
+ [Test]
+ [TestCase("photo.jpg")]
+ [TestCase("photo.bmp")]
+ [TestCase("photo_8.bmp", Ignore=true, IgnoreReason="PixelFormat 8bppIndex is not currently supported (no color map support)")]
+ [TestCase("photo_24.bmp")]
+ [TestCase("photo.png")]
+ [TestCase("photo_8.png")]
+ [TestCase("photo_24.png")]
+ [TestCase("photo_32.png")]
+ [TestCase("photo.tif")]
+ [TestCase("photo.gif", Ignore = true, IgnoreReason = "PixelFormat 8bppIndex is not currently supported (no color map support)")]
+ public unsafe void Convert_BitmapToPix(string sourceFile)
+ {
+ var sourceFilePath = Path.Combine(DataDirectory, sourceFile);
+ var bitmapConverter = new BitmapToPixConverter();
+ using (var source = new Bitmap(sourceFilePath)) {
+ //Console.WriteLine("Img format: {0}", img.PixelFormat);
+ using (var dest = bitmapConverter.Convert(source)) {
+ AssertAreEquivalent(source, dest);
+
+ //dest.Save("converted_img.bmp");
+ }
+ }
+ }
+
+
+ private void AssertAreEquivalent(Bitmap bmp, Pix pix)
+ {
+ // verify img metadata
+ Assert.That(pix.Width, Is.EqualTo(bmp.Width));
+ Assert.That(pix.Height, Is.EqualTo(bmp.Height));
+ //Assert.That(pix.Resolution.X, Is.EqualTo(bmp.HorizontalResolution));
+ //Assert.That(pix.Resolution.Y, Is.EqualTo(bmp.VerticalResolution));
+
+ // do some random sampling over image
+ var height = pix.Height;
+ var width = pix.Width;
+ for (int y = 0; y < height; y += height / 4) {
+ for (int x = 0; x < width; x += width / 4) {
+ Color sourcePixel = (Color)bmp.GetPixel(x, y);
+ Color destPixel = GetPixel(pix, x, y);
+ Assert.That(destPixel, Is.EqualTo(sourcePixel), "Expected pixel at <{0},{1}> to be same in both source and dest.");
+ }
+ }
+ }
+
+ private unsafe Color GetPixel(Pix pix, int x, int y)
+ {
+ var pixData = pix.GetData();
+
+ var pixLine = (uint*)pixData.Data + pixData.WordsPerLine * y;
+ return Color.FromRgba(pixLine[x]);
+ }
+ }
+}
View
146 Tesseract.Tests/Leptonica/PixDataAccessTests.cs
@@ -7,116 +7,54 @@
namespace Tesseract.Tests.Leptonica
{
[TestFixture]
- public class DataAccessTests
+ public unsafe class DataAccessTests
{
- const int Width = 3308, Height = 4676;
-
- [Test]
- public void CanReadAndWrite1BitData()
- {
- const int depth = 1;
- const int length = Width * Height;
-
- uint sum = 0;
- using (var pix = (Pix1Bit)Pix.Create(Width, Height, depth)) {
- for (int i = 0; i < length; i++) {
- uint val = (uint)(i % (1 << depth));
- pix[i] = val;
- var readVal = pix[i];
- sum += readVal;
- }
- }
- // Assert.That(readVal, Is.EqualTo(val));
- }
-
- [Test]
- public void CanReadAndWrite2BitData()
- {
- const int depth = 2;
- const int length = Width * Height;
-
- uint sum = 0;
- using (var pix = (Pix2Bit)Pix.Create(Width, Height, depth)) {
- for (int i = 0; i < length; i++) {
- uint val = (uint)(i % (1 << depth));
- pix[i] = val;
- var readVal = pix[i];
- sum += readVal;
- }
- }
- // Assert.That(readVal, Is.EqualTo(val));
- }
+ const int Width = 59, Height = 53;
+
[Test]
- public void CanReadAndWrite4BitData()
+ [TestCase(1)]
+ [TestCase(2)]
+ [TestCase(4)]
+ [TestCase(8)]
+ [TestCase(16)]
+ [TestCase(32)]
+ public void CanReadAndWriteData(int depth)
{
- const int depth = 4;
- const int length = Width * Height;
-
- uint sum = 0;
- using (var pix = (Pix4Bit)Pix.Create(Width, Height, depth)) {
- for (int i = 0; i < length; i++) {
- uint val = (uint)(i % (1 << depth));
- pix[i] = val;
- var readVal = pix[i];
- sum += readVal;
- }
- }
- // Assert.That(readVal, Is.EqualTo(val));
- }
-
- [Test]
- public void CanReadAndWrite8BitData()
- {
- const int depth = 8;
- const int length = Width * Height;
-
- uint sum = 0;
- using (var pix = (Pix8Bit)Pix.Create(Width, Height, depth)) {
- for (int i = 0; i < length; i++) {
- byte val = (byte)(i % (1 << depth));
- pix[i] = val;
- var readVal = pix[i];
- sum += readVal;
- }
- }
- // Assert.That(readVal, Is.EqualTo(val));
- }
-
- [Test]
- public void CanReadAndWrite16BitData()
- {
- const int depth = 16;
- const int length = Width * Height;
-
- uint sum = 0;
- using (var pix = (Pix16Bit)Pix.Create(Width, Height, depth)) {
- for (int i = 0; i < length; i++) {
- ushort val = (ushort)(i % (1 << depth));
- pix[i] = val;
- var readVal = pix[i];
- sum += readVal;
- }
- }
- // Assert.That(readVal, Is.EqualTo(val));
- }
-
- [Test]
- public void CanReadAndWrite32BitData()
- {
- const int depth = 32;
- const int length = Width * Height;
-
- uint sum = 0;
- using (var pix = (Pix32Bit)Pix.Create(Width, Height, depth)) {
- for (int i = 0; i < length; i++) {
- uint val = (uint)(i % (1 << depth));
- pix[i] = val;
- var readVal = pix[i];
- sum += readVal;
+ using (var pix = Pix.Create(Width, Height, depth)) {
+ var pixData = pix.GetData();
+
+ for (int y = 0; y < Height; y++) {
+ uint* line = (uint*)pixData.Data + (y * pixData.WordsPerLine);
+ for (int x = 0; x < Width; x++) {
+ uint val = (uint)((y * Width + x) % (1 << depth));
+ uint readVal;
+ if (depth == 1) {
+ PixData.SetDataBit(line, x, val);
+ readVal = PixData.GetDataBit(line, x);
+ } else if (depth == 2) {
+ PixData.SetDataDIBit(line, x, val);
+ readVal = PixData.GetDataDIBit(line, x);
+ } else if (depth == 4) {
+ PixData.SetDataQBit(line, x, val);
+ readVal = PixData.GetDataQBit(line, x);
+ } else if (depth == 8) {
+ PixData.SetDataByte(line, x, val);
+ readVal = PixData.GetDataByte(line, x);
+ } else if (depth == 16) {
+ PixData.SetDataTwoByte(line, x, val);
+ readVal = PixData.GetDataTwoByte(line, x);
+ } else if (depth == 32) {
+ PixData.SetDataFourByte(line, x, val);
+ readVal = PixData.GetDataFourByte(line, x);
+ } else {
+ throw new NotSupportedException();
+ }
+
+ Assert.That(readVal, Is.EqualTo(val));
+ }
}
}
- // Assert.That(readVal, Is.EqualTo(val));
}
}
}
View
38 Tesseract.Tests/Tesseract.Tests.csproj
@@ -40,6 +40,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<Prefer32Bit>false</Prefer32Bit>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<Prefer32Bit>false</Prefer32Bit>
@@ -52,6 +53,7 @@
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
+ <Reference Include="System.Drawing" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq">
@@ -61,13 +63,47 @@
<ItemGroup>
<Compile Include="BaseApiTests.cs" />
<Compile Include="EngineTests.cs" />
+ <Compile Include="Leptonica\BitmapHelperTests.cs" />
+ <Compile Include="Leptonica\ConvertBitmapToPixTests.cs" />
<Compile Include="Leptonica\LoadFromFileTests.cs" />
<Compile Include="Leptonica\PixDataAccessTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TesseractResultSet.cs" />
</ItemGroup>
<ItemGroup>
- <Content Include="phototest.tif" />
+ <Content Include="Data\Conversion\photo.bmp">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Data\Conversion\photo.gif">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Data\Conversion\photo.jpg">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Data\Conversion\photo.png">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Data\Conversion\photo.tif">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Data\Conversion\photo_24.bmp">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Data\Conversion\photo_8.bmp">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="phototest.tif">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Data\Conversion\photo_24.png">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Data\Conversion\photo_32.png">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="Data\Conversion\photo_8.png">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="ProfilingSessions\Session20121124_192229.sdps" />
<Content Include="ProfilingSessions\Session20121124_192424.sdps" />
<Content Include="ProfilingSessions\Session20121124_192603.sdps" />
View
12 Tesseract.sln
@@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tesseract.Net20", "Tesserac
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tesseract.Net40", "Tesseract.Net40\Tesseract.Net40.csproj", "{E0784105-00D8-4692-BD2E-0645440D4D09}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tesseract.Tests.Console", "Tesseract.Tests.Console\Tesseract.Tests.Console.csproj", "{4D635942-9F51-45C6-BAB0-23AD2E42C99F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -71,6 +73,16 @@ Global
{E0784105-00D8-4692-BD2E-0645440D4D09}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{E0784105-00D8-4692-BD2E-0645440D4D09}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{E0784105-00D8-4692-BD2E-0645440D4D09}.Release|x86.ActiveCfg = Release|Any CPU
+ {4D635942-9F51-45C6-BAB0-23AD2E42C99F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4D635942-9F51-45C6-BAB0-23AD2E42C99F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4D635942-9F51-45C6-BAB0-23AD2E42C99F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {4D635942-9F51-45C6-BAB0-23AD2E42C99F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {4D635942-9F51-45C6-BAB0-23AD2E42C99F}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4D635942-9F51-45C6-BAB0-23AD2E42C99F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4D635942-9F51-45C6-BAB0-23AD2E42C99F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4D635942-9F51-45C6-BAB0-23AD2E42C99F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {4D635942-9F51-45C6-BAB0-23AD2E42C99F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {4D635942-9F51-45C6-BAB0-23AD2E42C99F}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
View
174 hacks/BitmapToPixConverter.cs
@@ -0,0 +1,174 @@
+
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+
+namespace Tesseract
+{
+ /// <summary>
+ /// Description of BitmapToPixConverter.
+ /// </summary>
+ public class BitmapToPixConverter
+ {
+ public BitmapToPixConverter()
+ {
+ }
+
+ public Pix Convert(Bitmap img)
+ {
+ var pixDepth = GetPixDepth(img.PixelFormat);
+ var pix = Pix.Create(img.Width, img.Height, pixDepth);
+
+ BitmapData imgData = null;
+ PixData pixData = null;
+ try {
+
+ // TODO: Setup colormap, if required
+ if ((img.PixelFormat & PixelFormat.Indexed) == PixelFormat.Indexed) {
+ throw new NotImplementedException("Indexed bitmaps are not currently supported.");
+ }
+
+ // transfer data
+ imgData = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadOnly, img.PixelFormat);
+ pixData = pix.GetData();
+
+ TransferData(imgData, pixData);
+
+ return pix;
+ } catch (Exception) {
+ pix.Dispose();
+ throw;
+ } finally {
+ if (imgData != null) {
+ img.UnlockBits(imgData);
+ }
+ }
+ }
+
+ private int GetPixDepth(PixelFormat pixelFormat)
+ {
+ switch (pixelFormat) {
+ case PixelFormat.Format1bppIndexed:
+ return 1;
+ case PixelFormat.Format4bppIndexed:
+ return 4;
+ case PixelFormat.Format8bppIndexed:
+ return 8;
+ case PixelFormat.Format16bppGrayScale:
+ return 16;
+ case PixelFormat.Canonical:
+ case PixelFormat.Format16bppRgb555:
+ case PixelFormat.Format16bppRgb565:
+ case PixelFormat.Format16bppArgb1555:
+ case PixelFormat.Format24bppRgb:
+ case PixelFormat.Format32bppRgb:
+ case PixelFormat.Format32bppArgb:
+ case PixelFormat.Format32bppPArgb:
+ return 32;
+ default:
+ throw new Exception("Invalid value for PixelFormat");
+ }
+ }
+
+ private unsafe void TransferData(BitmapData imgData, PixData pixData)
+ {
+ var imgFormat = imgData.PixelFormat;
+ var imgDepth = Bitmap.GetPixelFormatSize(imgFormat);
+ var height = imgData.Height;
+ var width = imgData.Width;
+
+ if (imgDepth == 16) {
+ for (int y = 0; y < height; y++) {
+ ushort* imgLine = (ushort*)imgData.Scan0 + (y * (imgData.Stride >> 1));
+ uint* pixLine = (uint*)pixData.Data + (y * pixData.WordsPerLine);
+
+ if (imgFormat == PixelFormat.Format16bppRgb555) {
+ for (int x = 0; x < width; x++) {
+ ushort imgPixel = BitmapHelper.GetDataUInt16(imgLine, x);
+ PixData.SetDataFourByte(pixLine, x, BitmapHelper.ConvertRgb555ToRGBA(imgPixel));
+ }
+ } else if (imgFormat == PixelFormat.Format16bppRgb565) {
+ for (int x = 0; x < width; x++) {
+ ushort imgPixel = BitmapHelper.GetDataUInt16(imgLine, x);
+ PixData.SetDataFourByte(pixLine, x, BitmapHelper.ConvertRgb565ToRGBA(imgPixel));
+ }
+ } else if (imgFormat == PixelFormat.Format16bppArgb1555) {
+ for (int x = 0; x < width; x++) {
+ ushort imgPixel = BitmapHelper.GetDataUInt16(imgLine, x);
+ PixData.SetDataFourByte(pixLine, x, BitmapHelper.ConvertArgb1555ToRGBA(imgPixel));
+ }
+ } else if (imgFormat == PixelFormat.Format16bppGrayScale) {
+ for (int x = 0; x < width; x++) {
+ ushort imgPixel = BitmapHelper.GetDataUInt16(imgLine, x);
+ PixData.SetDataTwoByte(pixLine, x, imgPixel);
+ }
+ }
+ }
+ } else if(imgDepth == 1 || imgDepth == 4 || imgDepth == 8) {
+ for (int y = 0; y < height; y++) {
+ byte* imgLine = (byte*)imgData.Scan0 + (y * imgData.Stride);
+ uint* pixLine = (uint*)pixData.Data + (y * pixData.WordsPerLine);
+
+ if (imgFormat == PixelFormat.Format8bppIndexed) {
+ for (int x = 0; x < width; x++) {
+ byte* pixelPtr = imgLine + x;
+ byte red = *pixelPtr;
+ byte green = *(pixelPtr + 1);
+ byte blue = *(pixelPtr + 2);
+ PixData.SetDataFourByte(pixLine, x, BitmapHelper.EncodeAsRGBA(red, green, blue, 255));
+ }
+ }
+ }
+ } else if(imgDepth == 24 || imgDepth == 32) {
+ for (int y = 0; y < height; y++) {
+ byte* imgLine = (byte*)imgData.Scan0 + (y * imgData.Stride);
+ uint* pixLine = (uint*)pixData.Data + (y * pixData.WordsPerLine);
+ if (imgFormat == PixelFormat.Format24bppRgb) {
+ for (int x = 0; x < lineLength; x += 3) {
+ byte* pixelPtr = imgLine + x;
+ byte red = *pixelPtr;
+ byte green = *(pixelPtr + 1);
+ byte blue = *(pixelPtr + 2);
+ PixData.SetDataFourByte(pixLine, x, BitmapHelper.EncodeAsRGBA(red, green, blue, 255));
+ }
+ } else if (imgFormat == PixelFormat.Format32bppArgb) {
+ for (int x = 0; x < lineLength; x += 4) {
+ byte* pixelPtr = imgLine + x;
+ byte alpha = *pixelPtr;
+ byte red = *(pixelPtr + 1);
+ byte green = *(pixelPtr + 2);
+ byte blue = *(pixelPtr + 3);
+ PixData.SetDataFourByte(pixLine, x, BitmapHelper.EncodeAsRGBA(red, green, blue, alpha));
+ }
+ } else if (imgFormat == PixelFormat.Format32bppPArgb) {
+ for (int x = 0; x < lineLength; x += 4) {
+ byte* pixelPtr = imgLine + x;
+ byte alpha = *pixelPtr;
+ byte red = *(pixelPtr + 1);
+ byte green = *(pixelPtr + 2);
+ byte blue = *(pixelPtr + 3);
+ PixData.SetDataFourByte(pixLine, x, BitmapHelper.EncodeAsRGBA(red, green, blue, alpha));
+ }
+ } else if (imgFormat == PixelFormat.Format32bppRgb) {
+ for (int x = 0; x < lineLength; x += 4) {
+ byte* pixelPtr = imgLine + x;
+ byte red = *pixelPtr;
+ byte green = *(pixelPtr + 1);
+