2

I'm currently using Brendan Tompkins ImageQuantization dll. http://codebetter.com/blogs/brendan.tompkins/archive/2007/06/14/gif-image-color-quantizer-now-with-safe-goodness.aspx

But it doesn't run in medium trust in asp.net.

Does anyone know of a Image Quantization library that does run in medium trust?

Update I don't care if the solution is slow. I just need something that works.

Donny V.
  • 22,248
  • 13
  • 65
  • 79

1 Answers1

3

You should be able to replace the code using Marshal with explicit reading of the underlying stream via something like BinaryReader. This may be slower since you must read the stream entirely into your managed memory or seek into it rather than relying on the copy already in unmanaged memory being quickly accessible but is fundamentally your only option.

You simply cannot go spelunking into unmanaged memory from a medium trust context, even if only performing read operations.

Having looked at the linked code there's a reason you're not allowed to do this sort of thing. For starters he's ignoring the 64/32bit aspect of the IntPtr!

The underlying BitMapData class he's using is utterly predicated on having unfettered read access to arbitrary memory, this is never happening under medium trust.
A significant rewrite of his base functionality will be required to either use BitMap's directly (with the slow GetPixel calls) or read the data directly via conventional stream apis, dropping it into an array(s) and then parse it out yourself. Neither of these are likely to be pleasant. The former will be much slower (I would expect order of magnitude due to the high overhead per pixel read), the later less slow (though still slower) but has much more associated effort in terms of rewriting the low level parsing of the image data.

Here's a rough guide to what you need to change based on the current code:

from Quantizer.cs

public Bitmap Quantize(Image source)
{
    // Get the size of the source image
    int height = source.Height;
    int width = source.Width;
    // And construct a rectangle from these dimensions
    Rectangle bounds = new Rectangle(0, 0, width, height);
    // First off take a 32bpp copy of the image
    Bitmap copy = new Bitmap(width, height, PixelFormat.Format32bppArgb);
    // And construct an 8bpp version
    Bitmap output = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
    // Now lock the bitmap into memory
    using (Graphics g = Graphics.FromImage(copy))
    {
        g.PageUnit = GraphicsUnit.Pixel;
        // Draw the source image onto the copy bitmap,
        // which will effect a widening as appropriate.
            g.DrawImage(source, bounds);
    }

    //!! BEGIN CHANGES - no locking here
    //!! simply use copy not a pointer to it
    //!! you could also simply write directly to a buffer then make the final immage in one go but I don't bother here

    // Call the FirstPass function if not a single pass algorithm.
    // For something like an octree quantizer, this will run through
    // all image pixels, build a data structure, and create a palette.
    if (!_singlePass)
        FirstPass(copy, width, height);

    // Then set the color palette on the output bitmap. I'm passing in the current palette 
    // as there's no way to construct a new, empty palette.
    output.Palette = GetPalette(output.Palette);
    // Then call the second pass which actually does the conversion
    SecondPass(copy, output, width, height, bounds);
    //!! END CHANGES
    // Last but not least, return the output bitmap
    return output;
}

//!! Completely changed, note that I assume all the code is changed to just use Color rather than Color32
protected  virtual void FirstPass(Bitmap source, int width, int height)
{
    // Loop through each row
    for (int row = 0; row < height; row++)
    {
        // And loop through each column
        for (int col = 0; col < width; col++)
        {            
            InitialQuantizePixel(source.GetPixel(col, row)); 
        }   // Now I have the pixel, call the FirstPassQuantize function...
    }
}

you would need to do roughly the same in the other functions. This removes any need for Color32, the Bitmap class will deal with all that for you.

Bitmap.SetPixel() will deal with the second pass. Note that this is the easiest way to port things but absolutely not the fastest way to do it within a medium trust environment.

ShuggyCoUk
  • 36,004
  • 6
  • 77
  • 101
  • Any calls to Marshal will fail in medium trust. :-( – Donny V. Jul 08 '09 at 18:14
  • well anything with the link demand requiring unmanaged code, which is all of them from memory. Can you not read the underlying bitmap directly? – ShuggyCoUk Jul 08 '09 at 18:26
  • You can call Bitmap.GetPixel(x,y) legally, this will get you the color, albeit slowly – ShuggyCoUk Jul 08 '09 at 18:27
  • you are likely better reading the whole image into _managed_ memory and then dealing with it there ratehr than going back and forth through the GetPixel stuff one pixel at a time. this will require significantly more memory on the managed heap though sadly (and limits the max possible image size) – ShuggyCoUk Jul 08 '09 at 18:29
  • the image is read into _unmanaged_ memory. you can't get at that at all, you must load it into managed memory (or use slow but safe calsl to get at the data bit by bit) – ShuggyCoUk Jul 08 '09 at 18:41
  • Sorry I know it's not what you want to hear but the fundamental design of this library is based around having more trust than you have. – ShuggyCoUk Jul 08 '09 at 18:42
  • Do you have any examples for this? ThaNKS – Donny V. Jul 08 '09 at 18:44
  • for the per pixel call, yes this is trivial in that you replace all usage of the BitmapData with Bitmap and replace data.Scan0.IntPtr then pointer arithmeticwith bitmap.GetPixel(x,y) iterating over x and y. If you iterate over scan lines still this will be markedly quicker due to caching, i.e. put x in your _inner_ loop. Rewriting it to load the whole thing into managed memory is going to be pretty much a total rewrite of the library – ShuggyCoUk Jul 08 '09 at 18:49
  • I really need this for my site too. I guess I'll have to slowly rewrite it. I have no clue what I'm doing with images, so I'll have to research while I do it. Thanks for all your help. – Donny V. Jul 08 '09 at 18:55
  • Here you go - I added a rough guide as a starter. You will fundamentally need to learn a bit about the Bitmap class if you want to do this. – ShuggyCoUk Jul 08 '09 at 19:04
  • +1, since it appears that no one else will. You certainly sound like you know what you are doing. – Robert Harvey Jul 08 '09 at 19:11
  • +1 Thank you for the start!! I'm going to leave this question unanswered for a couple days just to see anyone else wants to chime in. If now one anwsers I'll give you points. Thanks again! – Donny V. Jul 08 '09 at 19:38
  • You can't call SetPixel with an indexed value. It's not permitted. So while you can build a palette with GetPixel, you can't quantize the image... But if you assign the 8-bit palette to a 32-bit image, GDI will quantize it for you, except for the transparency. May only work on WS2008 and Windows 7, haven't tested it on XP/2003 yet. – Lilith River Jan 01 '12 at 14:58
  • @Com thanks, I hadn't even considered the palletized bitmaps, indeed they make it even more complex thanks for the proviso. If you can meaningfully improve the 'psuedocode' present feel free to edit the answer. – ShuggyCoUk Jan 05 '12 at 18:22
  • I'm having mixed luck with having GDI quantize based off a 32-bit image with a palette. Looks like the only solid solution is to have the entire GIF encoder written in C#. I'm working with the ImageTools and GifComponents projects to achieve this goal. – Lilith River Jan 06 '12 at 17:11