Find last drawn pixel of C# Metafile












0














I have a Metafile object. For reasons outside of my control, it has been provided much larger (thousands of times larger) than what would be required to fit the image drawn inside it.



For example, it could be 40 000 x 40 000, yet only contains "real" (non-transparent) pixels in an area 2000 x 1600.



Originally, this metafile was simply drawn to a control, and the control bounds limited the area to a reasonable size.



Now I am trying to split it into different chunks of dynamic size, depending on user input. What I want to do it count how many of those chunks will be there (in x and in y, even the splitting is into a two-dimensional grid of chunks).



I am aware that, technically, I could go the O(N²) way, and just check the pixels one by one to find the "real" bounds of the drawn image.



But this will be painfully slow.



I am looking for a way of getting the position (x,y) of the very last drawn pixel in the entire metafile, without iterating through every single one of them.



enter image description here



Since The DrawImage method is not painfully slow, at least not N² slow, I assume that the metafile object has some optimisations on the inside that would allow something like this. Just like the List object has a .Count Property that is much faster than actually counting the objects, is there some way of getting the practical bounds of a metafile?



The drawn content, in this scenario, will always be rectangular. I can safely assume that the last pixel will be the same, whether I loop in x then y, or in y then x.



How can I find the coordinates of this "last" pixel?










share|improve this question
























  • So by 'last' you mean the one 'most right-bottom'? Not the one drawn by the last WMF drawing command/shape?
    – TaW
    Nov 19 '18 at 19:07










  • If the shape was unknown, it might be hard to tell which is the last, but since I am garanteed a rectangular shape, by last I indeed mean the bottom right. If doing a loop over the width, and inside a loop over the height (or the reverse), it would be the last pixel checked that is not transparent. So yes, the last coordinates-wise, not drawing-order-wise
    – Kaito Kid
    Nov 19 '18 at 19:14












  • three things come to mind: a) don't look from top-left but from bottom-right. (you probably do that anyway) b) if you can draw the image scaled down by 1/10x or 1/100x you could get way with looking a a lot less pixels. c) if you can estimate the numbers you could draw the image shifted to the top-left and only look at the remainder. All three schemes should probably be combined..
    – TaW
    Nov 19 '18 at 19:52












  • @TaW this could easily be made into an answer
    – Kaito Kid
    Nov 26 '18 at 13:29
















0














I have a Metafile object. For reasons outside of my control, it has been provided much larger (thousands of times larger) than what would be required to fit the image drawn inside it.



For example, it could be 40 000 x 40 000, yet only contains "real" (non-transparent) pixels in an area 2000 x 1600.



Originally, this metafile was simply drawn to a control, and the control bounds limited the area to a reasonable size.



Now I am trying to split it into different chunks of dynamic size, depending on user input. What I want to do it count how many of those chunks will be there (in x and in y, even the splitting is into a two-dimensional grid of chunks).



I am aware that, technically, I could go the O(N²) way, and just check the pixels one by one to find the "real" bounds of the drawn image.



But this will be painfully slow.



I am looking for a way of getting the position (x,y) of the very last drawn pixel in the entire metafile, without iterating through every single one of them.



enter image description here



Since The DrawImage method is not painfully slow, at least not N² slow, I assume that the metafile object has some optimisations on the inside that would allow something like this. Just like the List object has a .Count Property that is much faster than actually counting the objects, is there some way of getting the practical bounds of a metafile?



The drawn content, in this scenario, will always be rectangular. I can safely assume that the last pixel will be the same, whether I loop in x then y, or in y then x.



How can I find the coordinates of this "last" pixel?










share|improve this question
























  • So by 'last' you mean the one 'most right-bottom'? Not the one drawn by the last WMF drawing command/shape?
    – TaW
    Nov 19 '18 at 19:07










  • If the shape was unknown, it might be hard to tell which is the last, but since I am garanteed a rectangular shape, by last I indeed mean the bottom right. If doing a loop over the width, and inside a loop over the height (or the reverse), it would be the last pixel checked that is not transparent. So yes, the last coordinates-wise, not drawing-order-wise
    – Kaito Kid
    Nov 19 '18 at 19:14












  • three things come to mind: a) don't look from top-left but from bottom-right. (you probably do that anyway) b) if you can draw the image scaled down by 1/10x or 1/100x you could get way with looking a a lot less pixels. c) if you can estimate the numbers you could draw the image shifted to the top-left and only look at the remainder. All three schemes should probably be combined..
    – TaW
    Nov 19 '18 at 19:52












  • @TaW this could easily be made into an answer
    – Kaito Kid
    Nov 26 '18 at 13:29














0












0








0







I have a Metafile object. For reasons outside of my control, it has been provided much larger (thousands of times larger) than what would be required to fit the image drawn inside it.



For example, it could be 40 000 x 40 000, yet only contains "real" (non-transparent) pixels in an area 2000 x 1600.



Originally, this metafile was simply drawn to a control, and the control bounds limited the area to a reasonable size.



Now I am trying to split it into different chunks of dynamic size, depending on user input. What I want to do it count how many of those chunks will be there (in x and in y, even the splitting is into a two-dimensional grid of chunks).



I am aware that, technically, I could go the O(N²) way, and just check the pixels one by one to find the "real" bounds of the drawn image.



But this will be painfully slow.



I am looking for a way of getting the position (x,y) of the very last drawn pixel in the entire metafile, without iterating through every single one of them.



enter image description here



Since The DrawImage method is not painfully slow, at least not N² slow, I assume that the metafile object has some optimisations on the inside that would allow something like this. Just like the List object has a .Count Property that is much faster than actually counting the objects, is there some way of getting the practical bounds of a metafile?



The drawn content, in this scenario, will always be rectangular. I can safely assume that the last pixel will be the same, whether I loop in x then y, or in y then x.



How can I find the coordinates of this "last" pixel?










share|improve this question















I have a Metafile object. For reasons outside of my control, it has been provided much larger (thousands of times larger) than what would be required to fit the image drawn inside it.



For example, it could be 40 000 x 40 000, yet only contains "real" (non-transparent) pixels in an area 2000 x 1600.



Originally, this metafile was simply drawn to a control, and the control bounds limited the area to a reasonable size.



Now I am trying to split it into different chunks of dynamic size, depending on user input. What I want to do it count how many of those chunks will be there (in x and in y, even the splitting is into a two-dimensional grid of chunks).



I am aware that, technically, I could go the O(N²) way, and just check the pixels one by one to find the "real" bounds of the drawn image.



But this will be painfully slow.



I am looking for a way of getting the position (x,y) of the very last drawn pixel in the entire metafile, without iterating through every single one of them.



enter image description here



Since The DrawImage method is not painfully slow, at least not N² slow, I assume that the metafile object has some optimisations on the inside that would allow something like this. Just like the List object has a .Count Property that is much faster than actually counting the objects, is there some way of getting the practical bounds of a metafile?



The drawn content, in this scenario, will always be rectangular. I can safely assume that the last pixel will be the same, whether I loop in x then y, or in y then x.



How can I find the coordinates of this "last" pixel?







c# graphics metafile






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 19 '18 at 18:52







Kaito Kid

















asked Nov 19 '18 at 18:27









Kaito KidKaito Kid

1961421




1961421












  • So by 'last' you mean the one 'most right-bottom'? Not the one drawn by the last WMF drawing command/shape?
    – TaW
    Nov 19 '18 at 19:07










  • If the shape was unknown, it might be hard to tell which is the last, but since I am garanteed a rectangular shape, by last I indeed mean the bottom right. If doing a loop over the width, and inside a loop over the height (or the reverse), it would be the last pixel checked that is not transparent. So yes, the last coordinates-wise, not drawing-order-wise
    – Kaito Kid
    Nov 19 '18 at 19:14












  • three things come to mind: a) don't look from top-left but from bottom-right. (you probably do that anyway) b) if you can draw the image scaled down by 1/10x or 1/100x you could get way with looking a a lot less pixels. c) if you can estimate the numbers you could draw the image shifted to the top-left and only look at the remainder. All three schemes should probably be combined..
    – TaW
    Nov 19 '18 at 19:52












  • @TaW this could easily be made into an answer
    – Kaito Kid
    Nov 26 '18 at 13:29


















  • So by 'last' you mean the one 'most right-bottom'? Not the one drawn by the last WMF drawing command/shape?
    – TaW
    Nov 19 '18 at 19:07










  • If the shape was unknown, it might be hard to tell which is the last, but since I am garanteed a rectangular shape, by last I indeed mean the bottom right. If doing a loop over the width, and inside a loop over the height (or the reverse), it would be the last pixel checked that is not transparent. So yes, the last coordinates-wise, not drawing-order-wise
    – Kaito Kid
    Nov 19 '18 at 19:14












  • three things come to mind: a) don't look from top-left but from bottom-right. (you probably do that anyway) b) if you can draw the image scaled down by 1/10x or 1/100x you could get way with looking a a lot less pixels. c) if you can estimate the numbers you could draw the image shifted to the top-left and only look at the remainder. All three schemes should probably be combined..
    – TaW
    Nov 19 '18 at 19:52












  • @TaW this could easily be made into an answer
    – Kaito Kid
    Nov 26 '18 at 13:29
















So by 'last' you mean the one 'most right-bottom'? Not the one drawn by the last WMF drawing command/shape?
– TaW
Nov 19 '18 at 19:07




So by 'last' you mean the one 'most right-bottom'? Not the one drawn by the last WMF drawing command/shape?
– TaW
Nov 19 '18 at 19:07












If the shape was unknown, it might be hard to tell which is the last, but since I am garanteed a rectangular shape, by last I indeed mean the bottom right. If doing a loop over the width, and inside a loop over the height (or the reverse), it would be the last pixel checked that is not transparent. So yes, the last coordinates-wise, not drawing-order-wise
– Kaito Kid
Nov 19 '18 at 19:14






If the shape was unknown, it might be hard to tell which is the last, but since I am garanteed a rectangular shape, by last I indeed mean the bottom right. If doing a loop over the width, and inside a loop over the height (or the reverse), it would be the last pixel checked that is not transparent. So yes, the last coordinates-wise, not drawing-order-wise
– Kaito Kid
Nov 19 '18 at 19:14














three things come to mind: a) don't look from top-left but from bottom-right. (you probably do that anyway) b) if you can draw the image scaled down by 1/10x or 1/100x you could get way with looking a a lot less pixels. c) if you can estimate the numbers you could draw the image shifted to the top-left and only look at the remainder. All three schemes should probably be combined..
– TaW
Nov 19 '18 at 19:52






three things come to mind: a) don't look from top-left but from bottom-right. (you probably do that anyway) b) if you can draw the image scaled down by 1/10x or 1/100x you could get way with looking a a lot less pixels. c) if you can estimate the numbers you could draw the image shifted to the top-left and only look at the remainder. All three schemes should probably be combined..
– TaW
Nov 19 '18 at 19:52














@TaW this could easily be made into an answer
– Kaito Kid
Nov 26 '18 at 13:29




@TaW this could easily be made into an answer
– Kaito Kid
Nov 26 '18 at 13:29












1 Answer
1






active

oldest

votes


















1














Finding the bounding rectangle of the non-transparent pixels for such a large image is indeed an interesting challenge.



The most direct approach would be tackling the WMF content but that is also by far the hardest to get right.



Let's instead render the image to a bitmap and look at the bitmap.



First the basic approach, then a few optimizations.



To get the bounds one need to find the left, top, right and bottom borders.



Here is a simple function to do that:



Rectangle getBounds(Bitmap bmp)
{
int l, r, t, b; l = t = r = b = 0;
for (int x = 0; x < bmp.Width - 1; x++)
for (int y = 0; y < bmp.Height - 1; y++)
if (bmp.GetPixel(x,y).A > 0) { l = x; goto l1; }
l1:
for (int x = bmp.Width - 1; x > l ; x--)
for (int y = 0; y < bmp.Height - 1; y++)
if (bmp.GetPixel(x,y).A > 0) { r = x; goto l2; }
l2:
for (int y = 0; y < bmp.Height - 1; y++)
for (int x = l; x < r; x++)
if (bmp.GetPixel(x,y).A > 0) { t = y; goto l3; }
l3:
for (int y = bmp.Height - 1; y > t; y--)
for (int x = l; x < r; x++)
if (bmp.GetPixel(x,y).A > 0) { b = y; goto l4; }
l4:

return Rectangle.FromLTRB(l,t,r,b);
}


Note that is optimizes the last, vertical loops a little to look only at the portion not already tested by the horizontal loops.



It uses GetPixel, which is painfully slow; but even Lockbits only gains 'only' about 10x or so. So we need to reduce the sheer numbers; we need to do that anyway, because 40k x 40k pixels is too large for a Bitmap.



Since WMF is usually filled with vector data we probably can scale it down a lot. Here is an example:



string fn = "D:\_test18b.emf";
Image img = Image.FromFile(fn);

int w = img.Width;
int h = img.Height;
float scale = 100;
Rectangle rScaled = Rectangle.Empty;

using (Bitmap bmp = new Bitmap((int)(w / scale), (int)(h / scale)))
using (Graphics g = Graphics.FromImage(bmp))
{
g.ScaleTransform(1f/scale, 1f/scale);
g.Clear(Color.Transparent);
g.DrawImage(img, 0, 0);
rScaled = getBounds(bmp);
Rectangle rUnscaled = Rectangle.Round(
new RectangleF(rScaled.Left * scale, rScaled.Top * scale,
rScaled.Width * scale, rScaled.Height * scale ));

}


Note that to properly draw the wmf file one may need to adapt the resolutions. Here is an example i used for testing:



    using (Graphics g2 = pictureBox.CreateGraphics())
{
float scaleX = g2.DpiX / img.HorizontalResolution / scale;
float scaleY = g2.DpiY / img.VerticalResolution / scale;

g2.ScaleTransform(scaleX, scaleY);
g2.DrawImage(img, 0, 0); // draw the original emf image.. (*)
g2.ResetTransform();
// g2.DrawImage(bmp, 0, 0); // .. it will look the same as (*)
g2.DrawRectangle(Pens.Black, rScaled);
}


I left this out but for fully controlling the rendering, it ought have been included in the snippet above as well..





This may or may not be good enough, depending on the accuracy needed.



To measure the bounds perfectly one can do this trick: Use the bounds from the scaled down test and measure unscaled but only a tiny stripe around the four bound numbers. When creating the render bitmap we move the origin accordingly.



Example for the right bound:



Rectangle rScaled2 = Rectangle.Empty;
int delta = 80;
int right = (int)(rScaled.Right * scale);

using (Bitmap bmp = new Bitmap((int)(delta * 2 ), (int)(h )))
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.Transparent);
g.DrawImage(img, - right - delta, 0);
rScaled2 = getBounds(bmp);
}


I could have optimized by not going over the full height but only the portion (plus delte) we already found..



Further optimization can be achieved if one can use knowledge about the data. If we know that the image data are connected we could use larger steps in the loops until a pixel is found and then trace back one step..






share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53380617%2ffind-last-drawn-pixel-of-c-sharp-metafile%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1














    Finding the bounding rectangle of the non-transparent pixels for such a large image is indeed an interesting challenge.



    The most direct approach would be tackling the WMF content but that is also by far the hardest to get right.



    Let's instead render the image to a bitmap and look at the bitmap.



    First the basic approach, then a few optimizations.



    To get the bounds one need to find the left, top, right and bottom borders.



    Here is a simple function to do that:



    Rectangle getBounds(Bitmap bmp)
    {
    int l, r, t, b; l = t = r = b = 0;
    for (int x = 0; x < bmp.Width - 1; x++)
    for (int y = 0; y < bmp.Height - 1; y++)
    if (bmp.GetPixel(x,y).A > 0) { l = x; goto l1; }
    l1:
    for (int x = bmp.Width - 1; x > l ; x--)
    for (int y = 0; y < bmp.Height - 1; y++)
    if (bmp.GetPixel(x,y).A > 0) { r = x; goto l2; }
    l2:
    for (int y = 0; y < bmp.Height - 1; y++)
    for (int x = l; x < r; x++)
    if (bmp.GetPixel(x,y).A > 0) { t = y; goto l3; }
    l3:
    for (int y = bmp.Height - 1; y > t; y--)
    for (int x = l; x < r; x++)
    if (bmp.GetPixel(x,y).A > 0) { b = y; goto l4; }
    l4:

    return Rectangle.FromLTRB(l,t,r,b);
    }


    Note that is optimizes the last, vertical loops a little to look only at the portion not already tested by the horizontal loops.



    It uses GetPixel, which is painfully slow; but even Lockbits only gains 'only' about 10x or so. So we need to reduce the sheer numbers; we need to do that anyway, because 40k x 40k pixels is too large for a Bitmap.



    Since WMF is usually filled with vector data we probably can scale it down a lot. Here is an example:



    string fn = "D:\_test18b.emf";
    Image img = Image.FromFile(fn);

    int w = img.Width;
    int h = img.Height;
    float scale = 100;
    Rectangle rScaled = Rectangle.Empty;

    using (Bitmap bmp = new Bitmap((int)(w / scale), (int)(h / scale)))
    using (Graphics g = Graphics.FromImage(bmp))
    {
    g.ScaleTransform(1f/scale, 1f/scale);
    g.Clear(Color.Transparent);
    g.DrawImage(img, 0, 0);
    rScaled = getBounds(bmp);
    Rectangle rUnscaled = Rectangle.Round(
    new RectangleF(rScaled.Left * scale, rScaled.Top * scale,
    rScaled.Width * scale, rScaled.Height * scale ));

    }


    Note that to properly draw the wmf file one may need to adapt the resolutions. Here is an example i used for testing:



        using (Graphics g2 = pictureBox.CreateGraphics())
    {
    float scaleX = g2.DpiX / img.HorizontalResolution / scale;
    float scaleY = g2.DpiY / img.VerticalResolution / scale;

    g2.ScaleTransform(scaleX, scaleY);
    g2.DrawImage(img, 0, 0); // draw the original emf image.. (*)
    g2.ResetTransform();
    // g2.DrawImage(bmp, 0, 0); // .. it will look the same as (*)
    g2.DrawRectangle(Pens.Black, rScaled);
    }


    I left this out but for fully controlling the rendering, it ought have been included in the snippet above as well..





    This may or may not be good enough, depending on the accuracy needed.



    To measure the bounds perfectly one can do this trick: Use the bounds from the scaled down test and measure unscaled but only a tiny stripe around the four bound numbers. When creating the render bitmap we move the origin accordingly.



    Example for the right bound:



    Rectangle rScaled2 = Rectangle.Empty;
    int delta = 80;
    int right = (int)(rScaled.Right * scale);

    using (Bitmap bmp = new Bitmap((int)(delta * 2 ), (int)(h )))
    using (Graphics g = Graphics.FromImage(bmp))
    {
    g.Clear(Color.Transparent);
    g.DrawImage(img, - right - delta, 0);
    rScaled2 = getBounds(bmp);
    }


    I could have optimized by not going over the full height but only the portion (plus delte) we already found..



    Further optimization can be achieved if one can use knowledge about the data. If we know that the image data are connected we could use larger steps in the loops until a pixel is found and then trace back one step..






    share|improve this answer




























      1














      Finding the bounding rectangle of the non-transparent pixels for such a large image is indeed an interesting challenge.



      The most direct approach would be tackling the WMF content but that is also by far the hardest to get right.



      Let's instead render the image to a bitmap and look at the bitmap.



      First the basic approach, then a few optimizations.



      To get the bounds one need to find the left, top, right and bottom borders.



      Here is a simple function to do that:



      Rectangle getBounds(Bitmap bmp)
      {
      int l, r, t, b; l = t = r = b = 0;
      for (int x = 0; x < bmp.Width - 1; x++)
      for (int y = 0; y < bmp.Height - 1; y++)
      if (bmp.GetPixel(x,y).A > 0) { l = x; goto l1; }
      l1:
      for (int x = bmp.Width - 1; x > l ; x--)
      for (int y = 0; y < bmp.Height - 1; y++)
      if (bmp.GetPixel(x,y).A > 0) { r = x; goto l2; }
      l2:
      for (int y = 0; y < bmp.Height - 1; y++)
      for (int x = l; x < r; x++)
      if (bmp.GetPixel(x,y).A > 0) { t = y; goto l3; }
      l3:
      for (int y = bmp.Height - 1; y > t; y--)
      for (int x = l; x < r; x++)
      if (bmp.GetPixel(x,y).A > 0) { b = y; goto l4; }
      l4:

      return Rectangle.FromLTRB(l,t,r,b);
      }


      Note that is optimizes the last, vertical loops a little to look only at the portion not already tested by the horizontal loops.



      It uses GetPixel, which is painfully slow; but even Lockbits only gains 'only' about 10x or so. So we need to reduce the sheer numbers; we need to do that anyway, because 40k x 40k pixels is too large for a Bitmap.



      Since WMF is usually filled with vector data we probably can scale it down a lot. Here is an example:



      string fn = "D:\_test18b.emf";
      Image img = Image.FromFile(fn);

      int w = img.Width;
      int h = img.Height;
      float scale = 100;
      Rectangle rScaled = Rectangle.Empty;

      using (Bitmap bmp = new Bitmap((int)(w / scale), (int)(h / scale)))
      using (Graphics g = Graphics.FromImage(bmp))
      {
      g.ScaleTransform(1f/scale, 1f/scale);
      g.Clear(Color.Transparent);
      g.DrawImage(img, 0, 0);
      rScaled = getBounds(bmp);
      Rectangle rUnscaled = Rectangle.Round(
      new RectangleF(rScaled.Left * scale, rScaled.Top * scale,
      rScaled.Width * scale, rScaled.Height * scale ));

      }


      Note that to properly draw the wmf file one may need to adapt the resolutions. Here is an example i used for testing:



          using (Graphics g2 = pictureBox.CreateGraphics())
      {
      float scaleX = g2.DpiX / img.HorizontalResolution / scale;
      float scaleY = g2.DpiY / img.VerticalResolution / scale;

      g2.ScaleTransform(scaleX, scaleY);
      g2.DrawImage(img, 0, 0); // draw the original emf image.. (*)
      g2.ResetTransform();
      // g2.DrawImage(bmp, 0, 0); // .. it will look the same as (*)
      g2.DrawRectangle(Pens.Black, rScaled);
      }


      I left this out but for fully controlling the rendering, it ought have been included in the snippet above as well..





      This may or may not be good enough, depending on the accuracy needed.



      To measure the bounds perfectly one can do this trick: Use the bounds from the scaled down test and measure unscaled but only a tiny stripe around the four bound numbers. When creating the render bitmap we move the origin accordingly.



      Example for the right bound:



      Rectangle rScaled2 = Rectangle.Empty;
      int delta = 80;
      int right = (int)(rScaled.Right * scale);

      using (Bitmap bmp = new Bitmap((int)(delta * 2 ), (int)(h )))
      using (Graphics g = Graphics.FromImage(bmp))
      {
      g.Clear(Color.Transparent);
      g.DrawImage(img, - right - delta, 0);
      rScaled2 = getBounds(bmp);
      }


      I could have optimized by not going over the full height but only the portion (plus delte) we already found..



      Further optimization can be achieved if one can use knowledge about the data. If we know that the image data are connected we could use larger steps in the loops until a pixel is found and then trace back one step..






      share|improve this answer


























        1












        1








        1






        Finding the bounding rectangle of the non-transparent pixels for such a large image is indeed an interesting challenge.



        The most direct approach would be tackling the WMF content but that is also by far the hardest to get right.



        Let's instead render the image to a bitmap and look at the bitmap.



        First the basic approach, then a few optimizations.



        To get the bounds one need to find the left, top, right and bottom borders.



        Here is a simple function to do that:



        Rectangle getBounds(Bitmap bmp)
        {
        int l, r, t, b; l = t = r = b = 0;
        for (int x = 0; x < bmp.Width - 1; x++)
        for (int y = 0; y < bmp.Height - 1; y++)
        if (bmp.GetPixel(x,y).A > 0) { l = x; goto l1; }
        l1:
        for (int x = bmp.Width - 1; x > l ; x--)
        for (int y = 0; y < bmp.Height - 1; y++)
        if (bmp.GetPixel(x,y).A > 0) { r = x; goto l2; }
        l2:
        for (int y = 0; y < bmp.Height - 1; y++)
        for (int x = l; x < r; x++)
        if (bmp.GetPixel(x,y).A > 0) { t = y; goto l3; }
        l3:
        for (int y = bmp.Height - 1; y > t; y--)
        for (int x = l; x < r; x++)
        if (bmp.GetPixel(x,y).A > 0) { b = y; goto l4; }
        l4:

        return Rectangle.FromLTRB(l,t,r,b);
        }


        Note that is optimizes the last, vertical loops a little to look only at the portion not already tested by the horizontal loops.



        It uses GetPixel, which is painfully slow; but even Lockbits only gains 'only' about 10x or so. So we need to reduce the sheer numbers; we need to do that anyway, because 40k x 40k pixels is too large for a Bitmap.



        Since WMF is usually filled with vector data we probably can scale it down a lot. Here is an example:



        string fn = "D:\_test18b.emf";
        Image img = Image.FromFile(fn);

        int w = img.Width;
        int h = img.Height;
        float scale = 100;
        Rectangle rScaled = Rectangle.Empty;

        using (Bitmap bmp = new Bitmap((int)(w / scale), (int)(h / scale)))
        using (Graphics g = Graphics.FromImage(bmp))
        {
        g.ScaleTransform(1f/scale, 1f/scale);
        g.Clear(Color.Transparent);
        g.DrawImage(img, 0, 0);
        rScaled = getBounds(bmp);
        Rectangle rUnscaled = Rectangle.Round(
        new RectangleF(rScaled.Left * scale, rScaled.Top * scale,
        rScaled.Width * scale, rScaled.Height * scale ));

        }


        Note that to properly draw the wmf file one may need to adapt the resolutions. Here is an example i used for testing:



            using (Graphics g2 = pictureBox.CreateGraphics())
        {
        float scaleX = g2.DpiX / img.HorizontalResolution / scale;
        float scaleY = g2.DpiY / img.VerticalResolution / scale;

        g2.ScaleTransform(scaleX, scaleY);
        g2.DrawImage(img, 0, 0); // draw the original emf image.. (*)
        g2.ResetTransform();
        // g2.DrawImage(bmp, 0, 0); // .. it will look the same as (*)
        g2.DrawRectangle(Pens.Black, rScaled);
        }


        I left this out but for fully controlling the rendering, it ought have been included in the snippet above as well..





        This may or may not be good enough, depending on the accuracy needed.



        To measure the bounds perfectly one can do this trick: Use the bounds from the scaled down test and measure unscaled but only a tiny stripe around the four bound numbers. When creating the render bitmap we move the origin accordingly.



        Example for the right bound:



        Rectangle rScaled2 = Rectangle.Empty;
        int delta = 80;
        int right = (int)(rScaled.Right * scale);

        using (Bitmap bmp = new Bitmap((int)(delta * 2 ), (int)(h )))
        using (Graphics g = Graphics.FromImage(bmp))
        {
        g.Clear(Color.Transparent);
        g.DrawImage(img, - right - delta, 0);
        rScaled2 = getBounds(bmp);
        }


        I could have optimized by not going over the full height but only the portion (plus delte) we already found..



        Further optimization can be achieved if one can use knowledge about the data. If we know that the image data are connected we could use larger steps in the loops until a pixel is found and then trace back one step..






        share|improve this answer














        Finding the bounding rectangle of the non-transparent pixels for such a large image is indeed an interesting challenge.



        The most direct approach would be tackling the WMF content but that is also by far the hardest to get right.



        Let's instead render the image to a bitmap and look at the bitmap.



        First the basic approach, then a few optimizations.



        To get the bounds one need to find the left, top, right and bottom borders.



        Here is a simple function to do that:



        Rectangle getBounds(Bitmap bmp)
        {
        int l, r, t, b; l = t = r = b = 0;
        for (int x = 0; x < bmp.Width - 1; x++)
        for (int y = 0; y < bmp.Height - 1; y++)
        if (bmp.GetPixel(x,y).A > 0) { l = x; goto l1; }
        l1:
        for (int x = bmp.Width - 1; x > l ; x--)
        for (int y = 0; y < bmp.Height - 1; y++)
        if (bmp.GetPixel(x,y).A > 0) { r = x; goto l2; }
        l2:
        for (int y = 0; y < bmp.Height - 1; y++)
        for (int x = l; x < r; x++)
        if (bmp.GetPixel(x,y).A > 0) { t = y; goto l3; }
        l3:
        for (int y = bmp.Height - 1; y > t; y--)
        for (int x = l; x < r; x++)
        if (bmp.GetPixel(x,y).A > 0) { b = y; goto l4; }
        l4:

        return Rectangle.FromLTRB(l,t,r,b);
        }


        Note that is optimizes the last, vertical loops a little to look only at the portion not already tested by the horizontal loops.



        It uses GetPixel, which is painfully slow; but even Lockbits only gains 'only' about 10x or so. So we need to reduce the sheer numbers; we need to do that anyway, because 40k x 40k pixels is too large for a Bitmap.



        Since WMF is usually filled with vector data we probably can scale it down a lot. Here is an example:



        string fn = "D:\_test18b.emf";
        Image img = Image.FromFile(fn);

        int w = img.Width;
        int h = img.Height;
        float scale = 100;
        Rectangle rScaled = Rectangle.Empty;

        using (Bitmap bmp = new Bitmap((int)(w / scale), (int)(h / scale)))
        using (Graphics g = Graphics.FromImage(bmp))
        {
        g.ScaleTransform(1f/scale, 1f/scale);
        g.Clear(Color.Transparent);
        g.DrawImage(img, 0, 0);
        rScaled = getBounds(bmp);
        Rectangle rUnscaled = Rectangle.Round(
        new RectangleF(rScaled.Left * scale, rScaled.Top * scale,
        rScaled.Width * scale, rScaled.Height * scale ));

        }


        Note that to properly draw the wmf file one may need to adapt the resolutions. Here is an example i used for testing:



            using (Graphics g2 = pictureBox.CreateGraphics())
        {
        float scaleX = g2.DpiX / img.HorizontalResolution / scale;
        float scaleY = g2.DpiY / img.VerticalResolution / scale;

        g2.ScaleTransform(scaleX, scaleY);
        g2.DrawImage(img, 0, 0); // draw the original emf image.. (*)
        g2.ResetTransform();
        // g2.DrawImage(bmp, 0, 0); // .. it will look the same as (*)
        g2.DrawRectangle(Pens.Black, rScaled);
        }


        I left this out but for fully controlling the rendering, it ought have been included in the snippet above as well..





        This may or may not be good enough, depending on the accuracy needed.



        To measure the bounds perfectly one can do this trick: Use the bounds from the scaled down test and measure unscaled but only a tiny stripe around the four bound numbers. When creating the render bitmap we move the origin accordingly.



        Example for the right bound:



        Rectangle rScaled2 = Rectangle.Empty;
        int delta = 80;
        int right = (int)(rScaled.Right * scale);

        using (Bitmap bmp = new Bitmap((int)(delta * 2 ), (int)(h )))
        using (Graphics g = Graphics.FromImage(bmp))
        {
        g.Clear(Color.Transparent);
        g.DrawImage(img, - right - delta, 0);
        rScaled2 = getBounds(bmp);
        }


        I could have optimized by not going over the full height but only the portion (plus delte) we already found..



        Further optimization can be achieved if one can use knowledge about the data. If we know that the image data are connected we could use larger steps in the loops until a pixel is found and then trace back one step..







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Dec 3 '18 at 15:20

























        answered Nov 29 '18 at 11:23









        TaWTaW

        40.9k62762




        40.9k62762






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53380617%2ffind-last-drawn-pixel-of-c-sharp-metafile%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            MongoDB - Not Authorized To Execute Command

            How to fix TextFormField cause rebuild widget in Flutter

            in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith