script-fu - paste a layer in an alpha mask - gimp

I'm looking for a procedure to make the same thing I usually do manually in Gimp:
Copy a layer
Select alpha mask of another layer
Paste
I've already made great researches in the Procedure Browser but I couldn't find anything.

Finally found the Procedures:
(gimp-edit-copy layer)
(define floating-sel (car (gimp-edit-paste mask TRUE)))
(gimp-floating-sel-anchor floating-sel)

Related

GIMP how to select from merged path

I have the path, merge from 8 subpath (because it require symmetry, I create 1/8 then merge them together)
I wanna select the picture inside the path, that is... impossible. Are there any ways to:
re-order the points inside the path
or
manual set the points position (x and y coordinate)
or
unclose any subpath (I hate this, hate to do drawing the paths over again)
or
anyway it select inside the path smarter?
thank you all.
The best you can do is splice the strokes so that you have a truly closed stroke from which you get a selection.
The ofn-path-edits plugin has a Join strokes function to connect together strokes whose end points are close enough.
But you'll have to redo part of your work: use the Join strokes function to create the closed shapes, then replicate+shift these closed shapes. And yes, this means that between the shapes, there are overlapping strokes, one for the shape on the left/top and one for the shape on the right/bottom.
At the same place there is also a path-mirror script that can create the symmetry of a stroke, and connect the ends that are on the symmetry axis.

Why did CopyRect Flipped the second image in delphi 10.3?

I want to take a screenshot of my page and put the result into a bitmap, Because there is a scrollbar on the page, i have to take several screenshots, and i want to merge those bitmaps.
if have used this code to make a screenshot and save it: Take a screenshot of a particular area in Delphi 7
i used the code to merge them from this page http://www.delphigroups.info/2/8/309463.html
if i copied it directly it would result in the first image being used, and i white rectangle for the second. so i tried to change it a little bit, and now i'm getting both images in one file.
This is the code i use to concatenate the bitmaps:
function ConcatenateBitmaps(const MainBitmap: TBitmap; const BitmapToAdd:
TBitmap): TBitmap;
begin
Result := MainBitmap;
If BitmapToAdd.Width > MainBitmap.Width then
Result.Width := BitmapToAdd.Width;
Result.Height := MainBitmap.Height + MainBitmap.Height;
Result.Canvas.CopyRect(
Rect(0,MainBitmap.Height,BitmapToAdd.Width,BitmapToAdd.Height),
BitmapToAdd.Canvas,
Rect(0,0,BitmapToAdd.Width,BitmapToAdd.Height)
);
end;
The problem is that te second image is being flipped, vertical and horizontal;
What am i doing wrong here?
EDIT:
An example of the result, the first image is good, the second image is flipped:
as i see now, my description was wrong, it's horizontaly mirrored, and verticaly flipped
Cause and quickfix:
The problem is in this part:
Rect(0,MainBitmap.Height,BitmapToAdd.Width,BitmapToAdd.Height)
You make a rectangle of which the top is the total height of the resulting image, and the bottom is the height of the bitmap to add. So this rectangle is basically inverted (its bottom is above its top).
And it's likely deformed as well, since the height of this rectangle is not the height of the bitmap to add.
The quickfix would be:
Rect(0,Result.Height- BitmapToAdd.Height,BitmapToAdd.Width,Result.Height)
Other issues and confusion:
But I think the cause of your confusion is because you think that Result and MainBitmap are two different bitmaps, while actually they are both references to the same bitmap. The assignment you do in the beginning just copies the reference, not the actual TBitmap object.
In addition, you mix up 'height' and 'bottom'. TRect expects you to set top and bottom coordinates, not top and height. This, together with the previous issue, causes not only that the bitmap is upside down, but also that it will be stretched, and partially covering the previous images. The more images you add, the more clear that effect will be.
Personally I think it's way more efficient to modify the existing bitmap in this scenario, mainly because you would otherwise have to clean up your old bitmap all the time, plus that you have a function that magically creates bitmaps. You get the question of ownership of the bitmap objects, and with that, the risk of memory leaks, which is not good, especially when dealing with large bitmaps.
My suggested version:
So, I would just make it a procedure, where the first bitmap is modified by adding the second bitmap to it.
In the version below, I also used Canvas.ClipRect, which is for a bitmap essentially the bounding rectangle of the bitmap. And then I used OffsetRect to 'move' this rectangle(increasing its top Y and bottom Y).
By doing this in a separate variable, you can have a relatively clean version compared to the quick fix I presented above, because you can use the dimensions of MainBitmap before actually modifying it.
procedure AppendBitmap(const MainBitmap: TBitmap; const BitmapToAdd:
TBitmap);
var
TargetRect: TRect;
begin
// Widen the main bitmap if needed
if BitmapToAdd.Width > MainBitmap.Width then
MainBitmap.Width := BitmapToAdd.Width;
// Set TargetRect to the right size
TargetRect := BitmapToAdd.Canvas.ClipRect;
// And then to the right position
OffsetRect(TargetRect, 0, MainBitmap.Height);
// Make room for the bitmap to add
MainBitmap.Height := MainBitmap.Height + BitmapToAdd.Height;
// Draw it in the created space
MainBitmap.Canvas.CopyRect(
TargetRect,
BitmapToAdd.Canvas,
BitmapToAdd.Canvas.ClipRect
);
end;
And if you like, you can make a wrapper function with the signature of the original, that creates a copy of the main image and returns that. Note though, that MainBitmap and the result of this function are no longer the same bitmap, and you have to make sure to properly free both of them when you're done.
function ConcatenateBitmaps(const MainBitmap: TBitmap; const BitmapToAdd:
TBitmap): TBitmap;
begin
Result := TBitmap.Create;
Result.Assign(MainBitmap);
AppendBitmap(Result, BitmapToAdd);
end;
PS: I like questions like this from which I learn something. I never realized you could flip an image by flipping the rect passed to CopyRect. :D

Gimp python-fu: How to crop layer to selection

What is the GIMP API call to crop a layer to a selection, equivalent to Layer -> Crop to Selection in the GUI?
I looked in the Procedure Browser but the calls I found (gimp-crop and gimp-image-crop) perform the crop on the image, not a layer.
(What I really want to do is cut-and-paste multiple layers at once; I'm making a plug-in to help.)
You use pdb.gimp_layer_resize() using the data from pdb.gimp_selection_bounds(image).
x0,y0 = pdb.gimp_drawable_offsets(layer)
non_empty, x1, y1, x2, y2 = pdb.gimp_selection_bounds(image)
pdb.gimp_layer_resize(layer,x2-x1,y2-y1,x0-x1,y0-y1)

GIMP: Add padding to multiple images

I scanned a couple pages and edited them a little so the look neat.
But as I wanted to reprint them I noticed that I cropped the border from these images so that the printer won't print the whole content.
I have a Brother printer which has Linux drivers but somehow the scaling option does not work.
So I thought to scale the PDF (in which I have converted these scans) but the printer driver scales them back to fit the page. (If I disable the scale-to-fit option it becomes garbage)
So I thought that I could do some script-fu to resize these images with a couple of lines and add some padding for the printer. But I have no clue how to do this.
Here's my first attempt:
(define (resize-image filename-in filename-out )
(let* ((image (car (gimp-file-load RUN-NONINTERACTIVE filename-in "")))
(drawable (car (gimp-image-active-drawable image)))
)
(gimp-image-resize image 2728 3819 124 173)
(gimp-file-save RUN-NONINTERACTIVE image drawable filename-out "")
)
)
This does not work. The image simply remains unchanged.
My page is A4 with 2480x3472, so I thought to add 10% to the width so it becomes 2728x3819 and set the offset to 5% so the content is centered (with offset values 124 and 173).
Wnen you resize the image you are just extending the canvas but not the layer. And gimp-file-save only saves the active "drawable" (layer in your case), so you just save the same image. What you have to do is either:
add a white layer at the bottom of the layer stack
flatten the image
save the result
or
remove the layer's alpha channel (assuming the background paint color is white)
extend the layer to cover the canvas (the extension will be filled with white): gimp-layer-resize-to-image-size
save the result

Delphi graphics32 saving layers as transparent PNG goes wrong

I am having a strange problem, and I do not think I can solve it.
I have an ImgView containing layers (transparent png images) and I intend to save all the layers as png files (like a "Save Project" thing), so that later on I can re-open them and place them where I left them. (like an "Open Project" thing)
This is my problem, following steps work just fine:
I add layers (transparent PNG files)
I move them around and place them where I want them
I press save project (so here I save all layers as png image files)
It works
If I do the next following steps, something goes wrong:
I add layers (transparent PNG files)
I move them around and place them where I want them
I change the location of the layers (as in: send to back one layer for example) (so this step is different)
I press save project (so here I save all layers as png image files)
It crashes with "Access violation at address 005380FB in module 'MyApp.exe'. Read of address 000000C0"
Right now it only gives me the above error, but a few runs ago, it pointed me to this line:
procedure TCustomBitmap32.ResetAlpha(const AlphaValue: Byte);
var
I: Integer;
P: PByteArray;
begin
if not FMeasuringMode then <<<<<------ this line
So if I change the index of layers... I cannot save them anymore as PNG ?!?!?!
Here is my save procedure:
for i:=0 to mainForm.ImgView.Layers.Count-2 do
begin
mylay := TBitmapLayer(mainForm.ImgView.Layers.Items[i]);
SaveBMPAsPng(mylay.Bitmap,'C:\MyApp\tmp\'+getLayerPan(i)+'.png');
end;
// where getLayerPan is a function that retrieves a name that I gave to the layer
... and
procedure SaveBmpAsPng(bmp:TBitmap32;dest:string);
var
Y: Integer;
X: Integer;
Png: TPortableNetworkGraphic32;
function IsWhite(Color32: TColor32): Boolean;
begin
Result:= (TColor32Entry(Color32).B = 255) and
(TColor32Entry(Color32).G = 255) and
(TColor32Entry(Color32).R = 255);
end;
begin
bmp.ResetAlpha;
for Y := 0 to bmp.Height-1 do
for X := 0 to bmp.Width-1 do
begin
if IsWhite(bmp.Pixel[X, Y]) then
bmp.Pixel[X,Y]:=Color32(255,255,255,0);
end;
Png:= TPortableNetworkGraphic32.Create;
Png.Assign(bmp);
Png.SaveToFile(dest);
Png.Free;
end;
What could be wrong?
Please help...
EDIT
I think I discovered my problem...
When I move the layers around, the only way (that I know of) to do it clean, is to load all layers into an imagelist (TBitmap32List was my choice at that moment) and after that clean the layers and re-add them from the imagelist to my ImageView in the desired order.
I can only assume that this is where something goes wrong.
It must be because in the layers I have transparent PNGs, and when I load them into the Bitmap32List, I load them as BMPs.
I must look for another way of reorganizing my layers before going any further. I will update you with my solution. If any of you know of a better way of reordering layers in ImageView32, please let me know.
EDIT
So, please observe in the image bellow that the GUI is done, and working. I have the panels representing the layers, I can move them around (as you may see in the image I am dragging layer 'Elementul 0' and movin it up in the chain).
And I repeat, my logic also works when I use temporary files for moving layers up or down in the order. One of the answers suggested that I should just use the Index property to change a layers position in the layers hierarchy, and I am saying that it cannot be done without at least adding new layers to the image. So this is not a double question. It is just a response to one of the answers I received.
Thank you
Your problem is a lot simpler than you might think. Working with layers comes natural:
Send to back
Set the layer's index to 0 or simply call SendToBack. All layers previously before it will have their index increased by 1. All layers previously after it remain at the same position.
Send backward
Decrease the layer's index by 1. The layer previously before it will now come after it, thus has its index increased by one.
Send forward
Increase the layer's index by 1. The layer previously after it will now come before it, thus has its index decreased by one.
Send to front
Set the layer's index to the number of layers minus 1. The layers previously after it have their increased decreased by one.
Hence there is absolutely no need to touch the bitmap, save to disk it to disk, or use any kind of temporary layers to change the order. In virtually every case, the right thing happens when you just set the index of the layer to the position (counting from 0, back to front) you want it to appear at. After moving a panel in your list, you could set the corresponding layer's index to the new index of the panel in the list. However, because the panel is ordered front to back and GR32 orders back to front, you need to translate the index of the panel to the desired index of the layer.
Here's an example how to do that with a TListBox and a TButton:
procedure TForm1.SendBackwardButtonClick(Sender: TObject);
var
LNewListBoxItemIndex: Integer;
begin
// Calculate the new list index and make sure it's valid
LNewListBoxItemIndex := Max(0, Min(ListBox1.ItemIndex + 1, ListBox1.Items.Count - 1));
// Transform the current and new list indices and use them to move the layer
ImgView321.Layers[ListBox1.Items.Count - 1 - ListBox1.ItemIndex].Index :=
ListBox1.Items.Count - 1 - LNewListBoxItemIndex;
// Move the list item
ListBox1.Items.Move(ListBox1.ItemIndex, LNewListBoxItemIndex);
// Preserve the selection (if applicable)
ListBox1.ItemIndex := LNewListBoxItemIndex;
end;
You may also decide to fully synchronize the list with the layers. In that case you should associate each item (possibly TPanel) with a layer.
// Create layers from front to back
LLayer := TBitmapLayer.Create(ImgView321.Layers);
ListBox1.Items.AddObject('First layer', LLayer);
// Could use LPanel := TPanel.Create(...); LPanel.Tag := Integer(Pointer(LLayer)) instead
LLayer := TBitmapLayer.Create(ImgView321.Layers);
ListBox1.Items.AddObject('Second layer', LLayer);
// Now the list is correct but the layers are not in the right order.
// Use the code listed below whenever you need to synchronize the layers
// with the list. In theory it may be slow (O(n^2)) but practically it
// won't matter much assuming you won't have hundreds of layers.
// Don't update the screen every time we move a layer to get closer to the final result
ImgView321.BeginUpdate;
try
for LIndex := 0 to ListBox1.Items.Count - 1 do
// Get the associated layer and make it the least visible of all processed so far
TCustomLayer(ListBox1.Items.Objects[LIndex]).SendToBack;
// Could use TCustomLayer(Pointer(SomePanel.Tag)).SendToBack instead
finally
// Always do this not to have strange behavior after an error
ImgView321.EndUpdate;
end;
// When it's done, update the screen
ImgView321.Changed;
By your description of how you changed the order of the layers, it is most likely the reason for your problem. Since you did not post that part of the code it can not be assessed with certainty.
Anyway, to rearrange the layers, you can use the Index property of TCustomLayer (of which TBitmapLayer is a descendant)
So the solution to the problem is to NOT use a Bitmap32List as a temporary container for png layers when reordering layers, because something gets lost in the process.
So try other solution for reordering. My curent solution is to drop the layers as PNG files to the disk, then reload them from the disk in the desired order.
Another solution (untested yet) would be to create a number of new layers, equal to the number of existing layers, move actual layers there, then get them back one by one in the desired order, and then remove the extra layers.
Anyway. That was the question, and this is the answer so far

Resources