> Audio samples are point samples (usually). This is nice, because there's a whole theory on how to upsample point samples without loss of information.
> It was already wrong in 1995 when monitors where CRTs, and it's way wrong in 2025 in the LCD/OLED era where pixels are truly discrete.
I don't think it has anything to do with display technologies though. Imagine this: there is a computer that is dedicated to image processing. It has no display, no CRT, no LCD, nothing. The computer is running a service that is resizing images from 100x100 pixels to 200x200 pixels. Would the programmer of this server be better off thinking in terms of samples or rectangular subdivisions of a display?
Alvy Ray Smith, the author of this paper, was coming from the background of developing Renderman for Pixar. In that case, there were render farms doing all sorts of graphics processing before the final image was displayed anywhere.
> I don't think it has anything to do with display technologies though.
I think your two examples nicely illustrate that it's all about the display technology.
> The computer is running a service that is resizing images from 100x100 pixels to 200x200 pixels. Would the programmer of this server be better off thinking in terms of samples or rectangular subdivisions of a display?
That entirely depends on how the resizing is done. Usually people choose nearest neighbor in scenarios like that to be faithful to the original 100x100 display, and to keep the images sharp. This treats the pixels as squares, which means the programmer should do so as well.
> Alvy Ray Smith, the author of this paper, was coming from the background of developing Renderman for Pixar.
That's meaningful context. I'm sure that in 1995, Pixar movies were exposed onto analog film before being shown in theatres. I'm almost certain this process didn't preserve sharp pixels, so "pixels aren't squares" was perhaps literally true for this technology.
> Usually people choose nearest neighbor in scenarios like that to be faithful to the original
Perhaps I should have chosen a higher resolution. AIUI, in many modern systems, such as your OS, it’s usually bilinear or Lanczos resampling.
You say that the resize should be faithful to the “100x100 display”, but we don’t know whether it was used from such a display, or coming from a camera, or generated by software.
> I'm almost certain this process didn't preserve sharp pixels
Sure, but modern image processing pipelines work the same way. They are working to capture the original signal, with a hopeful representation of the continuous signal, not just a grid of squares.
I suppose this is different for a “pixel art” situation, where resampling has to be explicitly set to nearest neighbor. Even so, images like that have problems in modern video codecs, which model samples of a continuous signal.
And yes, I am aware that the “pixel” in “pixel art” means a little square :). The terminology being overloaded is what makes these discussions so confusing.
> I don't think it has anything to do with display technologies though. Imagine this: there is a computer that is dedicated to image processing. It has no display, no CRT, no LCD, nothing. The computer is running a service that is resizing images from 100x100 pixels to 200x200 pixels. Would the programmer of this server be better off thinking in terms of samples or rectangular subdivisions of a display?
How about a counter example: As part of a vectorization engine you need to trace the outline of all pixels of the same color in a bitmap. What other choice to you have than to think of pixels as squares with four sides?
> As part of a vectorization engine you need to trace the outline of all pixels of the same color in a bitmap. What other choice to you have than to think of pixels as squares with four sides?
I think that’s a bad example. For vector tracing, you want the ability to trace using lines and curves at any angle, not alongside the pixel boundaries, so you want to see the image as a function from ℝ² to RGB space for which you have samples at grid positions. Target then is to find a shape that covers the part of ℝ² that satisfies a discriminator function (e.g. “red component at least 0.8, green and blue components at most 0.1) decently well, balancing the simplicity of the shape (in terms of number of control points or something like that) with the quality of the cover.
If your model of the pixels is that they're point samples, they have no edges and there's no way to know what they do or don't border. They're near other pixels, but there could be anything in between.
> This is an issue that strikes right at the root of correct image (sprite) computing and the ability to correctly integrate (converge) the discrete and the continuous. The little square model is simply incorrect. It harms. It gets in the way. If you find yourself thinking that a pixel is a little square, please read this paper.
> A pixel is a point sample. It exists only at a point. For a color picture, a pixel might actually contain three samples, one for each primary color contributing to the picture at the sampling point. We can still think of this as a point sample of a color. But we cannot think of a pixel as a square—or anything other than a point.
A pixel is simply not a point sample. A camera does not take point sample snapshots, it integrates lightfall over little rectangular areas. A modern display does not reconstruct an image the way a DAC reconstructs sounds, they render little rectangles of light, generally with visible XY edges.
The paper's claim applies at least somewhat sensibly to CRTs, but one mustn't imagine the voltage interpolation and shadow masking a CRT does corresponds meaningfully to how modern displays work... and even for CRTs it was never actually correct to claim that pixels were point samples.
It is pretty reasonable in the modern day to say that an idealized pixel is a little square. A lot of graphics operates under this simplifying assumption, and it works better than most things in practice.
> A camera does not take point sample snapshots, it integrates lightfall over little rectangular areas.
Integrates this information into what? :)
> A modern display does not reconstruct an image the way a DAC reconstructs sounds
Sure, but some software may apply resampling over the original signal for the purposes of upscaling, for example. "Pixels as samples" makes more sense in that context.
> It is pretty reasonable in the modern day to say that an idealized pixel is a little square.
I do agree with this actually. A "pixel" in popular terminology is a rectangular subdivision of an image, leading us right back to TFA. The term "pixel art" makes sense with this definition.
Perhaps we need better names for these things. Is the "pixel" the name for the sample, or is it the name of the square-ish thing that you reconstruct from image data when you're ready to send to a display?
Into electric charge? I don’t understand the question, and it sounds like the question is supposed to lead readers somewhere.
The camera integrates incoming light into a tiny square into an electric charge and then reads out the charge (at least for a CCD), giving a brightness (and with the Bayer filter in front of the sensor, a color) for the pixel. So it’s a measurement over the tiny square, not a point sample.
> The camera integrates incoming light into a tiny square [...] giving a brightness (and with the Bayer filter in front of the sensor, a color) for the pixel
This is where I was trying to go. The pixel, the result at the end of all that, is the single value (which may be a color with multiple components, sure). The physical reality of the sensor having an area and generating a charge is not relevant to the signal processing that happens after that. For Smith, he's saying that this sample is best understood as a point, rather than a rectangle. This makes more sense for Smith, who was working in image processing within software, unrelated to displays and sensors.
It’s a single value, but it’s an integral over the square, not a point sample. If a shine a perfectly focused laser very close to the corner of one sensor pixel, I’ll still get a brightness value for the pixel. If it were a point sample, only the brightness at a single point would give an output.
And depending on your application, you absolutely need to account for sensor properties like pixel pitch and color filter array. It affects moire pattern behavior and creates some artifacts.
I’m not saying you can’t think of a pixel as a point sample, but correcting other people who say it’s a little square is just wrong.
It's never a point source. Light is integrated over a finite area to form a singke color sample. During Bayer mosaicking, contributions from neighbouring pixels are integrated to form samples of complementary color channels.
> Light is integrated over a finite area to form a singke color sample. During Bayer mosaicking, contributions from neighbouring pixels are integrated to form samples of complementary color channels.
Integrated into a single color sample indeed. After all the integration, mosaicking, and filtering, a single sample is calculated. That’s the pixel. I think that’s where the confusion is coming from. To Smith, the “pixel” is the sample that lives in the computer.
The actual realization of the image sensors and their filters are not encoded in a typical image format, nor used in a typical high level image processing pipelines. For abstract representations of images, the “pixel” abstraction is used.
The initial reply to this chain focused on how camera sensors capture information about light, and yes, those sensors take up space and operate over time. But the pixel, the piece of data in the computer, is just a point among many.
> Integrated into a single color sample indeed. After all the integration, mosaicking, and filtering, a single sample is calculated. That’s the pixel. I think that’s where the confusion is coming from. To Smith, the “pixel” is the sample that lives in the computer.
> But the pixel, the piece of data in the computer, is just a point among many.
Sure, but saying that this is the pixel, and negating all other forms as not "true" pixels is arbitrary. The real-valued physical pixels (including printer dots) are equally valid forms of pixels. If anything, it would be impossible for humans to sense the pixels without interacting with the real-valued forms.
IMO Smith misapplied the term "pixel" to mean "sample". A pixel is a physical object, a sample is a logical value that corresponds in some way to the state of the physical object (either as an input from a sensor pixel or an output to a display pixel) but is also used in signal processing. Samples aren't squares, pixels (usually) are.
A slightly tangential comment: integrating a continuous image on squares paving the image plane might be best viewed as applying a box filter to the continuous image, resulting in another continuous image, then sampling it point-wise at the center of each square.
It turns out that when you view things that way, pixels as points continues to make sense.
The representation of pixels on the screen is not necessarily normative for the definition of the pixel. Indeed, since different display devices use different representations as you point out, it can't really be. You have to look at the source of the information. Is it a hit mask for a game? Then they are squares. Is it a heatmap of some analytical function? Then they are points. And so on.
They contain zero-order holds, but they also contain reconstruction filters. Ideal reconstruction filters have infinite width, which makes them clearly different in kind. And most DACs nowadays are Delta-Sigma, so the zero-order hold is on a binary value at a much higher frequency than the source signal. The dissimilarities matter.
The 'point' of Alvy's article is that pixels should be treated as point sources when manipulating them, not when displaying them.
Obviously, when a pile of pixels is shown on a screen (or for that matter, collected from a camera's CCD, or blobbed by ink on a piece of paper), it will have some shape: The shape of the LCD matrix, the shape of the physical sensor, the shape of the ink blot. But those aren't pixels, they're the physical shapes of the pixels expressed on some physical medium.
If you're going to manipulate pixels in a computer's memory (like by creating more of them, or fewer), then you'd do best by treating the pixels as sampling points - at this point, the operation is 100% sampling theory, not geometry.
When you're done, and have an XY matrix of pixels again, you'll no doubt have done it so that you can give those pixels _shape_ by displaying them on a screen or sheet of paper or some other medium.
I believe it is a bug in the the emulator's implementation of COMMAND.COM. Often, these DOS "emulators" re-implement the standard commands of DOS, including the shell[1]. This is in addition to emulating weird 16-bit environment stuff and the BIOS.
The bug can pop up in any C program using stdio that assumes it's fine to do `fread` followed immediately by `fwrite`. The spec forbids this. To make matters more confusing, this behavior does _not_ seem to be in modern libc implementations. Or at least, it works on my machine. I bet modern implementations are able to be more sane about managing different buffers for reading and writing.
The original COMMAND.COM from MS-DOS probably did not have this problem, since at least in some versions it was written in assembly[2]. Even for a shell written in C, the fix is pretty easy: seek the file before switching between reading/writing.
The title of this post is confusing, since it clearly _is_ a bug somewhere. But I think the author was excited about possibly finding a bug in libc:
> Sitting down with a debugger, I could just see how the C run-time library (Open Watcom) could be fixed to avoid this problem.
The article is very vague about which emulator and COMMAND.COM it is about, and if they're integrated with each other. Can't be DOSBox, since it handles it correctly:
C:\> echo AB> foo.txt
C:\> echo CD>> foo.txt
C:\> type foo.txt
AB
CD
(Note that echo adds a newline, same as on real DOS, or even UNIX without "-n". This other shell doesn't for some reason.)
The "real" COMMAND.COM, and all other essential parts of MS-/PC-/DR-DOS, have always been written in asm, where none of this libc nonsense matters.
Also it annoys me greatly when people talk about "the C Library" as if it exists in some Platonic realm, and is essential to all software ever written.
> UNIX time counts the number of seconds since an ``epoch.'' This is very convenient for programs that work with time intervals: the difference between two UNIX time values is a real-time difference measured in seconds, within the accuracy of the local clock. Thousands of programmers rely on this fact.
Contrary to this, since at least 2008[0], the POSIX standard (which is just paper not necessarily how real systems worked at that time) has said that "every day shall be accounted for by exactly 86400 seconds." That means that in modern systems using NTP, your Unix timestamps will be off from the expected number of TAI seconds. And yes, it means that a Unix timestamp _can repeat_ on a leap second day.
There's really no perfect way of doing things though. Should Unix time - an integer - represent the number of physical seconds since some epoch moment, or a packed encoding of a "date time" that can be quickly mapped to a calendar day? "The answer is obvious" say both sides simultaneously :^)
EDIT: I know DJB is calling out POSIX's choices in this article, but it seems like his "definition" does diverge from what the count actually meant to a lot of people.
> There's really no perfect way of doing things though. Should Unix time - an integer - represent the number of physical seconds since some epoch moment, or a packed encoding of a "date time" that can be quickly mapped to a calendar day? "The answer is obvious" say both sides simultaneously :^)
Either way arguably POSIX time is the worst of both worlds, not being either of those. This is one of those cases where middleground compromise is worse than either option. Having datetime be just 15:17 bit structure (date:time_of_day) or more practically just int:int struct, it would be infinitely better than current POSIX time. Or just have it be plain SI seconds since epoch counter would also be better than POSIX time. Or even have both options available. There are so many better options, it is almost baffling how POSIX ended up with the worst possible one
I've spent literally hundreds of hours thinking about this, and I've landed on the opinion that TAI should always be the source of truth. It's usually not, because most systems ultimately get their source of truth from the GPS system, which uses something that looks kind-of like UTC (but is actually more similar to TAI, but people prefer to dress it up as UTC). This was a reasonable mistake that has caused so many problems due to the leap seconds.
If you're using TAI, it's more obvious that you have to account for leap seconds in order to convert to a human-readable civil time format (year, month, day, hour, minute, second). You know that you have to incorporate the official leap second list from IANA or some other authority. UTC makes it seem simpler than it actually is, and that's why there are so many problems.
You can always convert from TAI to civil time. You cannot always go the other way.
My opinion is to use a dual structure which is how I had considered in my operating system design: a signed 64-bit (or possibly longer) number of seconds, and a 32-bit number of nanoseconds (which is not always present; sometimes the precision of nanoseconds is unknown or unnecessary, so it would be omitted). There are then separate subtypes of timestamps, such as UTC (which allows the number of nanoseconds to exceed one billion) and SI (which does not allow the number of nanoseconds to exceed one billion). Which is appropriate depends on the application.
That's an interesting approach, but not without pitfalls. You can't just subtract two timestamps to get a time difference, since there may have been one with 2 billion nanoseconds somewhere in between.
- You can subtract two SI-based timestamps to get a SI-based time difference. This will give you the correct number of nanoseconds.
- You can subtract two UTC-based timestamps to get a UTC-based time difference. This will not give you a number of nanoseconds.
Note that, if you have a list of when leap seconds are, then you can convert between SI-based and UTC-based timestamps which are counted from the epoch; in this way it is possible to get a SI-based time difference from UTC-based timestamps if you need it.
It is not as simple as just subtracting them of course, but if your application is meant to use precise time differences then you can just use SI-based timestamps instead of UTC-based timestamps, so that you can simply subtract them from each other. Programs that need to deal with days and calendars instead, are likely to prefer to use UTC-based timestamps instead.
I think the main issue with Monotone was the performance. Linus also hates databases and C++.
--
Hoare didn't come up with this idea either, but he did apply it to version control. He had potentially been influenced by his earlier work on distributed file systems and object systems. Here's his 1999 project making use of hashes: https://web.archive.org/web/20010420023937/venge.net/graydon...
He was in contact with Ian Clarke of Freenet fame (also 1999). There seems to have been a rise in distributed and encrypted communications around the time, as kragen mentions in his other post.
BitTorrent would also come to use hashes for identifying torrents in a tracker, and would come out in 2001, created by Bram Cohen, the author of the post here :)
interestingly it does say bk used md5 in some way; i'm not sure how i overlooked that when i was looking at the code earlier, but indeed md5 is used in lots of places (though apparently not for naming objects the way git and monotone do)
the crucial way bittorrent uses hashes actually is for identifying chunks of files (a .torrent file is mostly chunk hashes by volume); that's why it was immune to the poisoning attacks used against other p2p systems in the early 02000s where malicious entities would send you bogus data. once you had the correct .torrent file, you could tell good data from bad data. using the infohash talking to the tracker is convenient but, as i understand it, there isn't really a security reason for it; the tracker doesn't verify you're really participating productively in the swarm, it just sends your IP to other peers in case you might. so there isn't a strong reason to keep torrent infohashes from colliding
Right, bitkeeper doesn't name files with hashes like git does. But it uses sha1 (or similar) for decentralization: to tell if two remote files are the same.
Another player is tridge's rsync, which also uses hashes like that.
Activation Lock is a weird feature. Users love it when it helps prevent device theft. But really, at the end of the day, you're giving up control of the device to Apple.
Apple has already stopped supporting activation of certain versions of iOS[0]. Also, just as a user can request locking down a phone at any time, Apple could technically lock down your device when they see fit. They're not going to do that, but the fact that it's possible is a bit freaky.
Someday, far in the future, their activation servers will go down, and no unactivated Apple device will be usable. Already, it is not possible to set up a new device without an Internet connection. Hopefully the jailbreak community figures something out by then, or maybe Apple would release a tool...
Apple has stopped signing older version of iOS for devices that could upgrade to later versions. The case you linked to is an iPhone running iOS 9 when Apple will happily sign iOS 11 for that device. iOS 11 is that latest version of the software that will run on that device.
Do we blame Google for not allowing Chromebooks to stick to an older version of their OS?
This isn’t about signing the OS updates. This is about the device contacting Apple’s activation servers during initial setup.
In the linked thread, the users have a “working” device with software already installed, but the activation fails with a cryptic “Activation Error” without any reference to the fact that they must update.
Typically, Apple continues to allow activation on older OSes, as long as they were installed previously. I’ve intentionally kept some devices on older versions, and personally have never had a problem activating after a reset. But I suppose that’s only possible because Apple allowed it.
EDIT: I was wrong about the "Activation Error" not mentioning a software upgrade. It does mention it. But my point about Apple controlling access to the software still stands. It's something weird that Apple did specifically for iOS 9 and the iPhone 6s. Naturally, who really cares about one random version combination? I don't in this particular case, but the fact that Apple can control it is weird to me.
AIUI, another big problem with the 80286 was that it did not support returning to real mode after switching to protected mode. This made compatibility a huge issue, which was a big problem for Microsoft. The 80836, besides marking a switch to 32-bit, added virtual 8086 mode, which allowed for emulating real mode after already having switched to protected mode.
This "virtual 8086" mode is what was used for the VMM kernel of Windows 9x, and later the NTVDM system on 32-bit versions of Windows NT. I remember being able to run some DOS software on Windows XP (but it wasn't perfect).
Asynchronous programming is a great fit for IO-driven programs, because modern IO is inherently asynchronous. This is clearly true for networking, but even for disk IO, generally commands are sent to the disks and results come back later. Another thing that’s asynchronous is user input, and that’s why JS has it.
As for threading vs. explicit yielding (e.g. coroutines), I’d say it’s a matter of taste. I generally prefer to see where code is going to yield. Something like gevent can make control flow confusing, since it’s unclear what will yield, and you need to implement explicit yielding for CPU-bound tasks anyway. Its green threads are based on greenlet, which are cooperative coroutines.
Cooperative multitasking was a big problem in operating systems, where you can’t tell whether other processes are looking for CPU time or not. But within your own code, you can control it however you want!
> Asynchronous programming is a great fit for IO-driven programs
Yeah, but this could already be solved without "async/await compiler magic" in native code just with OS primitives, for instance with Windows-style event objects, it might look like this in pseudo-code:
This would run three IO operations "in parallel", and you're waiting for all three to finish until execution continues after the wait_all() call.
Looks just as convenient as async/await style, but doesn't need special language features or a tricky code-transformation pass in the compiler which turns sequential code into a switch-case state machine (and most importantly, it doesn't have the 'function-color problem').
(this really makes me wonder why Rust has gone down the Javascript-style async/await route with function coloring - the only reason why it remotely makes sense is that it also works in WASM).
> this really makes me wonder why Rust has gone down the Javascript-style async/await route with function coloring - the only reason why it remotely makes sense is that it also works in WASM
As someone who’s done asynchronous programming in Rust before Futures (I’ll call it C style), then with Futures, then with async/await, it’s because it is far simpler. On top of that it allows for an ecosystem of libraries to be built up around common implementations for problems. Without it, what you end up with is a lot of people solving common state machine problems in ways that have global context or other things going on which make the library unportable and not able to easily be reused in other contexts. With async/await, we actually have multiple runtimes in the wild, and common implementations that work across those different runtimes without any changes needed. So while I’m disappointed that we ended up with function coloring, I have to say that it’s far simpler than anything else I’ve worked with while maintaining zero overhead costs allowing it to be used in nostd contexts like Operating Systems and embedded software.
But the difference is that wait_all() is blocking the thread, right? Or does it keep running the event loop while it's waiting, so callbacks for other events can be processed on the same thread?
If it does the latter, the stack will keep growing with each nested wait call:
Yeah it blocks the thread, any other "user work" needs to happen on a different thread. But if you just need multiple non-blocking IO operations run in parallel it's as simple as it gets.
(the operating system's thread scheduler is basically the equivalent to the JS "event loop").
Desktop operating systems all have application event loops that run within a single thread because the OS thread scheduler is not the same thing. If you just want an event loop, trying to use threads instead for everything will often end up in tears due to concurrent data access issues.
An async/await runtime doesn't necessarily need to run everything on the same thread though (that's really just a Javascript runtime restriction), instead it could use a thread pool. In this case, the same concurrent data issues would apply.
The wait_all() "yields" to the operating system's thread scheduler, which switches to another thread that's ready to run (or if fibers are used instead of threads, a 'fiber scheduler' would switch to a different fiber within the same thread).
Taking into account that an async/await runtime also needs to switch to a different "context", the performance difference really shouldn't be all that big, especially if the task scheduler uses fibers (YMMV of course).
That's not how an operating system models disk access though. You synchronously write to the kernel cache, and the kernel eventually gets those written to disk.
Wanting to do asynchronous I/O to disk is only useful if you're aiming to bypass the cache. In practice it is very hard to reach higher performance when doing that though.
I was referring to the fact that interaction with the disk itself is asynchronous. Indeed, the interface provided by a kernel for files is synchronous, and for most cases, that's what programmers probably want.
But I also think the interest in things like io_uring in Linux reflect that people are open to asynchronous file IO, since the kernel is doing asynchronous work internally. To be honest, I don't know much about io_uring though - I haven't used it for anything serious.
There's no perfect choice (as always) -- After all, for extremely high-performance scenarios, people avoid the async nature of IO entirely, and dedicate a thread to busy-looping and polling for readiness. That's what DPDK does for networking. And I think for io_uring and other Linux disk interfaces have options to use polling internally.
This signal processing applies to images as well. Resampling is used very often for upscaling, for example. Here's an example: https://en.wikipedia.org/wiki/Lanczos_resampling
> It was already wrong in 1995 when monitors where CRTs, and it's way wrong in 2025 in the LCD/OLED era where pixels are truly discrete.
I don't think it has anything to do with display technologies though. Imagine this: there is a computer that is dedicated to image processing. It has no display, no CRT, no LCD, nothing. The computer is running a service that is resizing images from 100x100 pixels to 200x200 pixels. Would the programmer of this server be better off thinking in terms of samples or rectangular subdivisions of a display?
Alvy Ray Smith, the author of this paper, was coming from the background of developing Renderman for Pixar. In that case, there were render farms doing all sorts of graphics processing before the final image was displayed anywhere.