synopse pdf changing paper size using Delphi - delphi

I am trying to create a PDF document using Synopse SynPDF library using Delphi. I need to be able to change the paper size on the fly to accommodate the document I am creating. The paper size's height needs to be changed anywhere from 11 inches to over 100 inches. I would also like to set the resolution of the image to be anywhere from 300 pixels per inch to 600 pixels per inch. This is what I have as a test.
lPdf := TPdfDocumentGDI.Create;
try
lPdf.ScreenLogPixels:=600;
lPdf.DefaultPageHeight := lPdf.ScreenLogPixels * 50; // Since ScreenLogPixels holds the number of pixels per inch this should give me a 50 inch long page.
lPdf.DefaultPageWidth := lPdf.ScreenLogPixels * 8; // Same here with Page being 8 inches wide.
// When viewing the document in Adobe Reader the page height and width 66.67 x 200.00 with nothing displayed
// If I comment out the ScreenLogPixels line the page size becomes 10.67 x 66.67 with a pixel count of 768 x 4800 with the proper text on the document.
lPage := lPDF.AddPage;
lPdf.VCLCanvas.Brush.Style:=bsClear;
MyY:=300;
lPDF.VCLCanvas.TextOut(100, 100, 'Width = ' + IntToStr(lPage.PageWidth) +
' Height = ' + IntToStr(lPage.PageHeight));
for MyX := 1 to 400 do begin
MyXLoc:=(MyX*120) mod (lPage.PageWidth);
MyString:=IntToStr(MyX);
lPDF.VCLCanvas.TextOut(MyXLoc, MyY, Mystring);
lPDF.VCLCanvas.Font.Size:= lPDF.VCLCanvas.Font.Size+4;
lPDF.VCLCanvas.Rectangle(MyXLoc, MyY, MyXLoc+lPDF.VCLCanvas.TextWidth(MyString), MyY+lPDF.VCLCanvas.TextHeight(MyString));
MyY := MyY + lPDF.VCLCanvas.TextHeight(MyString);
end;
lPdf.SaveToFile('c:\Syntest.pdf');
finally
lPdf.Free;
end;

In PDF, all locations and sizes are stored in a logical value called a PDF unit. 1 PDF unit is equivalent to 1/72 of an inch.
DefaultPageHeight and DefaultPageWidth are values in PDF units, so 1/72th of a inch.
So for a 50' * 8' page, you can write:
lPdf.DefaultPageHeight := 72 * 50;
lPdf.DefaultPageWidth := 72 * 8;
Then the VCL canvas available in lPdf.VCLCanvas will have a diverse coordinate system, depending in fact of lPdf.ScreenLogPixels.
So when you draw something in the lPdf.VCLCanvas, ensure you use the right size for coordinates, i.e. via lPdf.VCLCanvasSize values.

Related

Watchkit Interface Controller 38mm and 42mm background image size

For some reason, I have searched high and low, and cannot find the exact image sizes that I need to supply for a background image for both the 38mm and 42mm sizes in Watchkit. Currently I am stretching a smaller image using "scale to fill".
I don't want it stretched, so I am looking for real pixel sizes.
Here is the code I use to get the size for the background image in my WKInterfaceController. My app has a page control, you you probably don't want the extra -14 at the end.
-(CGSize)backgroundSize
{
CGRect contentFrame = self.contentFrame;
CGSize size = contentFrame.size;
CGFloat contentScale = 2.0;
size.width *= contentScale;
size.height *= contentScale;
//I lined up the generated image with one in the simulator until
//they perfectly matched. I did this on both 38 and 42 mm.
//I am not sure why they all came out to be off 4.
//There is an offset of 10 in IB and I am not sure I need this
//to be 4 to match perfect.
size.height -= 4;
//it looks like there is 2 pixels around the edge
size.width -= 4;
//Using page mode we need to take off an additional 14 pixels for the page dots at the bottom
size.height -= 14;
return size;
}

Detecting how bright an image is

For my app I use a user selected image as the background with the text above it blended in using kCGBlendingModeOverlay. It's fine on certain images, but the text isn't legible on a bright image. I know Apple use an algorithm in iOS 7 to change text colour based on the content below, but my question is how I would go about implementing it. I've searched around but haven't found anything relating to this so far. Does anyone have an idea about where I could start?
Thanks
We use this grayscale calculation that weighs the colors the same way the human eye does, basically.
The eye has different sensitivity for the different colors, so N photons of green will appear brighter than N photons of blue.
lColorIndex := ( (r * 77 + g * 151 + b * 28) shr 8 );
if (lColorIndex < 130) then
ForegroundColor := clWhite
else
ForegroundColor := clBlack;
This is only a pixel calculation, so you'd have to average over the area you're looking at (average R,G,B before the calculation of course)
Background:
The brightness we see is more or less:
v = 59% of the green, 30% of the red and 11% of the blue channel
= (30 * r + 59 * g + 11 * b) div 100
which comes close to:
v = (77 * r + 151 * g + 28 * b) div 256
v = (77 * r + 151 * g + 28 * b) shr 8
which calculates faster in the computer world.

How to discover the area chart data if we only have the image?

The area chart (image) has a few data series, which are charted with different colors. We know the image size and co-ordinates of each lable on x-Axis, is it possible to discover the series of y-Axis by image recongition? Can anybody shed some light?
If you know the y-axis scale, it should be possible.
To screenscrape, you could first filter your image with a color filter for each of the series.
Second step would be to gather the coordinates of all remaining pixels in your temporary image and transform them these to the scale needed.
given
a pixel at coordinates x,y
the offset of the charts Origin in image pixels xoffset, yoffset
the Scale of you chart axis xscale, yscale
you could calculate the data for this pixel (pseudocode)
pixelData.x := (x - xoffset) * xscale
pixeldata.y := (y - yoffset) * yscale
And afterwards, do some interpolation if your series line is more then one pixel wide (for example get the average data for all pixels in a single column or so).
Update1: Pseudocode for naive color filter filtering out red charts
//set up desired color levels to filter out
redmin := 240;
redmax := 255
bluemin := 0;
bluemax := 0;
greenmin := 0
greenmax := 0;
//load source bitmap
myBitmap := LoadBitmap("Chartfile.bmp");
//loop over bitmap pixels
for iX := 0 to myBitmap.width-1 do
for iY := 0 myBitmap.height-1 do
begin
myColorVal := myBitmap.GetPixels(iX, iY);
//if the pixel color is inside your target color range, store it
if ((mycolorVal.r >=redmin) and (myColorVal.r <= redmax)) and
((mycolorVal.g >=greenmin) and (myColorVal.g <= greenmax)) and
((mycolorVal.b >=bluemin) and (myColorVal.b <= bluemax)) then
storeDataValue(iX, iY); //performs the value scaling operation mentioned above
end;

How do I specify font height at different orientations?

The common way to create a font with GDI is to use the desired point size and the target device's vertical resolution (DPI) like this:
LOGFONT lf = {0};
lf.lfHeight = -MulDiv(point_size, GetDeviceCaps(hdc, LOGPIXELSY), 72);
...
HFONT hfont = CreateFontIndirect(&lf);
Assuming the default MM_TEXT mapping mode, this converts point_size into the pixel height for the desired device. (This is a common approximation. There are actually 72.27 points in an inch, not 72.) (The minus sign means I want to specify the actual character height, not the cell height.)
If I want to create a sideways font--that is, one with an orientation and escapement of 90 degrees--do I use LOGPIXELSX rather than LOGPIXELSY? For some of the printers I'm targeting, the horizontal and vertical resolutions are different.
Generally, if I want an angle of theta, do I combine LOGPIXELSX and LOGPIXELSY? I'm thinking of something like this:
// Given theta in degrees (e.g., theta = 45.0) ...
double theta_radians = theta * 2.0 * pi / 360.0;
int dpi = static_cast<int>(GetDeviceCaps(hdc, LOGPIXELSX) * sin(theta_radians) +
GetDeviceCaps(hdc, LOGPIXELSY) * cos(theta_radians) +
0.5);
LOGFONT lf = {0};
lf.lfHeight = -MulDiv(point_size, dpi, 72);
// Set escapement and orientation to theta in tenths of a degree.
lf.lfEscapement = lf.lfOrientation = static_cast<LONG>(theta * 10.0 + 0.5);
...
This makes intuitive sense to me, but I'm wondering if this is really how the GDI font mapper and printer drivers work.
1) There are 72 points/inch. (it used to be 72.27 but was changed.)
2) Combining LOGPIXELSX and LOGPIXELSY in the way that you do is fine, but
3) The font mapper doesn't look at escapement and orientation when mapping fonts. The LOGPIXELS values will only be used as part of the coordinate transformation.
http://msdn.microsoft.com/en-us/library/ms969909(loband).aspx
Not sure about how the "printer drivers work" because the statement could include many possible drivers and printers.
They could rasterize with square pixels, then stretch to non-square. They could transform glyph curves. They could do something else.

How to efficiently rotate bitmaps in code

Is there a faster way to rotate a large bitmap by 90 or 270 degrees than simply doing a nested loop with inverted coordinates?
The bitmaps are 8bpp and typically 2048x2400x8bpp
Currently I do this by simply copying with argument inversion, roughly (pseudo code:
for x = 0 to 2048-1
for y = 0 to 2048-1
dest[x][y]=src[y][x];
(In reality I do it with pointers, for a bit more speed, but that is roughly the same magnitude)
GDI is quite slow with large images, and GPU load/store times for textures (GF7 cards) are in the same magnitude as the current CPU time.
Any tips, pointers? An in-place algorithm would even be better, but speed is more important than being in-place.
Target is Delphi, but it is more an algorithmic question. SSE(2) vectorization no problem, it is a big enough problem for me to code it in assembler
Follow up to Nils' answer
Image 2048x2700 -> 2700x2048
Compiler Turbo Explorer 2006 with optimization on.
Windows: Power scheme set to "Always on". (important!!!!)
Machine: Core2 6600 (2.4 GHz)
time with old routine: 32ms (step 1)
time with stepsize 8 : 12ms
time with stepsize 16 : 10ms
time with stepsize 32+ : 9ms
Meanwhile I also tested on a Athlon 64 X2 (5200+ iirc), and the speed up there was slightly more than a factor four (80 to 19 ms).
The speed up is well worth it, thanks. Maybe that during the summer months I'll torture myself with a SSE(2) version. However I already thought about how to tackle that, and I think I'll run out of SSE2 registers for an straight implementation:
for n:=0 to 7 do
begin
load r0, <source+n*rowsize>
shift byte from r0 into r1
shift byte from r0 into r2
..
shift byte from r0 into r8
end;
store r1, <target>
store r2, <target+1*<rowsize>
..
store r8, <target+7*<rowsize>
So 8x8 needs 9 registers, but 32-bits SSE only has 8. Anyway that is something for the summer months :-)
Note that the pointer thing is something that I do out of instinct, but it could be there is actually something to it, if your dimensions are not hardcoded, the compiler can't turn the mul into a shift. While muls an sich are cheap nowadays, they also generate more register pressure afaik.
The code (validated by subtracting result from the "naieve" rotate1 implementation):
const stepsize = 32;
procedure rotatealign(Source: tbw8image; Target:tbw8image);
var stepsx,stepsy,restx,resty : Integer;
RowPitchSource, RowPitchTarget : Integer;
pSource, pTarget,ps1,ps2 : pchar;
x,y,i,j: integer;
rpstep : integer;
begin
RowPitchSource := source.RowPitch; // bytes to jump to next line. Can be negative (includes alignment)
RowPitchTarget := target.RowPitch; rpstep:=RowPitchTarget*stepsize;
stepsx:=source.ImageWidth div stepsize;
stepsy:=source.ImageHeight div stepsize;
// check if mod 16=0 here for both dimensions, if so -> SSE2.
for y := 0 to stepsy - 1 do
begin
psource:=source.GetImagePointer(0,y*stepsize); // gets pointer to pixel x,y
ptarget:=Target.GetImagePointer(target.imagewidth-(y+1)*stepsize,0);
for x := 0 to stepsx - 1 do
begin
for i := 0 to stepsize - 1 do
begin
ps1:=#psource[rowpitchsource*i]; // ( 0,i)
ps2:=#ptarget[stepsize-1-i]; // (maxx-i,0);
for j := 0 to stepsize - 1 do
begin
ps2[0]:=ps1[j];
inc(ps2,RowPitchTarget);
end;
end;
inc(psource,stepsize);
inc(ptarget,rpstep);
end;
end;
// 3 more areas to do, with dimensions
// - stepsy*stepsize * restx // right most column of restx width
// - stepsx*stepsize * resty // bottom row with resty height
// - restx*resty // bottom-right rectangle.
restx:=source.ImageWidth mod stepsize; // typically zero because width is
// typically 1024 or 2048
resty:=source.Imageheight mod stepsize;
if restx>0 then
begin
// one loop less, since we know this fits in one line of "blocks"
psource:=source.GetImagePointer(source.ImageWidth-restx,0); // gets pointer to pixel x,y
ptarget:=Target.GetImagePointer(Target.imagewidth-stepsize,Target.imageheight-restx);
for y := 0 to stepsy - 1 do
begin
for i := 0 to stepsize - 1 do
begin
ps1:=#psource[rowpitchsource*i]; // ( 0,i)
ps2:=#ptarget[stepsize-1-i]; // (maxx-i,0);
for j := 0 to restx - 1 do
begin
ps2[0]:=ps1[j];
inc(ps2,RowPitchTarget);
end;
end;
inc(psource,stepsize*RowPitchSource);
dec(ptarget,stepsize);
end;
end;
if resty>0 then
begin
// one loop less, since we know this fits in one line of "blocks"
psource:=source.GetImagePointer(0,source.ImageHeight-resty); // gets pointer to pixel x,y
ptarget:=Target.GetImagePointer(0,0);
for x := 0 to stepsx - 1 do
begin
for i := 0 to resty- 1 do
begin
ps1:=#psource[rowpitchsource*i]; // ( 0,i)
ps2:=#ptarget[resty-1-i]; // (maxx-i,0);
for j := 0 to stepsize - 1 do
begin
ps2[0]:=ps1[j];
inc(ps2,RowPitchTarget);
end;
end;
inc(psource,stepsize);
inc(ptarget,rpstep);
end;
end;
if (resty>0) and (restx>0) then
begin
// another loop less, since only one block
psource:=source.GetImagePointer(source.ImageWidth-restx,source.ImageHeight-resty); // gets pointer to pixel x,y
ptarget:=Target.GetImagePointer(0,target.ImageHeight-restx);
for i := 0 to resty- 1 do
begin
ps1:=#psource[rowpitchsource*i]; // ( 0,i)
ps2:=#ptarget[resty-1-i]; // (maxx-i,0);
for j := 0 to restx - 1 do
begin
ps2[0]:=ps1[j];
inc(ps2,RowPitchTarget);
end;
end;
end;
end;
Update 2 Generics
I tried to update this code to a generics version in Delphi XE. I failed because of QC 99703, and forum people have already confirmed it also exists in XE2. Please vote for it :-)
Update 3 Generics
Works now in XE10
Update 4
In 2017 i did some work on a assembler version for 8x8 cubes of 8bpp images only and related SO question about shuffle bottlenecks where Peter Cordes generously helped me out. This code still has a missed oportunity and still needs another looptiling level again to aggregate multiple 8x8 block iterations into pseudo larger ones like 64x64. Now it is whole lines again and that is wasteful.
Yes, there are faster ways to do this.
Your simple loop spends most of the time in cache misses. This happends because you touch a lot of data at very different places in a tight loop. Even worse: Your memory locations are exactly a power of two apart. That's a size where the cache performs worst.
You can improve this rotation algorithm if you improve the locality of your memory accesses.
A simple way to do this would be to rotate each 8x8 pixel block on it's own using the same code you've used for your whole bitmap, and wrap another loop that splits the image rotation into chunks of 8x8 pixels each.
E.g. something like this (not checked, and sorry for the C-code. My Delphi skills aren't up to date):
// this is the outer-loop that breaks your image rotation
// into chunks of 8x8 pixels each:
for (int block_x = 0; block_x < 2048; block_x+=8)
{
for (int block_y = 0; blocky_y < 2048; block_y+=8)
{
// this is the inner-loop that processes a block
// of 8x8 pixels.
for (int x= 0; x<8; x++)
for (int y=0; y<8; y++)
dest[x+block_x][y+block_y] = src[y+block_y][x+block_x]
}
}
There are other ways as well. You could process the data in Hilbert-Order or Morton-Order. That would be in theory even a bit faster, but the code will be much more complex.
Btw - Since you've mentioned that SSE is an option for you. Note that you can rotate a 8x8 byte block within the SSE-registers. It's a bit tricky to get it working, but looking at SSE matrix transpose code should get you started as it's the same thing.
EDIT:
Just checked:
With a block-size of 8x8 pixels the code runs ca. 5 times faster on my machine. With a block-size of 16x16 it runs 10 times faster.
Seems like it's a good idea to experiment with different block-sizes.
Here is the (very simple) test-program I've used:
#include <stdio.h>
#include <windows.h>
char temp1[2048*2048];
char temp2[2048*2048];
void rotate1 (void)
{
int x,y;
for (y=0; y<2048; y++)
for (x=0; x<2048; x++)
temp2[2048*y+x] = temp1[2048*x+y];
}
void rotate2 (void)
{
int x,y;
int bx, by;
for (by=0; by<2048; by+=8)
for (bx=0; bx<2048; bx+=8)
for (y=0; y<8; y++)
for (x=0; x<8; x++)
temp2[2048*(y+by)+x+bx] = temp1[2048*(x+bx)+y+by];
}
void rotate3 (void)
{
int x,y;
int bx, by;
for (by=0; by<2048; by+=16)
for (bx=0; bx<2048; bx+=16)
for (y=0; y<16; y++)
for (x=0; x<16; x++)
temp2[2048*(y+by)+x+bx] = temp1[2048*(x+bx)+y+by];
}
int main (int argc, char **args)
{
int i, t1;
t1 = GetTickCount();
for (i=0; i<20; i++) rotate1();
printf ("%d\n", GetTickCount()-t1);
t1 = GetTickCount();
for (i=0; i<20; i++) rotate2();
printf ("%d\n", GetTickCount()-t1);
t1 = GetTickCount();
for (i=0; i<20; i++) rotate3();
printf ("%d\n", GetTickCount()-t1);
}
If you can use C++ then you may want to look at Eigen.
It is a C++ template library that uses SSE (2 and later) and AltiVec instruction sets with graceful fallback to non-vectorized code.
Fast. (See benchmark).
Expression templates allow to intelligently remove temporaries and enable lazy evaluation, when that is appropriate -- Eigen takes care of this automatically and handles aliasing too in most cases.
Explicit vectorization is performed for the SSE (2 and later) and AltiVec instruction sets, with graceful fallback to non-vectorized code. Expression templates allow to perform these optimizations globally for whole expressions.
With fixed-size objects, dynamic memory allocation is avoided, and the loops are unrolled when that makes sense.
For large matrices, special attention is paid to cache-friendliness.
You might be able to improve it by copying in cache-aligned blocks rather than by rows, as at the moment the stride of either src dest will be a miss ( depending whether delphi is row major or column major ).
If the image isn't square, you can't do in-place. Even if you work in square images, the transform isn't conducive to in-place work.
If you want to try to do things a little faster, you can try to take advantage of the row strides to make it work, but I think the best you would do is to read 4 bytes at a time in a long from the source and then write it into four consecutive rows in the dest. That should cut some of your overhead, but I wouldn't expect more than a 5% improvement.

Resources