How do I get the logical to device ratio from an EMF ()Enhanced MetaFile? - .emf

I have closely studied the MS documentation on EMF files and from the definitions for the 3 header types I can't see how to convert from logical coords (which the graphics records coords are stored as) to device coords. The header has a Frame part that specifies the page size surrounding (but not necessarily bounding) the composite image in 0.01mm units; and a Bounds part that specifies the actual bounds of the composite image in logical units. And finally there are the Device and Millimeters parts that specify the size of the recording device.
From these there seems no way that calculating the ratio to convert from logical coords to device coords is possible.
I must be missing something simple :-)

Think I sussed it: you use the records:
EMR_SETVIEWPORTEXTEX - device units
EMR_SETVIEWPORTORGEX - (ditto)
EMR_SETWINDOWEXTEX - logical units
EMR_SETWINDOWORGEX - (ditto)
EMR_SETWORLDTRANSFORM

Yes, the Bounds header property is specified as the actual bounds of the composite image (in logical units) but, on investigating Inkscape and Adobe Illustrator created emf's, I find that they do not adhere to this.

After creating your DC (createDC), use getdevicecaps to get the total number of dots (raster lines) available for your DC. Horzres for width, Vertres for height. The dots aren't square. Then after reading your EMF file with getenhmetafile, use getenhmetafileheader to get the header record. You then look at either rclbound or rclframe in the header record. The second rectangle is a multiple of the first rectangle. For emfs created by powerpoint, the top and left is zero in my experience, so you focus on the bottom and right. The ratio of the two is your aspect ratio. You use that ratio to calculate the rectangle in DC units that has the same aspect ratio as rclbound, but likely adds margins all around so your image doesn't go right to the edge of your device. That rectangle, with units that fall within the range provide by vertres and horzres is the third arguement to the playenhmetafile command where your finish up. In sum, you convert from the EMF logical units to the DC logical units by using vertres and horzres (from your DC) combined with the aspect ratio you calculate (from your EMF).

Related

Can't find GDK::InterpType members in gtkmm

I'm trying to make a Gtk::Image widget display a picture from a file, but prevent the widget from expanding in size, so I'm loading it from a Gdk::Pixbuf and then scaling the picture. I'm using Gdk::Pixbuf instead of GdkPixBuf because the latter one works on regular pointers, but Gtk::Image requires a Glib::RefPtr<Gdk::Pixbuf>. (Just mentioning all this in case there's a better way to achieve what I'm doing that I'm unaware of.)
auto pixbuf = Gdk::Pixbuf::create_from_file("/home/raitis/Music/WRLD/Awake EP/cover.jpg");
auto scaled = pixbuf->scale_simple(48, 48, Gdk::InterpType::NEAREST);
image->set(scaled);
Anyway, problem is that although I'm following the documentation for Gdk::Pixbuf, line 2 in my code generate the error:
error: ‘NEAREST’ is not a member of ‘Gdk::InterpType’
auto scaled = pixbuf->scale_simple(48, 48, Gdk::InterpType::NEAREST);
^~~~~~~
Trying GDK_INTERP_NEAREST instead also leads to an error. :(
no known conversion for argument 3 from ‘GdkInterpType’ to ‘Gdk::InterpType’
From the stable gtkmm gdkmm documentation, Gdk::InterpType members are:
INTERP_NEAREST
Nearest neighbor sampling; this is the fastest and lowest quality
mode. Quality is normally unacceptable when scaling down, but may be OK when
scaling up.
INTERP_TILES
This is an accurate simulation of the PostScript image operator
without any interpolation enabled.
Each pixel is rendered as a tiny parallelogram of solid color, the
edges of which are implemented with antialiasing. It resembles nearest
neighbor for enlargement, and bilinear for reduction.
INTERP_BILINEAR
Best quality/speed balance; use this mode by default.
Bilinear interpolation. For enlargement, it is equivalent to
point-sampling the ideal bilinear-interpolated image. For reduction,
it is equivalent to laying down small tiles and integrating over the
coverage area.
INTERP_HYPER
This is the slowest and highest quality reconstruction function.
It is derived from the hyperbolic filters in Wolberg's "Digital Image
Warping", and is formally defined as the hyperbolic-filter sampling
the ideal hyperbolic-filter interpolated image (the filter is designed
to be idempotent for 1:1 pixel mapping).
And from the documentation of the Gdk::Pixbuf, in the scale_simple method you'll find a reference to the interpolation type:
Leaves src unaffected. interp_type should be Gdk::INTERP_NEAREST if
you want maximum speed (but when scaling down Gdk::INTERP_NEAREST is
usually unusably ugly). The default interp_type should be
Gdk::INTERP_BILINEAR which offers reasonable quality and speed.

How to place text in the certain position when using SceneKit?

I have tried to follow the same simple logic as for cylinder, box … , so just by defining the position for textNode, but it is not working.
func makeText(text3D: String, position: SCNVector3, depthOfText: CGFloat, color: NSColor, transparency: CGFloat) -> SCNNode
{
let textTodraw = SCNText(string: text3D, extrusionDepth: depthOfText)
textTodraw.firstMaterial?.transparency = transparency
textTodraw.firstMaterial?.diffuse.contents = color
let textNode = SCNNode(geometry: textTodraw)
textNode.position = position
return textNode
}
The default font for SCNText is 36 point Helvetica, and a "point" in font size is the same as a unit of scene space. (Well, of local space for the node containing the SCNText geometry. But unless you've set a scale factor on your node, local space units are the same as scene space units.) That means even a short label can be tens of units tall and hundreds of units wide.
It's typical to build SceneKit scenes with smaller scope — for example, simple test scenes like you might throw together in a Swift playground using the default sizes for SCNBox, SCNSphere, etc might be only 3-4 units wide. (And if you're using SceneKit with ARKit, scene units are meters, so some text in 36 "point" font is the size of a few office blocks downtown.)
Also, the anchor point for a text geometry relative to its containing node is at the lower left corner of the text. Put all this together and it's entirely possible that there are giant letters looming over the rest of your scene, hiding just out of camera view.
Note that if you try to fix this by setting a much smaller font on your SCNText, the text might get jagged and chunky. That's because the flatness property is measured relative to the point size of the text (more precisely, it's measured in a coordinate system where one unit == one point of text size). So if you choose a font size that'd be tiny by screen/print standards, you'll need to scale down the flatness accordingly to still get smooth curves in your letters.
Alternatively, you can leave font sizes and flatness alone — instead, set a scale factor on the node containing the text geometry, or set that node's pivot to a transform matrix that scales down its content. For example, if you set a scale factor of 1/72, one unit of scene space is the same as one "inch" (72 points) of text height — depending on the other sizes in your scene, that might make it a bit easier to think of font sizes the way you do in 2D.
The fact is you generally just use "small numbers" for font sizes in SceneKit.
In 3D you always use real meters. A humanoid robot must be about "2" units tall, a car is about "3" units long and so on.
Very typical sizes for the font is about "0.1"
Note that the flatness value is SMALL, usually about one hundredth the size of the font. (Which is obvious, it's how long the line segments are.)
Typical:
t.font = UIFont(name: "Blah", size: 0.10)
t.flatness = 0.001
Set the flatness to about 1/4 the size of the font (hence, 0.025 in the example) to understand what "flatness" is.
I would never change the scale of the type node, that's a bad idea for many reasons. There's absolutely no reason to do so, and it makes it very difficult to genuinely set the flatness appropriately.
But note ...
That being said, on the different platforms and different versions, SCNText() often does a basically bad job drawing text, and it can go to hell at small numbers. So yeah, you may indeed have to scale in practice, if the text construction is crap at (very) small values :/

In what Units does QSvgGenerator operate in?

In the official documentation for QSvgGenerator there are two properties that relate to the size of the output. One is size and the other is resolution.
Resolution is set in DPI (Dots Per Inch), but which unit is size in?
I have tested multiple values now and none make sense to me when inspecting the output file.
size is in pixels. If you treat size as pixels then the output SVG width and height attributes will be approximately the correct number of mm.

Do I need to gamma correct the final color output on a modern computer/monitor

I've been under the assumption that my gamma correction pipeline should be as follows:
Use sRGB format for all textures loaded in (GL_SRGB8_ALPHA8) as all art programs pre-gamma correct their files. When sampling from a GL_SRGB8_ALPHA8 texture in a shader OpenGL will automatically convert to linear space.
Do all lighting calculations, post processing, etc. in linear space.
Convert back to sRGB space when writing final color that will be displayed on the screen.
Note that in my case the final color write involves me writing from a FBO (which is a linear RGB texture) to the back buffer.
My assumption has been challenged as if I gamma correct in the final stage my colors are brighter than they should be. I set up for a solid color to be drawn by my lights of value { 255, 106, 0 }, but when I render I get { 255, 171, 0 } (as determined by print-screening and color picking). Instead of orange I get yellow. If I don't gamma correct at the final step I get exactly the right value of { 255, 106, 0 }.
According to some resources modern LCD screens mimic CRT gamma. Do they always? If not, how can I tell if I should gamma correct? Am I going wrong somewhere else?
Edit 1
I've now noticed that even though the color I write with the light is correct, places where I use colors from textures are not correct (but rather far darker as I would expect without gamma correction). I don't know where this disparity is coming from.
Edit 2
After trying GL_RGBA8 for my textures instead of GL_SRGB8_ALPHA8, everything looks perfect, even when using the texture values in lighting computations (if I half the intensity of the light, the output color values are halfed).
My code is no longer taking gamma correction into account anywhere, and my output looks correct.
This confuses me even more, is gamma correction no longer needed/used?
Edit 3 - In response to datenwolf's answer
After some more experimenting I'm confused on a couple points here.
1 - Most image formats are stored non-linearly (in sRGB space)
I've loaded a few images (in my case both .png and .bmp images) and examined the raw binary data. It appears to me as though the images are actually in the RGB color space, as if I compare the values of pixels with an image editing program with the byte array I get in my program they match up perfectly. Since my image editor is giving me RGB values, this would indicate the image stored in RGB.
I'm using stb_image.h/.c to load my images and followed it all the way through loading a .png and did not see anywhere that it gamma corrected the image while loading. I also examined the .bmps in a hex editor and the values on disk matched up for them.
If these images are actually stored on disk in linear RGB space, how am I supposed to (programatically) know when to specify an image is in sRGB space? Is there some way to query for this that a more featured image loader might provide? Or is it up to the image creators to save their image as gamma corrected (or not) - meaning establishing a convention and following it for a given project. I've asked a couple artists and neither of them knew what gamma correction is.
If I specify my images are sRGB, they are too dark unless I gamma correct in the end (which would be understandable if the monitor output using sRGB, but see point #2).
2 - "On most computers the effective scanout LUT is linear! What does this mean though?"
I'm not sure I can find where this thought is finished in your response.
From what I can tell, having experimented, all monitors I've tested on output linear values. If I draw a full screen quad and color it with a hard-coded value in a shader with no gamma correction the monitor displays the correct value that I specified.
What the sentence I quoted above from your answer and my results would lead me to believe is that modern monitors output linear values (i.e. do not emulate CRT gamma).
The target platform for our application is the PC. For this platform (excluding people with CRTs or really old monitors), would it be reasonable to do whatever your response to #1 is, then for #2 to not gamma correct (i.e. not perform the final RGB->sRGB transformation - either manually or using GL_FRAMEBUFFER_SRGB)?
If this is so, what are the platforms on which GL_FRAMEBUFFER_SRGB is meant for (or where it would be valid to use it today), or are monitors that use linear RGB really that new (given that GL_FRAMEBUFFER_SRGB was introduced 2008)?
--
I've talked to a few other graphics devs at my school and from the sounds of it, none of them have taken gamma correction into account and they have not noticed anything incorrect (some were not even aware of it). One dev in particular said that he got incorrect results when taking gamma into account so he then decided to not worry about gamma. I'm unsure what to do in my project for my target platform given the conflicting information I'm getting online/seeing with my project.
Edit 4 - In response to datenwolf's updated answer
Yes, indeed. If somewhere in the signal chain a nonlinear transform is applied, but all the pixel values go unmodified from the image to the display, then that nonlinearity has already been pre-applied on the image's pixel values. Which means, that the image is already in a nonlinear color space.
Your response would make sense to me if I was examining the image on my display. To be sure I was clear, when I said I was examining the byte array for the image I mean I was examining the numerical value in memory for the texture, not the image output on the screen (which I did do for point #2). To me the only way I could see what you're saying to be true then is if the image editor was giving me values in sRGB space.
Also note that I did try examining the output on monitor, as well as modifying the texture color (for example, dividing by half or doubling it) and the output appeared correct (measured using the method I describe below).
How did you measure the signal response?
Unfortunately my methods of measurement are far cruder than yours. When I said I experimented on my monitors what I meant was that I output solid color full screen quad whose color was hard coded in a shader to a plain OpenGL framebuffer (which does not do any color space conversion when written to). When I output white, 75% gray, 50% gray, 25% gray and black the correct colors are displayed. Now here my interpretation of correct colors could most certainly be wrong. I take a screenshot and then use an image editing program to see what the values of the pixels are (as well as a visual appraisal to make sure the values make sense). If I understand correctly, if my monitors were non-linear I would need to perform a RGB->sRGB transformation before presenting them to the display device for them to be correct.
I'm not going to lie, I feel I'm getting a bit out of my depth here. I'm thinking the solution I might persue for my second point of confusion (the final RGB->sRGB transformation) will be a tweakable brightness setting and default it to what looks correct on my devices (no gamma correction).
First of all you must understand that the nonlinear mapping applied to the color channels is often more than just a simple power function. sRGB nonlinearity can be approximated by about x^2.4, but that's not really the real deal. Anyway your primary assumptions are more or less correct.
If your textures are stored in the more common image file formats, they will contain the values as they are presented to the graphics scanout. Now there are two common hardware scenarios:
The scanout interface outputs a linear signal and the display device will then internally apply a nonlinear mapping. Old CRT monitors were nonlinear due to their physics: The amplifiers could put only so much current into the electron beam, the phosphor saturating and so on – that's why the whole gamma thing was introduced in the first place, to model the nonlinearities of CRT displays.
Modern LCD and OLED displays either use resistor ladders in their driver amplifiers, or they have gamma ramp lookup tables in their image processors.
Some devices however are linear, and ask the image producing device to supply a proper matching LUT for the desired output color profile on the scanout.
On most computers the effective scanout LUT is linear! What does this mean though? A little detour:
For illustration I quickly hooked up my laptop's analogue display output (VGA connector) to my analogue oscilloscope: Blue channel onto scope channel 1, green channel to scope channel 2, external triggering on line synchronization signal (HSync). A quick and dirty OpenGL program, deliberately written with immediate mode was used to generate a linear color ramp:
#include <GL/glut.h>
void display()
{
GLuint win_width = glutGet(GLUT_WINDOW_WIDTH);
GLuint win_height = glutGet(GLUT_WINDOW_HEIGHT);
glViewport(0,0, win_width, win_height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 1, 0, 1, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glBegin(GL_QUAD_STRIP);
glColor3f(0., 0., 0.);
glVertex2f(0., 0.);
glVertex2f(0., 1.);
glColor3f(1., 1., 1.);
glVertex2f(1., 0.);
glVertex2f(1., 1.);
glEnd();
glutSwapBuffers();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutCreateWindow("linear");
glutFullScreen();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
The graphics output was configured with the Modeline
"1440x900_60.00" 106.50 1440 1528 1672 1904 900 903 909 934 -HSync +VSync
(because that's the same mode the flat panel runs in, and I was using cloning mode)
gamma=2 LUT on the green channel.
linear (gamma=1) LUT on the blue channel
This is how the signals of a single scanout line look like (upper curve: Ch2 = green, lower curve: Ch1 = blue):
You can clearly see the x⟼x² and x⟼x mappings (parabola and linear shapes of the curves).
Now after this little detour we know, that the pixel values that go to the main framebuffer, go there as they are: The OpenGL linear ramp underwent no further changes and only when a nonlinear scanout LUT was applied it altered the signal sent to the display.
Either way the values you present to the scanout (which means the on-screen framebuffers) will undergo a nonlinear mapping at some point in the signal chain. And for all standard consumer devices this mapping will be according to the sRGB standard, because it's the smallest common factor (i.e. images represented in the sRGB color space can be reproduced on most output devices).
Since most programs, like webbrowsers assume the output to undergo a sRGB to display color space mapping, they simply copy the pixel values of the standard image file formats to the on-screen frame as they are, without performing a color space conversion, thereby implying that the color values within those images are in sRGB color space (or they will often merely convert to sRGB, if the image color profile is not sRGB); the correct thing to do (if, and only if the color values written to the framebuffer are scanned out to the display unaltered; assuming that scanout LUT is part of the display), would be conversion to the specified color profile the display expects.
But this implies, that the on-screen framebuffer itself is in sRGB color space (I don't want to split hairs about how idiotic that is, lets just accept this fact).
How to bring this together with OpenGL? First of all, OpenGL does all it's color operations linearly. However since the scanout is expected to be in some nonlinear color space, this means, that the end result of the rendering operations of OpenGL somehow must be brougt into the on-screen framebuffer color space.
This is where the ARB_framebuffer_sRGB extension (which went core with OpenGL-3) enters the picture, which introduced new flags used for the configuration of window pixelformats:
New Tokens
Accepted by the <attribList> parameter of glXChooseVisual, and by
the <attrib> parameter of glXGetConfig:
GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20B2
Accepted by the <piAttributes> parameter of
wglGetPixelFormatAttribivEXT, wglGetPixelFormatAttribfvEXT, and
the <piAttribIList> and <pfAttribIList> of wglChoosePixelFormatEXT:
WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9
Accepted by the <cap> parameter of Enable, Disable, and IsEnabled,
and by the <pname> parameter of GetBooleanv, GetIntegerv, GetFloatv,
and GetDoublev:
FRAMEBUFFER_SRGB 0x8DB9
So if you have a window configured with such a sRGB pixelformat and enable sRGB rasterization mode in OpenGL with glEnable(GL_FRAMEBUFFER_SRGB); the result of the linear colorspace rendering operations will be transformed in sRGB color space.
Another way would be to render everything into an off-screen FBO and to the color conversion in a postprocessing shader.
But that's only the output side of rendering signal chain. You also got input signals, in the form of textures. And those are usually images, with their pixel values stored nonlinearly. So before those can be used in linear image operations, such images must be brought into a linear color space first. Lets just ignore for the time being, that mapping nonlinear color spaces into linear color spaces opens several of cans of worms upon itself – which is why the sRGB color space is so ridiculously small, namely to avoid those problems.
So to address this an extension EXT_texture_sRGB was introduced, which turned out to be so vital, that it never went through being ARB, but went straight into the OpenGL specification itself: Behold the GL_SRGB… internal texture formats.
A texture loaded with this format undergoes a sRGB to linear RGB colorspace transformation, before being used to source samples. This gives linear pixel values, suitable for linear rendering operations, and the result can then be validly transformed to sRGB when going to the main on-screen framebuffer.
A personal note on the whole issue: Presenting images on the on-screen framebuffer in the target device color space IMHO is a huge design flaw. There's no way to do everything right in such a setup without going insane.
What one really wants is to have the on-screen framebuffer in a linear, contact color space; the natural choice would be CIEXYZ. Rendering operations would naturally take place in the same contact color space. Doing all graphics operations in contact color spaces, avoids the opening of the aforementioned cans-of-worms involved with trying to push a square peg named linear RGB through a nonlinear, round hole named sRGB.
And although I don't like the design of Weston/Wayland very much, at least it offers the opportunity to actually implement such a display system, by having the clients render and the compositor operate in contact color space and apply the output device's color profiles in a last postprocessing step.
The only drawback of contact color spaces is, that there it's imperative to use deep color (i.e. > 12 bits per color channel). In fact 8 bits are completely insufficient, even with nonlinear RGB (the nonlinearity helps a bit to cover up the lack of perceptible resolution).
Update
I've loaded a few images (in my case both .png and .bmp images) and examined the raw binary data. It appears to me as though the images are actually in the RGB color space, as if I compare the values of pixels with an image editing program with the byte array I get in my program they match up perfectly. Since my image editor is giving me RGB values, this would indicate the image stored in RGB.
Yes, indeed. If somewhere in the signal chain a nonlinear transform is applied, but all the pixel values go unmodified from the image to the display, then that nonlinearity has already been pre-applied on the image's pixel values. Which means, that the image is already in a nonlinear color space.
2 - "On most computers the effective scanout LUT is linear! What does this mean though?
I'm not sure I can find where this thought is finished in your response.
This thought is elaborated in the section that immediately follows, where I show how the values you put into a plain (OpenGL) framebuffer go directly to the monitor, unmodified. The idea of sRGB is "put the values into the images exactly as they are sent to the monitor and build consumer displays to follow that sRGB color space".
From what I can tell, having experimented, all monitors I've tested on output linear values.
How did you measure the signal response? Did you use a calibrated power meter or similar device to measure the light intensity emitted from the monitor in response to the signal? You can't trust your eyes with that, because like all our senses our eyes have a logarithmic signal response.
Update 2
To me the only way I could see what you're saying to be true then is if the image editor was giving me values in sRGB space.
That's indeed the case. Because color management was added to all the widespread graphics systems as an afterthought, most image editors edit pixel values in their destination color space. Note that one particular design parameter of sRGB was, that it should merely retroactively specify the unmanaged, direct value transfer color operations as they were (and mostly still are done) done on consumer devices. Since there happens no color management at all, the values contained in the images and manipulated in editors must be in sRGB already. This works for so long, as long images are not synthetically created in a linear rendering process; in case of the later the render system has to take into account the destination color space.
I take a screenshot and then use an image editing program to see what the values of the pixels are
Which gives you of course only the raw values in the scanout buffer without the gamma LUT and the display nonlinearity applied.
I wanted to give a simple explanation of what went wrong in the initial attempt, because although the accepted answer goes in-depth on colorspace theory, it doesn't really answer that.
The setup of the pipeline was exactly right: use GL_SRGB8_ALPHA8 for textures, GL_FRAMEBUFFER_SRGB (or custom shader code) to convert back to sRGB at the end, and all your intermediate calculations will be using linear light.
The last bit is where you ran into trouble. You wanted a light with a color of (255, 106, 0) - but that's an sRGB color, and you're working with linear light. To get the color you want, you need to convert that color to the linear space, the same way that GL_SRGB8_ALPHA8 is doing for your textures. For your case, this would be a vec3 light with intensity (1, .1441, 0) - this is the value after applying gamma-compression.

jpegtran.exe not correctly rotating image

I have a freshly compiled libjpeg version 9 and tried running jpegtran.exe in command line with the arguments:
.\jpegtran.exe -rotate 180 -outfile test_output1.jpg testimg.jpg
testimg.jpg: test_output1.jpg:
As you can see it does rotate the image but it clips it and it's not put together correctly. The usage.txt file that comes with the package isn't totally up to date because I had to use the -outfile switch instead of what it says:
jpegtran uses a command line syntax similar to cjpeg or djpeg. On
Unix-like systems, you say:
jpegtran [switches] [inputfile] >outputfile
On most non-Unix systems, you say:
jpegtran [switches] inputfile outputfile
where both the input and output files are JPEG
files.
To specify the coded JPEG representation used in the output file,
jpegtran accepts a subset of the switches recognized by cjpeg:
-optimize Perform optimization of entropy encoding parameters.
-progressive Create progressive JPEG file.
-arithmetic Use arithmetic coding.
-restart N Emit a JPEG restart marker every N MCU rows, or every N MCU blocks if "B" is attached to the number.
-scans file Use the scan script given in the specified text file.
See the previous discussion of cjpeg for more details about these
switches. If you specify none of these switches, you get a plain
baseline-JPEG output file. The quality setting and so forth are
determined by the input file.
The image can be losslessly transformed by giving one of these
switches:
-flip horizontal Mirror image horizontally (left-right).
-flip vertical Mirror image vertically (top-bottom).
-rotate 90 Rotate image 90 degrees clockwise.
-rotate 180 Rotate image 180 degrees.
-rotate 270 Rotate image 270 degrees clockwise (or 90 ccw).
-transpose Transpose image (across UL-to-LR axis).
-transverse Transverse transpose (across UR-to-LL axis).
Oddly enough (or maybe not), if I execute .\jpegtran.exe -rotate 180 -outfile test_output2.jpg test_output1.jpg I get the original image back without any clipping issues. It's flipping the clipped parts but just not lining it up right with the rest of the image.
test_output2.jpg:
I get the same result by executing jpegtran.exe -rotate 90 twice.
Also, I tried it on a larger .jpg file which resulted in the same issue but the file size was 18KB smaller for the output. I imagine the issue is related to this.
Edit - I also found this blurb which seems to describe the problem:
jpegtran's default behavior when transforming an odd-size image is
designed to preserve exact reversibility and mathematical consistency
of the transformation set. As stated, transpose is able to flip the
entire image area. Horizontal mirroring leaves any partial iMCU
column at the right edge untouched, but is able to flip all rows of
the image. Similarly, vertical mirroring leaves any partial iMCU row
at the bottom edge untouched, but is able to flip all columns. The
other transforms can be built up as sequences of transpose and flip
operations; for consistency, their actions on edge pixels are defined
to be the same as the end result of the corresponding
transpose-and-flip sequence.
The -trim switch works, if you can call it that, and trims out the disorganized data but the image is smaller and lost data.
test_output5.jpg:
Adding the -perfect switch which supposedly stops the above from happening results in this: transformation is not perfect for output and no image.
So is it not possible to losslessly rotate a .jpg? I could, myself, go into paint and reconstruct the original image by simply moving the edge lines into their correct place. Is there a method to do this within libjpeg?
A lossless rotation works with whole DCT blocks contained within the JPEG file. These blocks are always 8x8 or 16x16 pixels (depending on the compression downsampling settings). The file contains a width and height so the extra pixels can be thrown away when the image is decoded, but there's no way to move the clipping from the right/bottom edge to the left/top edge. The software is doing the best it can with an impossible problem.
As you've discovered the way around this problem is to make the width and height evenly divisible by 16. You'll find that images from cameras for example will have this property.