Image Manipulation

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
NorbyDroid
Posts: 70
Joined: May 21, 2016 22:55

Image Manipulation

Post by NorbyDroid »

Here are some functions I put together for a project and I thought I would share it for others to use. Not sure how useful they are, and they could be improved. I hope someone finds these helpful. Not sure if there are anything missing, but if so let me know. Enjoy.

Code: Select all

#Include "fbgfx.bi"

' All Functions were created by me, except 'GetColor'.

' This Function returns the Red, Green, and Blue values in a 4 char string
' "RGBA" Red Green Blue Alpha  
Declare Function GetColor(ByVal ipColor As ULong) As String

' Checks if the image InImage is valid.
Declare Function GoodImage(InImage As FB.Image Ptr) As Long

' Each of these Functions returns -1 if there is a problem.

' Clears an image to the Transparent Color. RGB(255,0,155)
Declare Function ImageCleared(InImage As FB.Image Ptr) As Integer

' Fills an image to the InColor.
Declare Function ImageFilled(InImage As FB.Image Ptr,InColor As ULong) As Integer

' Inverts the image colors.
Declare Function ImageInverted(InImage As FB.Image Ptr,InFlag As Integer=0) As Integer

' Makes a copy of InImage using InFlag method:
' PSET, PRESET, TRANS, AND, OR, XOR, ALPHA, ADD
Declare Function ImageMirrored(InImage As FB.Image Ptr,InFlag As String) As Integer

' Flips the image either 'H' Horizontal, or 'V' Vertical according to InFlag.
Declare Function ImageFlipped(InImage As FB.Image Ptr,InFlag As String) As Integer

' Shifts the image 'Up', 'Down', 'Left', or 'Right' according to InFlag.
' Shortcuts 'U', 'D', 'L', and 'R' may also be used.

' Shifts is the number of Shifts to make.
'   Up/ Down: 1 to ImageHeight-1
' Left/Right: 1 to ImageWidth-1
Declare Function ImageShifted(InImage As FB.Image Ptr,InFlag As String,Shifts As Integer) As Integer

' Rotate images either Clockwise, or Counter-Clockwise using InFlag:
' Options: 'R', 'RIGHT', 'CLOCKWISE', 'L', 'LEFT', 'COUNTERCLOCKWISE'

' Rotates: (1-3): 90, 180, or 270 degrees
Declare Function ImageRotated(InImage As FB.Image Ptr,InFlag As String,Rotates As Integer) As Integer

Type NewImage
  xPos As Integer
  yPos As Integer

	image As FB.Image Ptr 

	iWidth As Integer
	iHeight As Integer

  bypp As Integer
  pitch As Integer
  pixdata As Any Ptr
  size As Long
End Type

Dim Shared Smiles As NewImage
Dim Shared Smiley As NewImage

Type SetRGB
  iRed As Integer
  iGreen As Integer
  iBlue As Integer
  
  iAlpha As Integer
End Type

Dim Shared iRGB As SetRGB

ScreenRes 640,480,32

' Create a Smile Face for Demonstrations
Dim As FB.Image Ptr SmileFace=ImageCreate(32,32,RGB(255,0,255))

Circle SmileFace,(16,16),15,RGB(255,255,0),,,1,f
Circle SmileFace,(10,10), 3,RGB(0,0,0),,,2,f
Circle SmileFace,(23,10), 3,RGB(0,0,0),,,2,f
Circle SmileFace,(16,18),10,RGB(0,0,0),3.14,6.28

Draw String(0,0),"Original"
Put(4,8),SmileFace,Trans

Draw String(120,0),"Cleared"
If ImageCleared(SmileFace)=0 Then Put(124,8),Smiley.image,Trans
If ImageCleared(SmileFace)=0 Then Put(164,8),Smiley.image,PSet

Draw String(240,0),"Gold Filled"
If ImageFilled(SmileFace,RGB(255,215,0))=0 Then Put(244,8),Smiley.image,Trans

Draw String(360,0),"Inverted"
If ImageInverted(SmileFace)=0 Then Put(364,8),Smiley.image,Trans
If ImageInverted(SmileFace,1)=0 Then Put(404,8),Smiley.image,Trans

Draw String(480,0),"Mirrored"
If ImageMirrored(SmileFace,"PSET")=0 Then Put(484,8),Smiley.image,Trans
If ImageMirrored(SmileFace,"PRESET")=0 Then Put(524,8),Smiley.image,Trans
If ImageMirrored(SmileFace,"TRANS")=0 Then Put(564,8),Smiley.image,Trans

Draw String(0,80),"Horizontal Flipped"
If ImageFlipped(SmileFace,"H")=0 Then Put(4,88),Smiley.image,Trans

Draw String(240,80),"Vertical Flipped"
If ImageFlipped(SmileFace,"V")=0 Then Put(244,88),Smiley.image,Trans

Draw String(0,160),"Shifted Up"
If ImageShifted(SmileFace,"UP",8)=0 Then Put(4,168),Smiley.image,Trans

Draw String(120,160),"Shifted Down"
If ImageShifted(SmileFace,"DOWN",16)=0 Then Put(124,168),Smiley.image,Trans

Draw String(240,160),"Shifted Left"
If ImageShifted(SmileFace,"LEFT",8)=0 Then Put(244,168),Smiley.image,Trans

Draw String(360,160),"Shifted Right"
If ImageShifted(SmileFace,"RIGHT",16)=0 Then Put(364,168),Smiley.image,Trans

Draw String(0,240),"Rotate Right"
If ImageRotated(SmileFace,"RIGHT",1)=0 Then Put(4,248),Smiley.image,Trans
If ImageRotated(SmileFace,"RIGHT",2)=0 Then Put(44,248),Smiley.image,Trans
If ImageRotated(SmileFace,"RIGHT",3)=0 Then Put(84,248),Smiley.image,Trans

Draw String(240,240),"Rotate Left"
If ImageRotated(SmileFace,"LEFT",1)=0 Then Put(244,248),Smiley.image,Trans
If ImageRotated(SmileFace,"LEFT",2)=0 Then Put(284,248),Smiley.image,Trans
If ImageRotated(SmileFace,"LEFT",3)=0 Then Put(324,248),Smiley.image,Trans

While Asc(Inkey())<>27
	Sleep 10
Wend

ImageDestroy Smiley.image

' Convert pixel color to Red, Green, and Blue values
Function GetColor(ByVal ipColor As ULong) As String
Dim As UByte Ptr clrP=Cast(UByte Ptr, @ipColor)

iRGB.iRed=clrP[2]
iRGB.iGreen=clrP[1]
iRGB.iBlue=clrP[0]
iRGB.iAlpha=clrP[3]

GetColor=Chr(iRGB.iRed)+Chr(iRGB.iGreen)+Chr(iRGB.iBlue)
End Function

' Get Image Information
Function GoodImage(InImage As FB.Image Ptr) As Long
GoodImage=ImageInfo(InImage,Smiley.iWidth,Smiley.iHeight,_
                    Smiley.bypp,Smiley.pitch,Smiley.pixdata,Smiley.size)
End Function

' Clears image to transparent: RGB(255,0,255)
Function ImageCleared(InImage As FB.Image Ptr) As Integer
If GoodImage(InImage)=0 Then
  Smiley.image=ImageCreate(Smiley.iWidth,Smiley.iHeight)
  Line Smiley.image,(0,0)-(Smiley.iWidth-1,Smiley.iHeight-1),RGB(255,0,255),BF
Else
	ImageCleared=-1
End If
End Function

' Fills image with InColor.
Function ImageFilled(InImage As FB.Image Ptr,InColor As ULong) As Integer
If GoodImage(InImage)=0 Then
  Smiley.image=ImageCreate(Smiley.iWidth,Smiley.iHeight)
  Line Smiley.image,(0,0)-(Smiley.iWidth-1,Smiley.iHeight-1),InColor,BF
Else
  ImageFilled=-1
End If
End Function

' Inverts the image colors.
Function ImageInverted(InImage As FB.Image Ptr,InFlag As Integer=0) As Integer
Dim As String tColor
Dim As Integer tRed,tGreen,tBlue

If GoodImage(InImage)=0 Then
  Smiley.image=ImageCreate(Smiley.iWidth,Smiley.iHeight)
  
  For yPos As Integer=0 To Smiley.iHeight-1
    For xPos As Integer=0 To Smiley.iWidth-1
      ' Get the R G B values
      tColor=GetColor(Point(xPos,yPos,InImage))

      ' Invert Red, Green, and Blue values
      tRed=255-Asc(Left(tColor,1))
      tGreen=255-Asc(Mid(tColor,2,1))
      tBlue=255-Asc(Mid(tColor,3,1))

      ' Don't invert transparency if InFlag=0
      If InFlag=0 And tColor=Chr(255)+Chr(0)+Chr(255) Then
      	tRed=255:tGreen=0:tBlue=255
      EndIf

      PSet Smiley.image,(xPos,yPos),RGB(tRed,tGreen,tBlue)
    Next 
  Next
Else
  ImageInverted=-1
End If
End Function

' Makes a copy of the InpuSmiley using InFlag method:
' PSET, PRESET, TRANS, AND OR XOR, ALPHA, ADD
Function ImageMirrored(InImage As FB.Image Ptr,InFlag As String) As Integer
If GoodImage(InImage)=0 Then
  Smiley.image=ImageCreate(Smiley.iWidth,Smiley.iHeight)

  Select Case InFlag
  	Case "PSET"
      Put Smiley.image,(0,0),InImage,(0,0)-(Smiley.iWidth,Smiley.iHeight),PSet

  	Case "PRESET"
      Put Smiley.image,(0,0),InImage,(0,0)-(Smiley.iWidth,Smiley.iHeight),PReset

  	Case "TRANS"
      Put Smiley.image,(0,0),InImage,(0,0)-(Smiley.iWidth,Smiley.iHeight),Trans

  	Case "AND"
      Put Smiley.image,(0,0),InImage,(0,0)-(Smiley.iWidth,Smiley.iHeight),And

  	Case "OR"
      Put Smiley.image,(0,0),InImage,(0,0)-(Smiley.iWidth,Smiley.iHeight),Or

  	Case "XOR"
      Put Smiley.image,(0,0),InImage,(0,0)-(Smiley.iWidth,Smiley.iHeight),Xor

  	Case "ALPHA"
      Put Smiley.image,(0,0),InImage,(0,0)-(Smiley.iWidth,Smiley.iHeight),Alpha

  	Case "ADD"
      Put Smiley.image,(0,0),InImage,(0,0)-(Smiley.iWidth,Smiley.iHeight),Add
  End Select
Else
  ImageMirrored=-1
End If
End Function

' Flips the image either 'H' Horizontal, or 'V' Vertical according to InFlag.
Function ImageFlipped(InImage As FB.Image Ptr,InFlag As String) As Integer
Dim As Integer iWidth,iHeight

If GoodImage(InImage)=0 Then
  iWidth=Smiley.iWidth-1
  iHeight=Smiley.iHeight-1
  Smiley.image=ImageCreate(iWidth+1,iHeight+1)
  
  Select Case InFlag
  	Case "H"
      For yPos As Integer=0 To iHeight
        For xPos As Integer=0 To iWidth
        	PSet Smiley.image,(xPos,yPos),Point(iWidth-xPos,yPos,InImage)
        Next
      Next

    Case "V"
      For yPos As Integer=0 To iHeight
        For xPos As Integer=0 To iWidth
        	PSet Smiley.image,(xPos,yPos),Point(xPos,iHeight-yPos,InImage)
        Next
      Next
  End Select
Else
  ImageFlipped=-1
End If
End Function

' Shifts the image 'Up', 'Down', 'Left', or 'Right' according to InFlag.
' Shortcuts 'U', 'D', 'L', and 'R' may also be used.
' Shifts is the number of Shifts to make
Function ImageShifted(InImage As FB.Image Ptr,InFlag As String,Shifts As Integer) As Integer
Dim As Integer iStart,iWidth,iHeight,tX,tY

If GoodImage(InImage)=0 Then
  iWidth=Smiley.iWidth-1
  iHeight=Smiley.iHeight-1
  Smiley.image=ImageCreate(iWidth+1,iHeight+1)

  If Shifts<1 Then Shifts=1
  If Shifts>iWidth Then Shifts=iWidth 

  Select Case InFlag
  	Case "U", "UP"
      ' Starting position
      iStart=(iHeight+1)-Shifts

      For yPos As Integer=0 To iHeight
        For xPos As Integer=0 To iWidth
          ' Use Mod to keep the Y position in the correct area 
        	tY=(iStart+yPos) Mod (iHeight+1)
          PSet Smiley.image,(xPos,tY),Point(xPos,yPos,InImage)
        Next
      Next

  	Case "D", "DOWN"
      ' Starting position
      iStart=(iHeight+1)+Shifts

      For yPos As Integer=0 To iHeight
        For xPos As Integer=0 To iWidth
          ' Use Mod to keep the Y position in the correct area 
        	tY=(iStart+yPos) Mod (iHeight+1)
          PSet Smiley.image,(xPos,tY),Point(xPos,yPos,InImage)
        Next
      Next

  	Case "L", "LEFT"
      ' Starting position
      iStart=(iWidth+1)-Shifts

      For yPos As Integer=0 To iHeight
        For xPos As Integer=0 To iWidth
          ' Use Mod to keep the X position in the correct area 
        	tX=(iStart+xPos) Mod (iWidth+1)
          PSet Smiley.image,(tX,yPos),Point(xPos,yPos,InImage)
        Next
      Next

  	Case "R", "RIGHT"
      ' Starting position
      iStart=(iWidth+1)+Shifts

      For yPos As Integer=0 To iHeight
        For xPos As Integer=0 To iWidth
          ' Use Mod to keep the X position in the correct area 
        	tX=(iStart+xPos) Mod (iWidth+1)
          PSet Smiley.image,(tX,yPos),Point(xPos,yPos,InImage)
        Next
      Next
  End Select
Else
  ImageShifted=-1
End If
End Function

' Rotate images either 'R' Clockwise, or 'L' Counter-Clockwise using InFlag
' Rotates (1-3): 90, 180, or 270 degrees
Function ImageRotated(InImage As FB.Image Ptr,InFlag As String,Rotates As Integer) As Integer
Dim As Integer iWidth,iHeight,tX,tY

If GoodImage(InImage)=0 Then
  iWidth=Smiley.iWidth-1
  iHeight=Smiley.iHeight-1

  ' Rotation only valid for square images
  If iWidth<>iHeight Then
  	ImageRotated=-1
  	Exit Function
  EndIf

  Smiley.image=ImageCreate(iWidth+1,iHeight+1)

  Select Case InFlag
  	Case "R", "RIGHT", "CLOCKWISE"
      For yPos As Integer=0 To iHeight
      	For xPos As Integer=0 To iWidth
          Select Case As Const Rotates
          	Case 1:PSet Smiley.image,(iWidth-yPos,xPos),Point(xPos,yPos,InImage)
          	Case 2:PSet Smiley.image,(iWidth-xPos,iHeight-yPos),Point(xPos,yPos,InImage)
          	Case 3:PSet Smiley.image,(yPos,iWidth-xPos),Point(xPos,yPos,InImage)
          End Select
      	Next
      Next

  	Case "L", "LEFT", "COUNTERCLOCKWISE"
      For yPos As Integer=0 To iHeight
      	For xPos As Integer=0 To iWidth
          Select Case As Const Rotates
          	Case 1:PSet Smiley.image,(yPos,iWidth-xPos),Point(xPos,yPos,InImage)
          	Case 2:PSet Smiley.image,(iWidth-xPos,iHeight-yPos),Point(xPos,yPos,InImage)
          	Case 3:PSet Smiley.image,(iWidth-yPos,xPos),Point(xPos,yPos,InImage)
          End Select
      	Next
      Next

  End Select
Else
  ImageRotated=-1
End If
End Function
I am not much of a commentor on code, but I did add comments to give you an idea how everything works. Please comment on this and let me know your thoughts.
grindstone
Posts: 862
Joined: May 05, 2015 5:35
Location: Germany

Re: Image Manipulation

Post by grindstone »

Looks to me like a good idea, from a first glance I would say two things:

Firstly you should use properties to get the image infos

Code: Select all

Type NewImage
	xPos As Integer
	yPos As Integer

	image As FB.Image Ptr
	res As Long

	Declare Property iWidth As Integer
	Declare Property iHeight As Integer

	Declare Property bypp As Integer
	Declare Property pitch As Integer
	Declare Property pixdata As Any Ptr
	Declare Property size As Long
	
	Private:
	i As Integer
	l As Long
	p As Any Ptr
End Type

Property NewImage.iWidth As Integer
	ImageInfo (this.image, this.i)
	Return this.i
End Property
Property NewImage.iHeight As Integer
	ImageInfo (this.image,, this.i)
	Return this.i
End Property
Property NewImage.bypp As Integer
	ImageInfo (this.image,,, this.i)
	Return this.i
End Property
Property NewImage.pitch As Integer
	ImageInfo (this.image,,,, this.i)
	Return this.i
End Property
Property NewImage.pixdata As Any Ptr
	ImageInfo (this.image,,,,, this.p)
	Return this.p
End Property
Property NewImage.size As Long
	ImageInfo (this.image,,,,,, this.l)
	Return this.l
End Property
that's less error-prone.

Secondly you should avoid shared variables.

Tomorrow I'll have a closer look to it.
marcov
Posts: 3462
Joined: Jun 16, 2005 9:45
Location: Netherlands
Contact:

Re: Image Manipulation

Post by marcov »

I've studied Image rotation by -90,90 somewhat.

First: rotating a jpg is often possible without actual decompression which can be very fast. IOW if you e.g. load a jpg, rotate it and then send it to the user in a webservice, read up on the jpg format.

Second, when rotating a bitmap (8bit in my case), a bit of looptiling can make things a lot faster: (in pascal, but more an algorithmic post)
https://stackoverflow.com/questions/848 ... ps-in-code


Third, if you want to go all out, go SSE2:
https://stackoverflow.com/questions/474 ... ivy-bridge
sourcecode: http://www.stack.nl/~marcov/rot8x8.txt

Note the assembler version is particularly faster for images that roughly fit in the cache.
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: Image Manipulation

Post by BasicCoder2 »

I gave the eyes different colors so you could see the horizontal flip.

Code: Select all

Circle SmileFace,(10,10), 3,RGB(0,0,200),,,2,f
Circle SmileFace,(23,10), 3,RGB(200,0,0),,,2,f
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Image Manipulation

Post by jj2007 »

marcov wrote: sourcecode: http://www.stack.nl/~marcov/rot8x8.txt
You know your stuff, compliments!
thrive4
Posts: 72
Joined: Jun 25, 2021 15:32

Re: Image Manipulation

Post by thrive4 »

I would like to thank NorbyDroid for sharing this code.
I have taken the liberty to trim it a bit specifically for
rotating a image.

- streamlined the rotation parameters
- dropped the width = height check
- example with ttf can be found here:
viewtopic.php?t=31963

Code: Select all

#Include "fbgfx.bi"

' based on code by NorbyDroid
' https://www.freebasic.net/forum/viewtopic.php?t=29100
' tweaked by thrive4 2022 rotation only

Type tmpimage
    image   As FB.Image Ptr
    xpos    As Integer
    ypos    As Integer
    iwidth  As Integer
    iheight As Integer
    bypp    As Integer
    pitch   As Integer
    pixdata As Any Ptr
    size    As Long
End Type
Dim Shared tmpimg As tmpimage

ScreenRes 640,480,32

' image information
Function checkimg(img As FB.Image Ptr) As Long
    checkimg = ImageInfo(img,tmpimg.iwidth,tmpimg.iheight,_
                         tmpimg.bypp,tmpimg.pitch,tmpimg.pixdata,tmpimg.size)
End Function

' rotates image 90, 180, or 270 degrees
Function imagerotate(img As FB.Image Ptr, rotation As Integer) As Integer
    Dim As Integer iwidth, iheight

    If checkimg(img) = 0 Then
        iwidth =  tmpimg.iheight - 1
        iheight = tmpimg.iwidth
        if rotation = 180 then
            iwidth =  tmpimg.iwidth - 1
            iheight = tmpimg.iheight
        end if

        tmpimg.image = ImageCreate(iwidth + 1,  iheight)

        For yPos As Integer = 0 To iheight + iwidth
            For xpos As Integer = 0 To iwidth + 1
              Select Case As Const rotation
                Case -90, 270:PSet tmpimg.image,(iwidth - ypos, xpos), Point(xpos, ypos, img)
                Case 180     :PSet tmpimg.image,(iwidth - xpos, iheight - ypos), Point(xpos, ypos -1, img)
                Case -270, 90:PSet tmpimg.image,(ypos, iheight - xpos + 1), Point(xpos - 1, ypos, img)
              End Select
            Next
        Next
    Else
        return -1
    End If

End Function

' main
' Create a Smile Face for Demonstrations
Dim As FB.Image Ptr SmileFace = ImageCreate(32,32,RGB(255,0,255))
Circle SmileFace,(16,16),15,RGB(255,255,0),,,1,f
Circle SmileFace,(10,10), 3,RGB(0,0,0),,,2,f
Circle SmileFace,(23,10), 3,RGB(0,0,0),,,2,f
Circle SmileFace,(16,19),10,RGB(0,0,0),3.14,6.28

Draw String(154, 0), "Original"
Put(164,18), SmileFace, trans

Draw String(0, 100), "Rotate 90/180/270"
If imagerotate(SmileFace, 90) =0 Then Put(4, 118), tmpimg.image, Trans
If imagerotate(SmileFace, 180)=0 Then Put(44,118), tmpimg.image, Trans
If imagerotate(SmileFace, 270)=0 Then Put(84,118), tmpimg.image, Trans

Draw String(240, 100),"Rotate Left -90/180/-270"
If imagerotate(SmileFace, -90) =0 Then Put(244,118), tmpimg.image, Trans
If imagerotate(SmileFace, 180) =0 Then Put(284,118), tmpimg.image, Trans
If imagerotate(SmileFace, -270)=0 Then Put(324,118), tmpimg.image, Trans

While Asc(Inkey())<>27
	Sleep 30
Wend

' cleanup
ImageDestroy tmpimg.image

end

Post Reply