• Jay (unregistered) in reply to Scourge of programmers.
    Scourge of programmers.:
    I hadn't. But despite my complete ignorance of the topic at hand, I felt the need to to insert some sort of smug comment to assuage my crushing lack of self-esteem. I'm an idiot, and I'll probably do it again in future.

    Hey, never let complete ignorance of a subject prevent you from having strong opinions about it.

  • (cs) in reply to nmclean
    nmclean:
    Also, the original code is not doing image conversion. The Bitmap constructor does not create any new data. If you save to the same format as the input, you will get an exact duplicate of the file, right down to the EXIF data. It doesn't re-encode anything unless the format actually changes.
    Interesting. Where is this documented?
  • Anon (unregistered) in reply to realmerlyn
    realmerlyn:
    Scourge of programmers.:
    And what part of PERL was dealing in image processing?

    And there's the real WTF… spelling it as PERL like it's an acronym. Hint: wrong.

    Popular Editable Ropy Language? Proper Enterprisy Real Lemon? Particularly Enduring Random Limit?

  • asifyoucare (unregistered)

    I love how every object is named objXXXXXX, even objImgData which is actually an array or primitives. The 'obj' prefix adds absolutely nothing, when most variables are objects, as they are in object oriented languages.

    This practice of type-tagging variable names in a strongly typed language is a misunderstanding of the purpose of Hungarian notation. It was widely practiced in Microsoft for a while but they've repented.

    Even if mutant-Hungarian notation was a good idea, the 'obj' prefix is the worst example of it. It is so generic that it is useless.

  • nmclean (unregistered) in reply to Frank
    Frank:
    chubertdev:
    Frank:
    Gosh, I've been away too long. When did [image] stop working?

    Uppercase tag/attribute, not closed, no alt attribute. Gross.

    Also, you're assuming that the image itself is in a directory that is served.

    OK, I was trying to be terse in order to focus on the essential point, but for your benefit alone:

    One pixel for spacing because we're obsessive but ignorant control freaks.

    Better?

    So back to my original question. Did that stop working? Or do you overengineering weenies just feel the need to write new object oriented enterprisey code for things that already worked fine long ago?

    Uh, no, that doesn't address what chubertdev said at all: this only works when the image is in a directory that is served, i.e. a directory configured as a static asset location for URLs to map to. The verbosity of your SRC doesn't change the fact that it needs to map to a served directory.

    So you are assuming:

    a) that all possible values of "filename" are mapped to URLs b) that "filename" and its corresponding URL can be determined at the point of page generation for all pages that contain an IMG reference to it

    Or for all we know, this code IS part of the implementation of a static asset manager.

    Now suppose SRC is set by the client, and the filename is determined by some parameters. Your proposed solution is:

    1. Client sends AJAX request "getImage?param1&param2".
    2. Filename is computed.
    3. URL is looked up based on filename.
    4. URL is returned to the client.
    5. Client sets SRC to URL.
    6. Client requests URL.
    7. Filename is looked up based on URL.
    8. File is opened.
    9. File contents are returned to the client.

    As opposed to the current solution:

    1. Client sets SRC to "getImage?param1&param2".
    2. Filename is computed.
    3. File is opened.
    4. File contents are returned to the client.
  • nmclean (unregistered) in reply to pjt33
    pjt33:
    nmclean:
    Also, the original code is not doing image conversion. The Bitmap constructor does not create any new data. If you save to the same format as the input, you will get an exact duplicate of the file, right down to the EXIF data. It doesn't re-encode anything unless the format actually changes.
    Interesting. Where is this documented?
    I've not seen it documented anywhere, but I've verified it. How does saving a jpeg as a jpeg imply any conversion? Really, I don't expect documentation to state what it does NOT do -- rather, I would expect it to state that it DOES change my data if it did.
  • Aris (unregistered)

    And the replacement code doesn't strip the metadata (localisation, serial number of camera, any html code that may be injected) introducing possible confidentiality/security breaches. The first code wasn't so bad if it wasn't so innefficient.

  • verto (unregistered) in reply to chubertdev
    chubertdev:
    it's LOOSE
    ... like your mom?

    Seriously, I have no idea what you're trying to get at there. Restating your point using a synonym doesn't change anything.

  • veniam (unregistered) in reply to nmclean
    nmclean:
    I've not seen it documented anywhere, but I've verified it. How does saving a jpeg as a jpeg imply any conversion? Really, I don't expect documentation to state what it does NOT do -- rather, I would expect it to state that it DOES change my data if it did.
    I'm not familiar with .NET at all, but based on how it's being used in the code, I'd expect Bitmap to load an image file in any supported format and decode it to an in-memory representation, then when it comes to saving it, re-encode it in the specified format. I wouldn't expect it to additionally keep the exact contents of the original file and re-use it when saving in the same format, unless the documentation said so.
  • nibh (unregistered) in reply to Aris
    Aris:
    And the replacement code doesn't strip the metadata (localisation, serial number of camera, any html code that may be injected) introducing possible confidentiality/security breaches.
    a) If the file on disk is a JPEG and nmclean is correct, then neither does the original code (and if it's not a JPEG, then the replacement code is wrong in a much more significant way).

    b) You're making a big assumption that there might be any data in the file in the first place that shouldn't be sent to the client. Unless you have a legitimate reason to believe that's the case you can't criticise the code on that basis (sending the file as-is is what web servers do by default, after all, and you don't see people all up in arms about what a horrible security hole that is).

  • anonymous (unregistered) in reply to veniam
    veniam:
    nmclean:
    I've not seen it documented anywhere, but I've verified it. How does saving a jpeg as a jpeg imply any conversion? Really, I don't expect documentation to state what it does NOT do -- rather, I would expect it to state that it DOES change my data if it did.
    I'm not familiar with .NET at all, but based on how it's being used in the code, I'd expect Bitmap to load an image file in any supported format and decode it to an in-memory representation, then when it comes to saving it, re-encode it in the specified format. I wouldn't expect it to additionally keep the exact contents of the original file and re-use it when saving in the same format, unless the documentation said so.
    Considering that storage space is usually the limiting factor now, it makes more sense that Bitmap would just load the JPEG, which is compressed by a significant amount, and not decode it into a significantly larger raw image unless it's specifically needed to do so (such as when it's actually being displayed, or if you ask it to give you the image in a format other than the one that it's already in).

    Way back in the day, graphics were stored as raw bitmaps in RAM (and sometimes on the disk too) because decoding compressed images was too complex a task for your "render bitmap" function to handle. Now, decoding compressed images is so goddamn trivial that your "render bitmap" function can support pretty much any format you'd like it to, saving you lots of RAM by allowing you to leave the images in their compressed form longer.

  • anonymous (unregistered) in reply to nibh
    nibh:
    If the file on disk is a JPEG and nmclean is correct, then neither does the original code (and if it's not a JPEG, then the replacement code is wrong in a much more significant way).
    Ironically, if it's not a JPEG but a different form of image that the browser supports, the replacement code would probably work just fine since the browser will ignore the content-type and render the image anyway. Better, even, because tagging a lossless PNG with an image/jpeg content-type doesn't actually remove any information, while re-encoding it as a lossy JPEG would.

    You still have to worry about them grabbing files that aren't images, though...

  • (cs) in reply to verto
    verto:
    chubertdev:
    it's LOOSE
    ... like your mom?

    Seriously, I have no idea what you're trying to get at there. Restating your point using a synonym doesn't change anything.

    My point is that just because a few "standards" say something is okay, like uppercase elements and not closing tags, it doesn't mean that it's not an antipattern.

  • (cs)

    Also, to borrow an economics term, it's signaling. The majority of HTML code that's lowercase isn't too bad, while the majority of HTML code that's uppercase would probably end up on this site. It's not a cause, but it's definitely an indicator.

  • anonymous (unregistered) in reply to chubertdev
    chubertdev:
    Also, to borrow an economics term, it's signaling.
    Real economists call it signalling.
  • (cs) in reply to anonymous
    anonymous:
    chubertdev:
    Also, to borrow an economics term, it's signaling.
    Real economists call it signalling.

    No, British economists do. American economists call it signaling.

  • (cs)

    HTML Tip: Use Lowercase Tags

    HTML tags are not case sensitive:

    means the same as

    . Many web sites use uppercase HTML tags.

    W3Schools use lowercase tags because the World Wide Web Consortium (W3C) recommends lowercase in HTML 4, and demands lowercase tags in XHTML.

    http://www.w3schools.com/html/html_elements.asp

  • minim (unregistered) in reply to chubertdev
    chubertdev:
    My point is that just because a few "standards" say something is okay, like uppercase elements and not closing tags, it doesn't mean that it's not an antipattern.
    Preferring lower-case in HTML 4 is entirely subjective. Upper-case isn't merely "okay", it's equally good. You (and the W3C) are entitled to your preference, but it's not in any way wrong or an "antipattern" for other people to prefer upper-case. And not closing the tags isn't just "okay" either, it's the only correct way to do it in HTML 4. Again, you don't have to like it stylistically, but the criticism in that case belongs with the designers of SGML, not with people who write syntactically correct code.
  • persto (unregistered) in reply to chubertdev
    chubertdev:
    Also, to borrow an economics term, it's signaling. The majority of HTML code that's lowercase isn't too bad, while the majority of HTML code that's uppercase would probably end up on this site. It's not a cause, but it's definitely an indicator.
    [citation needed]
  • duis (unregistered) in reply to chubertdev
    chubertdev:
    W3Schools use lowercase tags because the World Wide Web Consortium (W3C) recommends lowercase in HTML 4, and demands lowercase tags in XHTML.
    In fact, the HTML 4 spec mostly (but not entirely) refers to element names in upper-case (but attributes in lower-case, for some reason). Maybe somewhere else on the site someone recommends lower-case, but again, that would just be the preference of whoever wrote that particular article.

    As another data point, the DOM spec considers upper-case tag names to be canonical for HTML documents, and requires that element objects return their tag name in that form. It states that attribute names are "typically" returned in lower-case, but it doesn't seem to actually require that as far as I can tell.

  • (cs) in reply to persto
    persto:
    chubertdev:
    Also, to borrow an economics term, it's signaling. The majority of HTML code that's lowercase isn't too bad, while the majority of HTML code that's uppercase would probably end up on this site. It's not a cause, but it's definitely an indicator.
    [citation needed]

    Well, in the comments here alone, you can see one guy that uses uppercase, and does not use the alt attribute. I, on the otherhand, use lowercase, and I did include the alt attribute.

  • (cs) in reply to minim
    minim:
    chubertdev:
    My point is that just because a few "standards" say something is okay, like uppercase elements and not closing tags, it doesn't mean that it's not an antipattern.
    Preferring lower-case in HTML 4 is entirely subjective. Upper-case isn't merely "okay", it's equally good. You (and the W3C) are entitled to your preference, but it's not in any way wrong or an "antipattern" for other people to prefer upper-case. And not closing the tags isn't just "okay" either, it's the only correct way to do it in HTML 4. Again, you don't have to like it stylistically, but the criticism in that case belongs with the designers of SGML, not with people who write syntactically correct code.

    It's minor compared to the accessibility issues caused by leaving out the alt attribute, and it is very nitpicky, but in my experience, uppercase HTML definitely is most used by the WTFers.

    Do you shout your HTML?

  • (cs) in reply to duis
    duis:
    chubertdev:
    W3Schools use lowercase tags because the World Wide Web Consortium (W3C) recommends lowercase in HTML 4, and demands lowercase tags in XHTML.
    In fact, the HTML 4 spec mostly (but not entirely) refers to element names in upper-case (but attributes in lower-case, for some reason). Maybe somewhere else on the site someone recommends lower-case, but again, that would just be the preference of whoever wrote that particular article.

    As another data point, the DOM spec considers upper-case tag names to be canonical for HTML documents, and requires that element objects return their tag name in that form. It states that attribute names are "typically" returned in lower-case, but it doesn't seem to actually require that as far as I can tell.

    Again, back to the competing spec XKCD. And it definitely is worse that it's a preference, not a requirement.

    Can we just agree that it's preference, and that the real WTF in his comment was leaving out the alt attribute?

  • VEDTREBUHC (unregistered) in reply to chubertdev
    chubertdev:
    It's minor compared to the accessibility issues caused by leaving out the alt attribute, and it is very nitpicky, but in my experience, uppercase HTML definitely is most used by the WTFers.

    Do you shout your HTML?

    Look who's shouting his HTML. I'm whispering my html.

  • anonymous (unregistered) in reply to chubertdev
    chubertdev:
    anonymous:
    chubertdev:
    Also, to borrow an economics term, it's signaling.
    Real economists call it signalling.

    No, British economists do. American economists call it signaling.

    Quit repeating me.

  • nmclean (unregistered) in reply to veniam
    veniam:
    nmclean:
    I've not seen it documented anywhere, but I've verified it. How does saving a jpeg as a jpeg imply any conversion? Really, I don't expect documentation to state what it does NOT do -- rather, I would expect it to state that it DOES change my data if it did.
    I'm not familiar with .NET at all, but based on how it's being used in the code, I'd expect Bitmap to load an image file in any supported format and decode it to an in-memory representation, then when it comes to saving it, re-encode it in the specified format. I wouldn't expect it to additionally keep the exact contents of the original file and re-use it when saving in the same format, unless the documentation said so.
    Well this is TDWTF, so it's generally not a good idea to deduce proper usage based on "how it's being used in the code".

    A class that keeps references to the data passed to its constructor is not odd or unexpected at all. Keeping the exact contents for reuse is not an "additional" action, it's practically a no-op.

    If I was asked to write a "convert(input, outputFormat)" function, the first line I would immediately write would be "if (outputFormat == input.format) return input". I really don't understand why anyone expects the implementation to be inefficient and redundant when the efficient solution is so obvious and simple.

  • praesent (unregistered) in reply to nmclean
    nmclean:
    Well this is TDWTF, so it's generally not a good idea to deduce proper usage based on "how it's being used in the code".
    Heh, I just meant that it has a constructor that takes a (presumed) filename as a string argument, and reads the specified file and decodes the contents, and a Save method, that takes a stream and a format specification, that encodes the image in the specified format and writes the resulting bytes to the stream.

    Having looked at the documentation, however, it seems that not only does it not decode the image data until required, it doesn't even read it (or at least not all of it) from the file - the documentation states that when constructed from a filename it keeps the file locked until the Bitmap is disposed, and when constructed from a stream it requires that the stream remain valid, again until the Bitmap is disposed. I definitely wouldn't have guessed this without seeing it documented, or unless I'd actually used the class and observed such behaviour.

    nmclean:
    A class that keeps references to the data passed to its constructor is not odd or unexpected at all. Keeping the exact contents for reuse is not an "additional" action, it's practically a no-op.
    YMMV, but if I didn't know otherwise I'd expect the constructor and Save method to work like a parser and serialiser for some sort of structured data, and IME those generally don't keep the original bytes/characters around. As an exaggerated example, you wouldn't expect Int32.Parse("1") and Int32.Parse("01") to return different things, or for stringification to magically reinstate the leading zero for the latter - doing the corresponding thing for images is more reasonable, but again, I'd still consider it non-obvious enough to be worth documenting.
    nmclean:
    If I was asked to write a "convert(input, outputFormat)" function, the first line I would immediately write would be "if (outputFormat == input.format) return input".
    That's fair enough, but in this case it isn't a "convert" function - loading the original and saving it in the new format are separate steps, with no apparent reason for the save step to know anything about where the image came from to begin with (or at least the API makes it look that way - the actual implementation seems to be different, as per the above).
    nmclean:
    I really don't understand why anyone expects the implementation to be inefficient and redundant when the efficient solution is so obvious and simple.
    Well, for one thing, it isn't necessarily inefficient or redundant if it doesn't do the same thing as the "efficient" alternative. We've already had one person on this thread who thought the "conversion" would remove potentially sensitive metadata, and more generally, it's not that unreasonable to expect such an operation to perform some kind of normalisation. And on the other hand, someone who does want the optimisation may be led to waste time reimplementing it themself, if they either don't realise that the Bitmap class already does it or don't want to rely on undocumented behaviour.
  • (cs) in reply to VEDTREBUHC
    VEDTREBUHC:
    chubertdev:
    It's minor compared to the accessibility issues caused by leaving out the alt attribute, and it is very nitpicky, but in my experience, uppercase HTML definitely is most used by the WTFers.

    Do you shout your HTML?

    Look who's shouting his HTML. I'm whispering my html.

    After pondering it for a while, I wonder if it's more just a style of older developers that are more used to uppercase in their environment.

    I TURNED ON CAPS LOCK IN THE 80S WHILE DOING COBOL AND NEVER LOOKED BACK
  • nmclean (unregistered) in reply to praesent
    praesent:
    That's fair enough, but in this case it isn't a "convert" function - loading the original and saving it in the new format are separate steps, with no apparent reason for the save step to know anything about where the image came from to begin with
    Of course there is a reason: The data was passed to the instance constructor, and Save is an instance method. The point of a class is to work with persistent state that was determined in an earlier "step". Bitmap determines the input format and persists an ImageFormat value upon construction (the RawFormat property), and the Save method asks you for another ImageFormat value or uses the one stored in the state. Whether the information exists in the form of local function arguments or local object state, the principle is the same.

    In the same sense, your Int32.Parse example is not applicable, because it is not a constructor or a method. Here Parse is a stand-alone function that returns an integer. There is a clear disconnect between the string parameter and the value returned. There is no state.

    praesent:
    Well, for one thing, it isn't necessarily inefficient or redundant if it doesn't do the same thing as the "efficient" alternative.
    Yes it is redundant and inefficient, because the requirement of the method is to "Save this Image to the specified file in the specified format." When the specified format is the same as the Image's format, there is one implementation that is clearly more efficient: using the same data.
    praesent:
    We've already had one person on this thread who thought the "conversion" would remove potentially sensitive metadata, and more generally, it's not that unreasonable to expect such an operation to perform some kind of normalisation. And on the other hand, someone who does want the optimisation may be led to waste time reimplementing it themself, if they either don't realise that the Bitmap class already does it or don't want to rely on undocumented behaviour.
    Of course no one should rely on undocumented assumptions, I'm not saying that at all. On the contrary, other commenters were speaking authoritatively about something undocumented that they clearly had not researched, and I corrected them after running my own test and verifying the output. I do believe it is an unreasonable expectation, which is what prompted me to test it in the first place.

    And by the way, reimplementing the optimization himself wouldn't be a waste of time. That is still relying on undocumented implementation details, and it's not explicit to the code's reader or even necessarily forward-compatible. If you want to ensure that the exact file is copied, you should keep your own handle on the file contents and code that explicitly. And if you want to ensure that the metadata is removed, you should explicitly code that as well.

  • ideo (unregistered) in reply to nmclean

    I'm starting to think you're missing the point on purpose....

    nmclean:
    Of course there is a reason: The data was passed to the instance constructor, and Save is an instance method. The point of a class is to work with persistent state that was determined in an earlier "step".
    Yes, and without explicit documentation, the "persistent state" could just as easily be the decoded image data, with no reference to the original file once the constructor finishes reading it. If you create a new ArrayList by passing an existing collection to the constructor, then the new instance doesn't depend on the existing collection, and it's entirely reasonable to expect Bitmap to work similarly. (I see there's also a static method to create an ArrayList that does behave that way, but it's not the default, and for good reason IMHO.)
    nmclean:
    In the same sense, your Int32.Parse example is not applicable, because it is not a constructor or a method. Here Parse is a stand-alone function that returns an integer. There is a clear disconnect between the string parameter and the value returned. There is no state.
    OK, so I create a function that takes a filename, constructs a Bitmap using the filename as the constructor argument, and returns it. I don't see how being a function^Wstatic method magically changes the expectation - in some languages there isn't even a real distinction between a constructor and a static method that returns a new object in the first place.
    nmclean:
    And by the way, reimplementing the optimization himself wouldn't be a waste of time. That is still relying on undocumented implementation details, and it's not explicit to the code's reader or even necessarily forward-compatible.
    I'm not sure what you mean here....
    nmclean:
    If you want to ensure that the exact file is copied, you should keep your own handle on the file contents and code that explicitly.
    Yes, that's what I meant by reimplementing it. (I'm assuming that the code doesn't know when it reads the image which format it'll be saving it in, so it's not as simple as replacing the use of Bitmap with a plain read/write.) It's unnecessary to do that if Bitmap is guaranteed to do it itself, and if it isn't guaranteed, then that comes back to the original point, which is that without knowing explicitly, it's entirely reasonable to think that Bitmap might not behave that way.
  • (cs)

    Hahahaha, I recognise this exact code from my previous job! I actually wrote the code that replaced it, which ended up being significantly simpler. This particular code was used in an ASP.NET Web Forms page and the new code I wrote to replace it went into an ASP.NET MVC action (basically just returned a FileResult).

    There was no reason to load the image via the Bitmap constructor. Conversion was not necessary as the process that handled uploading the images guaranteed that they were JPEGs. The issue was that the files were located on an internal NAS and required access restrictions, so they had to be proxied for people to actually be able to see them. I would have liked to have a proper solution (like S3 with signed URLs) but didn't get allocated enough time to work on it :(

  • nmclean (unregistered) in reply to ideo
    ideo:
    I'm starting to think you're missing the point on purpose....

    Yes, and without explicit documentation, the "persistent state" could just as easily be the decoded image data, with no reference to the original file once the constructor finishes reading it. If you create a new ArrayList by passing an existing collection to the constructor, then the new instance doesn't depend on the existing collection, and it's entirely reasonable to expect Bitmap to work similarly. (I see there's also a static method to create an ArrayList that does behave that way, but it's not the default, and for good reason IMHO.)

    You are missing the point. My point was this: "Whether the information exists in the form of local function arguments or local object state, the principle is the same." Without explicit documentation, a convert function could just as easily decode image data as well. In both cases, it would be a poor implementation because it would be such a simple matter to design the function / class such that the given data was used when requested in the original format.

    Again, you give a non-applicable example: You are talking about the reference to the collection that holds the data, not the actual data. The ArrayList does reference the same elements. Likewise, I would not expect Bitmap to necessarily keep the reference of the Stream object or filename string that was passed in, but the data they point to.

    ideo:
    OK, so I create a function that takes a filename, constructs a Bitmap using the filename as the constructor argument, and returns it. I don't see how being a function^Wstatic method magically changes the expectation - in some languages there isn't even a real distinction between a constructor and a static method that returns a new object in the first place.
    Of course a constructor and a function is the same internally. The entirety of programming language constructs reduces to gotos, after all. The purpose is to understandably convey ideas to humans, and in the case of an object constructor, the message is "this object consists of this data", while the message of a parse function is "this text represents this value".

    These are very different things semantically. Are you going to tell me next that every "ToString" method has no "real distinction" from a constructor? You'll notice that although many things can be converted to strings, the only constructors of the String class accept characters. Why? Because a string consists of characters.

    Constructors imply construction, not conversion.

    ideo:
    It's unnecessary to do that if Bitmap is guaranteed to do it itself, and if it isn't guaranteed, then that comes back to the original point, which is that without knowing explicitly, it's entirely reasonable to think that Bitmap might not behave that way.
    There is quite a difference between "might not behave that way" and "expected to behave the opposite of that way". What I am disputing is the latter.
  • Jasmine (unregistered)

    That second method doesn't actually work. That's why you see the first method almost everywhere.

  • nmclean (unregistered) in reply to Jasmine
    Jasmine:
    That second method doesn't actually work. That's why you see the first method almost everywhere.
    The first method is undeniably terrible. Even if the second method doesn't work, the first can be reduced to one of the following:

    new Bitmap(fileName).Save(HttpContext.Current.Response.OutputStream, ImageFormat.Jpeg);

    or

    new FileStream(filename, FileMode.Open).CopyTo(HttpContext.Current.Response.OutputStream);

  • anonymous (unregistered) in reply to Jasmine
    Jasmine:
    That second method doesn't actually work. That's why you see the first method almost everywhere.
    Can you clarify what you mean by "doesn't actually work"?

    While it may require some extra steps, once you've verified that the picture exists, that it's really a picture, and that the user has permission to see it, the simplest way to handle it is to send a content-type header and then just copy the image file to the output.

Leave a comment on “Seeing Sextuple”

Log In or post as a guest

Replying to comment #:

« Return to Article