iOS Question [SOLVED?] Getting RGB from a pixel

JackKirk

Well-Known Member
Licensed User
Longtime User
In this B4A post:

https://www.b4x.com/android/forum/threads/get-alpha-red-green-blue.7258/#post-41502

Erel supplies this code to extract RGB from an Android pixel:
B4X:
Sub Activity_Create(FirstTime As Boolean)
    Dim argb() As Int
    argb = GetARGB(Colors.Transparent)
    Log("A = " & argb(0))
    Log("R = " & argb(1))
    Log("G = " & argb(2))
    Log("B = " & argb(3))
End Sub

Sub GetARGB(Color As Int) As Int()
    Dim res(4) As Int
    res(0) = Bit.UnsignedShiftRight(Bit.And(Color, 0xff000000), 24)
    res(1) = Bit.UnsignedShiftRight(Bit.And(Color, 0xff0000), 16)
    res(2) = Bit.UnsignedShiftRight(Bit.And(Color, 0xff00), 8)
    res(3) = Bit.And(Color, 0xff)
    Return res
End Sub

If you run similar code in B4I it does not work correctly - I suspect some sort of endian problem - the A is at least where the B was - or at least seems to be some of the time.

What is the correct equivalent for an iOS pixel?

Thanks in anticipation...
 
Last edited:

strat

Active Member
Licensed User
Longtime User
Worked for me as exactly B4A example. I haven't detect pixel, I tried only your code.
 
Upvote 0

JackKirk

Well-Known Member
Licensed User
Longtime User
strat, thanks for your response.

Digging a bit deeper I think I may have worked it out.

My confusion was that GetARGB worked fine in B4I if you did something like GetARGB(Colors.Red) but didn't when I threw pixels at it that I sourced from doing some low level bitmap-to-byte then byte-to-int (i.e. pixel) conversions.

This confusion was because I did not know that iOS natively uses a different pixel structure to what I am used to (IBM System/370, Windows, B4A and B4I).

Have a good look at the code in JanPRO's post:

https://www.b4x.com/android/forum/threads/getpixelcolor.56922

which I reproduce here:
B4X:
Sub GetPixelColor(Bitm As Bitmap, x As Int, y As Int) As Int
     Dim NativeMe As NativeObject = Me
     Dim UIColor As Object = NativeMe.RunMethod("GetPixelColor:::", Array (Bitm,x,y))
     return NativeMe.UIColorToColor(UIColor)
End Sub


#If OBJC

- (UIColor *)GetPixelColor:(UIImage *)bitmap :(int)x :(int)y {

    CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(bitmap.CGImage));
    const UInt8* data = CFDataGetBytePtr(pixelData);

    int pixelInfo = ((bitmap.size.width  * y) + x ) * 4;

    UInt8 red = data[pixelInfo];
    UInt8 green = data[(pixelInfo + 1)];
    UInt8 blue = data[pixelInfo + 2];
    UInt8 alpha = data[pixelInfo + 3];
    CFRelease(pixelData);

    return [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f];
}
#End If
Particularly notice the order of color data in the Objective C: red/green/blue/alpha (RGBA) - as distinct from the ARGB used in B4A and B4I.

Also note that in his GetPixelColor sub he uses a NativeObject method called UIColorToColor which has in-line documentation that reads "Converts a UIColor to B4i color value." - i.e. it effectively converts from RGBA to ARGB.

So, if you are after the RGBA of a native iOS pixel you would do something like this:
B4X:
...
    Dim rgba() As Int
    rgba = GetRGBA(some_native_iOS_pixel)
    Log("R = " & rgba(0))
    Log("G = " & rgba(1))
    Log("B = " & rgba(2))
    Log("A = " & rgba(3))
...

Sub GetRGBA(native_iOS_pixel As Int) As Int()
    Dim res(4) As Int
    res(0) = Bit.UnsignedShiftRight(Bit.And(native_iOS_pixel, 0xff000000), 24)
    res(1) = Bit.UnsignedShiftRight(Bit.And(native_iOS_pixel, 0xff0000), 16)
    res(2) = Bit.UnsignedShiftRight(Bit.And(native_iOS_pixel, 0xff00), 8)
    res(3) = Bit.And(native_iOS_pixel, 0xff)
    Return res
End Sub

I also dug up some further reading that seems to reinforce this:

http://iphonedevelopment.blogspot.com.au/2008/10/iphone-optimized-pngs.html

I hope I haven't added to others confusion by trying to explain the unravelling of mine...
 
Last edited:
Upvote 0

strat

Active Member
Licensed User
Longtime User
Yes, this may be confusing and a bit complex. I saw JanPRO's GetPixel method but hadn't tried up to now. I wrote a simple GetPixel example and at first it didn't work. I realized that Imageview and its bitmap size are not equal as you will see in the log. It may be the source of the problem. In my code, Alpha is at the leftmost of the color array and it seems true.

Working example is here:
B4X:
#Region  Project Attributes
    #ApplicationLabel: Pixel
    #Version: 1.0.0
    'Orientation possible values: Portrait, LandscapeLeft, LandscapeRight and PortraitUpsideDown
    #iPhoneOrientations: Portrait, LandscapeLeft, LandscapeRight
    #iPadOrientations: Portrait, LandscapeLeft, LandscapeRight, PortraitUpsideDown
    #PlistExtra: <key>UIViewControllerBasedStatusBarAppearance</key><false/>
#End Region

Sub Process_Globals
    Public App As Application
    Public NavControl As NavigationController
    Private Page1 As Page
    Private iv As ImageView
    Dim c As Canvas
    Private sw,sh As Int
    Private j As Int
    Private pcolor,cl As Int
    Private btn As Button
    Private bmp As Bitmap
    Private scw, sch As Float
End Sub

Private Sub Application_Start (Nav As NavigationController)
    Dim no As NativeObject = App
    no.RunMethod("setStatusBarHidden:animated:", Array(True, False))
    NavControl = Nav
    Page1.Initialize("Page1")
    Page1.Title = "Page 1"
    NavControl.NavigationBarVisible = False
    NavControl.ShowPage(Page1)
    sw=GetDeviceLayoutValues.Width
    sh=GetDeviceLayoutValues.Height
    btn.Initialize("btn",btn.STYLE_SYSTEM)
    btn.Color=Colors.LightGray
    btn.Text="Click"
    Page1.RootPanel.AddView(btn,40*sw/100,70*sh/100,20*sw/100,10*sh/100)
    iv.Initialize("iv")
    Page1.RootPanel.AddView(iv,0,0,sw,sh)
    c.Initialize(iv)
    cl=255
    For j=0 To sh
        cl=255-(j mod 256)
        c.DrawLine(0,j,sw,j,Colors.ARGB(255 ,cl , cl, cl ),1)
    Next
    iv.Bitmap=c.CreateBitmap
    bmp=iv.Bitmap
    Log("Bitmap size = "&bmp.Width&"   "&bmp.Height)
    Log("imageview size = "&iv.Width&"  "&iv.Height)
    btn.BringToFront
    scw=bmp.Width/iv.Width
    sch=bmp.Height/iv.Height
End Sub

Sub GetARGB(Clr As Int) As Int()
    Dim res(4) As Int
    res(0) = Bit.UnsignedShiftRight(Bit.And(Clr, 0xff000000), 24)
    res(1) = Bit.UnsignedShiftRight(Bit.And(Clr, 0xff0000), 16)
    res(2) = Bit.UnsignedShiftRight(Bit.And(Clr, 0xff00), 8)
    res(3) = Bit.And(Clr, 0xff)
    Return res
End Sub


Sub GetPixelColor(Bitm As Bitmap, x As Int, y As Int) As Int
     Dim NativeMe As NativeObject = Me
     Dim UIColor As Object = NativeMe.RunMethod("GetPixelColor:::", Array (Bitm,x*scw,y*sch))
     Return NativeMe.UIColorToColor(UIColor)
End Sub

Sub btn_Click
    Dim argb() As Int
    Log("Height       Color Value            A                R               G             B")
    For j=0 To sh
        pcolor=GetPixelColor(iv.Bitmap,100,j)
        argb = GetARGB(pcolor)
        Log(j&"                  "&pcolor&"               "&argb(0)&"            "&argb(1)&"            "&argb(2)&"         "&argb(3))
        c.DrawLine(0,j,10,j,Colors.ARGB(255 ,j , 255, 255 ),1)
     Next
    c.Refresh
End Sub


#If OBJC

- (UIColor *)GetPixelColor:(UIImage *)bitmap :(int)x :(int)y {

    CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(bitmap.CGImage));
    const UInt8* data = CFDataGetBytePtr(pixelData);

    int pixelInfo = ((bitmap.size.width  * y) + x ) * 4;

    UInt8 red = data[pixelInfo];
    UInt8 green = data[(pixelInfo + 1)];
    UInt8 blue = data[pixelInfo + 2];
    UInt8 alpha = data[pixelInfo + 3];
    CFRelease(pixelData);

    return [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f];
}
#End If
 
Upvote 0

JackKirk

Well-Known Member
Licensed User
Longtime User
strat, my last post is obviously confusing.

In your example you are creating colors with the B4I function Colors.whatever and then ARGB'ing them with JanPRO's GetARGB code.

When you use the B4I function Colors.whatever you supply ARGB but under the covers it is creating RGBA (probably by a function similar to NativeObject.ColorToUIColor).

When you use JanPRO's GetARGB code it is first extracting RGBA then reconfiguring it as ARGB via NativeObject.UIColorToColor.

You are obviously going to always see ARGB at the B4I level - as intended by the 2 NativeObject functions.

My problem/confusion was that my pixels/colors were not being sourced via B4I Colors.whatever they were coming from code of the form:
B4X:
Dim no As NativeObject = Me
Dim bmp_bytes() As Byte = no.NSDataToArray(no.RunMethod("BitmapToArray:", Array(bmp)))
Dim pixels() As Int
Dim bmp_ByteConverter As ByteConverter
pixels = bmp_ByteConverter.IntsFromBytes(bmp_bytes)
where I was doing a mass extraction of pixels from a bitmap - that at no stage involved a RGBA/ARGB transformation.

Hope this doesn't add to the confusion...
 
Upvote 0

strat

Active Member
Licensed User
Longtime User
My code is a sample of GexPixel from bitmap. It reads only one pixel at once. As I understood you need mass extraction of ARGB color values.
I think you should write your own function to do this. You haven't objected what will you do. If you have a sample project that doesn't work, you can share it. You can get better help .
 
Upvote 0

JackKirk

Well-Known Member
Licensed User
Longtime User
strat,

I have resolved it using the last bit of code in post #3.

When I get the entire exercise sorted I will post a code snippet explaining what I am doing.

Thanks...
 
Upvote 0

pivar

Member
Licensed User
Longtime User
In this B4A post:

https://www.b4x.com/android/forum/threads/get-alpha-red-green-blue.7258/#post-41502

Erel supplies this code to extract RGB from an Android pixel:
B4X:
Sub Activity_Create(FirstTime As Boolean)
    Dim argb() As Int
    argb = GetARGB(Colors.Transparent)
    Log("A = " & argb(0))
    Log("R = " & argb(1))
    Log("G = " & argb(2))
    Log("B = " & argb(3))
End Sub

Sub GetARGB(Color As Int) As Int()
    Dim res(4) As Int
    res(0) = Bit.UnsignedShiftRight(Bit.And(Color, 0xff000000), 24)
    res(1) = Bit.UnsignedShiftRight(Bit.And(Color, 0xff0000), 16)
    res(2) = Bit.UnsignedShiftRight(Bit.And(Color, 0xff00), 8)
    res(3) = Bit.And(Color, 0xff)
    Return res
End Sub

If you run similar code in B4I it does not work correctly - I suspect some sort of endian problem - the A is at least where the B was - or at least seems to be some of the time.

What is the correct equivalent for an iOS pixel?

Thanks in anticipation...

A little bit out of subject ; I will try work on forms in bitmaps ; load ARGB pixels in array is fast enough with getPixels , but get A,R,G,B colors with getARGB takes time ( on my phone ~100k pixels/s) ;I need only R,G,B and with:
pixdec=p(i)+dec
pB(i)=pixdec Mod 256:pixdec=pixdec/256
pG(i)=pixdec Mod 256:pixdec=pixdec/256
pR(i)=pixdec Mod 256
it runs ten times faster (dec=256*256*256*127) , pixdec as int
 
Upvote 0
Top