0

I am creating and rendering to a BufferedImage. This bitmap will be written to a printer after it's complete. I want to do all drawing using EMUs as the units. The bitmap will be 600 dpi. So [0,0] is the upper left corner but a pixel written to [914400,914400], will be at [600,600] in the bitmap.

How do I set the User Space for this? I know it's using the transform somehow, but everything I've tried hasn't worked (I think).

David Thielen
  • 28,723
  • 34
  • 119
  • 193
  • The first thing you need to know is what the source images dpi is, for [example](http://stackoverflow.com/questions/18460008/printable-prints-bufferedimage-with-incorrect-size/18466550#18466550), once you have this information, you can calculate the size the image should be to meet the required 600dpi output, these calculations are also demonstrated in the longed example. If you're starting with a blank image, then you need to know the output size of the paper, in pixels, inches or cns, from there you can calculate the dpi (image width/height), java print api assumes a dpi of 72dpi – MadProgrammer Dec 10 '15 at 20:29
  • @MadProgrammer no my question is a bit different. There is no src bitmap. I want to create a BufferImage and then a Graphics object from it. At this point the user space == pixels in the bitmap. I want to change that so the user space is in EMUs where I treat the pixels as 600 dpi. – David Thielen Dec 10 '15 at 20:41
  • I'm confused. If you want to do your work in inches, wouldn't your math be backwards? If you place an image at (1,1), it should go to pixel [600,600], not the other way around. – markspace Dec 10 '15 at 20:46
  • So, create an image which is 600dpi, what's you target output size (in inches), from there you can calculate the number of pixels you will need – MadProgrammer Dec 10 '15 at 20:53
  • So, for example, if your want to print to a A4 page, which is `21.0cm`x`29.7cm` @ 600dpi, you could use `(21.0 * 0.393700787) * 600`x`(29.7 * 0.393700787) * 600` which gives you an image size of `4960.6299162`x`7015.74802434` pixels – MadProgrammer Dec 10 '15 at 20:59
  • @MadProgrammer Yes I can do a calculation everytime I pass in parameters. I'm trying to avoid that. So I want to set the user space so it does that for me. – David Thielen Dec 10 '15 at 21:24
  • @DavidThielen Not sure you can, you simply need to be able to translate your coordinates from one context to another. Images work in pixels, that's their base unit – MadProgrammer Dec 10 '15 at 21:27
  • @MadProgrammer I know this can be done - years ago I used transforms on the Graphics object to not only change the units, but to rotate stuff. Unfortunately I don't remember exactly how to do it - what I try keeps coming up wrong. – David Thielen Dec 10 '15 at 21:32
  • @DavidThielen Transformation still only works on the pixel level, you will still need to translate from one coordinate space to another – MadProgrammer Dec 10 '15 at 21:33
  • @MadProgrammer - that's what user space does, translate between the passed in coordinates and the pixel coordinates. Revisit this question in 2 days and you'll see the answer. If no one else knows, I'll dive back in and figure it out. – David Thielen Dec 10 '15 at 21:34
  • @DavidThielen I think what you want is `AffineTransform` (`.getScaleInstance(...)`) and `Graphics2D.setTransform(...)` or `Graphics2D.transform(...)`. Then the `Graphics2D` will handle the mapping between user space and device space (or whatever you'll call it. :-) ). – Harald K Dec 11 '15 at 13:09
  • @MadProgrammer - solution posted now. – David Thielen Dec 13 '15 at 18:11
  • @DavidThielen Personally, I'm no convinced, but that's maybe because I use `AffineTransformation#scale` for other things. If it works for then I guess that's all that matters – MadProgrammer Dec 13 '15 at 20:03

2 Answers2

0

If you wish to have your image in size of [600,600], but you wish a pixel written to [914400,914400] to be at [600,600], then, before you start to draw anything on the Graphics2D object you get from this BufferedImage, just call a scale on it like this:

    BufferedImage image = new BufferedImage(600, 600, BufferedImage.TYPE_INT_RGB);
    Graphics2D g = (Graphics2D) image.getGraphics();
    g.scale(600/914400d,600/914400d);
    // your drawing code below

Be aware, that dependig on your rendering hints settings if you draw something small, like just one pixel it can be not visible at all if drawn at this scale.

DPI stands for dots per inch (aka pixels per inch). If someting is at position 600,600 in a 600 dpi image, then it is one inch far from top and one inch far from left. If you have image of size [914400,914400] then it is 1524x1524 inches big, it will occupy 2,3 TERA BYTES of RAM memory in 3 byte RGB fromat, it is rather not something you wish?

Krzysztof Cichocki
  • 6,294
  • 1
  • 16
  • 32
  • No, I want the user space units to be 914400 per inch while I want the pixels to be 600 per inch. So placing a pixel in the Graphics object at [914400,914400] will be the pixel at [600,600]. – David Thielen Dec 10 '15 at 20:39
  • @DavidThielen So, from the sounds if it, you've already got the calculation worked out – MadProgrammer Dec 10 '15 at 21:01
0

Ok, this is ridiculously simple. I was trying to build the AffineTransform from scratch. All I needed to do is:

/** Emus Per Inch */
public static final int EPI = 914400;

image = new BufferedImage(pixelsWidth, pixelsHeight, BufferedImage.TYPE_INT_ARGB);
graphics = image.createGraphics();

AffineTransform scaleToEmus = AffineTransform.getScaleInstance((float)dpi / (float)IDrawingSurface.EPI, (float)dpi / (float)IDrawingSurface.EPI);
graphics.transform(scaleToEmus);

And that's it. getScaleInstance() does all the heavy lifting (I remembered that it was something very simple, I just couldn't remember what!)

dpi is whatever DPI you want for the bitmap.

David Thielen
  • 28,723
  • 34
  • 119
  • 193