iOS Question Draw text on canvas dramatically increases bitmap size

lymey

Active Member
Licensed User
I have a problem related to drawing text on a bitmap. When text is drawn on the canvas the resulting bitmap is multiple times the size of the original bitmap.
I need to keep close as to the original image dimensions, size, and quality as possible.
Each time the bitmap is 'edited' the size of the bitmap becomes unmanageable and causes an app crash.

Here is a sample app demonstrating the increase in bitmap size:
B4X:
'Code module
#Region  Project Attributes
    #ApplicationLabel: CanvasBitmap
    #Version: 1.0.0
    'Orientation possible values: Portrait, LandscapeLeft, LandscapeRight and PortraitUpsideDown
    #iPhoneOrientations: Portrait, LandscapeLeft, LandscapeRight
    #iPadOrientations: Portrait, LandscapeLeft, LandscapeRight, PortraitUpsideDown
    #Target: iPhone, iPad
    #ATSEnabled: True
    #MinVersion: 8
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'Public variables can be accessed from all modules.
    Public App As Application
    Public NavControl As NavigationController
    Private MainPage As Page
    Private xui As XUI
    Private Button1 As Button
    Private ImageView1 As ImageView
    Private bmpinput As B4XBitmap
    Private cvs As B4XCanvas
    Private add_text As String
    Private xui As XUI
    Private Button2 As Button
End Sub

Private Sub Application_Start (Nav As NavigationController)
    NavControl = Nav
    MainPage.Initialize("MainPage")
    NavControl.ShowPage(MainPage)
    MainPage.RootPanel.LoadLayout("Layout1")
End Sub

Sub Button1_Click
    If File.Exists(File.DirLibrary, "Test_01.jpg") Then
        File.Delete(File.DirLibrary, "Test_01.jpg")
    End If
    If File.Exists(File.DirLibrary, "TempOut.jpg") Then
        File.Delete(File.DirLibrary, "TempOut.jpg")
    End If
    If File.Exists(File.DirLibrary, "TempOut2.jpg") Then
        File.Delete(File.DirLibrary, "TempOut2.jpg")
    End If
    File.Copy(File.DirAssets, "Test_01.jpg", File.DirLibrary, "Test_01.jpg")
    Log("Input File size: " & File.Size(File.DirLibrary, "Test_01.jpg"))
'   
    bmpinput = xui.LoadBitmap(File.DirLibrary,  "Test_01.jpg")
    Dim iv As ImageView
    iv.Initialize("")
    iv.SetLayoutAnimated(0, 1, 0, 0, bmpinput.Width, bmpinput.height)
    iv.Bitmap=bmpinput
    cvs.Initialize(iv)
'
    add_text = "Put this text on bitmap"
    Dim text_size_int As Int
    text_size_int = 110
    Dim text_colour As Object
    text_colour=Colors.Yellow
    cvs.DrawText(add_text, 10dip , bmpinput.Height - 28dip ,Font.CreateNew(text_size_int), text_colour, "LEFT")
'   
    Dim Out As OutputStream
    Out = File.OpenOutput(File.DirLibrary, "TempOut.jpg", False)
    cvs.CreateBitmap.WriteToStream(Out, 100, "JPEG")
    Out.close
    cvs.Release
    ImageView1.Bitmap = xui.LoadBitmapResize(File.DirLibrary, "TempOut.jpg",ImageView1.Width, ImageView1.Height, True)
    Log("output written")
    Log("Output File size: " & File.Size(File.DirLibrary,"TempOut.jpg"))
    
End Sub

Private Sub Button2_Click
    Log("Input File 2 size: " & File.Size(File.DirLibrary, "TempOut.jpg"))
    bmpinput = xui.LoadBitmap(File.DirLibrary,  "TempOut.jpg")
    Dim iv As ImageView
    iv.Initialize("")
    iv.SetLayoutAnimated(0, 1, 0, 0, bmpinput.Width, bmpinput.height)
    iv.Bitmap=bmpinput
    cvs.Initialize(iv)
    '
    add_text = "Put more text on bitmap"
    Dim text_size_int As Int
    text_size_int = 110
    Dim text_colour As Object
    text_colour=Colors.Yellow
    cvs.DrawText(add_text, 10dip , bmpinput.Height - 28dip ,Font.CreateNew(text_size_int), text_colour, "RIGHT")
'   
    Dim Out As OutputStream
    Out = File.OpenOutput(File.DirLibrary, "TempOut2.jpg", False)
    cvs.CreateBitmap.WriteToStream(Out, 100, "JPEG")
    Out.close
    cvs.Release
    ImageView1.Bitmap = xui.LoadBitmapResize(File.DirLibrary, "TempOut2.jpg",ImageView1.Width, ImageView1.Height, True)
    Log("output2 written")
    Log("Output2 File size: " & File.Size(File.DirLibrary,"TempOut2.jpg"))
End Sub
The log shows the issue:
Input File size: 5931008
output written
Output File size: 60912978
Input File 2 size: 60912978
Error occurred on line: 92 (Main)
Object was not initialized (UIImage)
etc etc...
The app is on an iphone 7 using the remote build server.

I will email the zip file, as it is too large to post with a 5Mb sample file.


Am I missing something obvious?
 

roumei

Active Member
Licensed User
The ImageView you're creating uses a normalized scale. This scale depends on the device (e.g., iPhone 7 = 2.0 (I think), iPhone 8 Plus = 3.0, ...). Each time you create an ImageView with the pixel size of the bitmap, these values are multiplied with the NonNormalizedScale of 2 and the width and height of your image increase each time.
Create your ImageView with the adjusted normalized scale values like this and you should be fine:
B4X:
Dim LayoutVal As LayoutValues = GetDeviceLayoutValues
Dim NNSFactor As Float = LayoutVal.NonnormalizedScale
Dim iv As ImageView
iv.Initialize("")
iv.SetLayoutAnimated(0, 1, 0, 0, bmpinput.Width / NNSFactor, bmpinput.height / NNSFactor)
iv.Bitmap=bmpinput
Dim cvs As Canvas
cvs.Initialize(iv)
 

lymey

Active Member
Licensed User
The ImageView you're creating uses a normalized scale. This scale depends on the device (e.g., iPhone 7 = 2.0 (I think), iPhone 8 Plus = 3.0, ...). Each time you create an ImageView with the pixel size of the bitmap, these values are multiplied with the NonNormalizedScale of 2 and the width and height of your image increase each time.
Create your ImageView with the adjusted normalized scale values like this and you should be fine:
B4X:
Dim LayoutVal As LayoutValues = GetDeviceLayoutValues
Dim NNSFactor As Float = LayoutVal.NonnormalizedScale
Dim iv As ImageView
iv.Initialize("")
iv.SetLayoutAnimated(0, 1, 0, 0, bmpinput.Width / NNSFactor, bmpinput.height / NNSFactor)
iv.Bitmap=bmpinput
Dim cvs As Canvas
cvs.Initialize(iv)
Hi roumei,
thanks for your input, there still seems to be quite a large jump in size.
The modified code:
B4X:
Sub Button1_Click
    If File.Exists(File.DirLibrary, "Test_01.jpg") Then
        File.Delete(File.DirLibrary, "Test_01.jpg")
    End If
    If File.Exists(File.DirLibrary, "TempOut.jpg") Then
        File.Delete(File.DirLibrary, "TempOut.jpg")
    End If
    If File.Exists(File.DirLibrary, "TempOut2.jpg") Then
        File.Delete(File.DirLibrary, "TempOut2.jpg")
    End If
    File.Copy(File.DirAssets, "Test_01.jpg", File.DirLibrary, "Test_01.jpg")
    Log("Input File size: " & File.Size(File.DirLibrary, "Test_01.jpg"))
'   
    bmpinput = xui.LoadBitmap(File.DirLibrary,  "Test_01.jpg")
    Dim LayoutVal As LayoutValues = GetDeviceLayoutValues
    Log("LayoutValues: " & GetDeviceLayoutValues)
    Dim NNSFactor As Float = LayoutVal.NonnormalizedScale
    Log("NNSFactor: " & NNSFactor)
    Dim iv As ImageView
    iv.Initialize("")
    iv.SetLayoutAnimated(0, 1, 0, 0, bmpinput.Width / NNSFactor, bmpinput.height / NNSFactor)
    iv.Bitmap=bmpinput
    Dim cvs As Canvas
    cvs.Initialize(iv)

''
'    bmpinput = xui.LoadBitmap(File.DirLibrary,  "Test_01.jpg")
'    Dim iv As ImageView
'    iv.Initialize("")
'    iv.SetLayoutAnimated(0, 1, 0, 0, bmpinput.Width, bmpinput.height)
'    iv.Bitmap=bmpinput
'    cvs.Initialize(iv)
'
    add_text = "Put this text on bitmap"
    Dim text_size_int As Int
    text_size_int = 150
    Dim text_colour As Object
    text_colour=Colors.Yellow
    cvs.DrawText(add_text, 10dip , bmpinput.Height - 28dip ,Font.CreateNew(text_size_int), text_colour, "LEFT")
'   
    Dim Out As OutputStream
    Out = File.OpenOutput(File.DirLibrary, "TempOut.jpg", False)
    cvs.CreateBitmap.WriteToStream(Out, 100, "JPEG")
    Out.close
    cvs.Release
    ImageView1.Bitmap = xui.LoadBitmapResize(File.DirLibrary, "TempOut.jpg",ImageView1.Width, ImageView1.Height, True)
    Log("output written")
    Log("Output File size: " & File.Size(File.DirLibrary,"TempOut.jpg"))
    
End Sub

Private Sub Button2_Click
    Log("Input File 2 size: " & File.Size(File.DirLibrary, "TempOut.jpg"))
    bmpinput = xui.LoadBitmap(File.DirLibrary,  "TempOut.jpg")
    Dim LayoutVal As LayoutValues = GetDeviceLayoutValues
    Dim NNSFactor As Float = LayoutVal.NonnormalizedScale
    Dim iv As ImageView
    iv.Initialize("")
    iv.SetLayoutAnimated(0, 1, 0, 0, bmpinput.Width / NNSFactor, bmpinput.height / NNSFactor)
    iv.Bitmap=bmpinput
    Dim cvs As Canvas
    cvs.Initialize(iv)
    
'    Dim iv As ImageView
'    iv.Initialize("")
'    iv.SetLayoutAnimated(0, 1, 0, 0, bmpinput.Width, bmpinput.height)
'    iv.Bitmap=bmpinput
'    cvs.Initialize(iv)
    '
    add_text = "Put more text on bitmap"
    Dim text_size_int As Int
    text_size_int = 110
    Dim text_colour As Object
    text_colour=Colors.Yellow
    cvs.DrawText(add_text, 10dip , bmpinput.Height - 28dip ,Font.CreateNew(text_size_int), text_colour, "RIGHT")
'   
    Dim Out As OutputStream
    Out = File.OpenOutput(File.DirLibrary, "TempOut2.jpg", False)
    cvs.CreateBitmap.WriteToStream(Out, 100, "JPEG")
    Out.close
    cvs.Release
    ImageView1.Bitmap = xui.LoadBitmapResize(File.DirLibrary, "TempOut2.jpg",ImageView1.Width, ImageView1.Height, True)
    Log("output2 written")
    Log("Output2 File size: " & File.Size(File.DirLibrary,"TempOut2.jpg"))
End Sub
and the log output:
Application_Start
Application_Active
Input File size: 5931008
LayoutValues: 375 x 667 (normalized scale)
NNSFactor: 2
output written
Output File size: 15468539
Input File 2 size: 15468539
output2 written
Output2 File size: 18255226
And curiously, no text is visible on the bitmap!
Do I also have to manipulate the text drawing somehow?
 

roumei

Active Member
Licensed User
You're saving the JPG with a quality of 100 (= no compression) so an increase in the file size compared to the original image is to be expected. I would internally prefer the PNG format with its lossless compression or even a normal BMP. To share the image, I would save it as a JPG with a quality of 75 to 90. This should drastically reduce the file size.
Yes, you also need to adjust the values for Drawtext.
If the image in your ImageView1 looks blurred, you can load it with LoadBitmapResize and multiply the width and height with the NonNormalizedScale.
 

lymey

Active Member
Licensed User
Thanks roumei, I understand what you say about compression. For this app I need to keep the original image quality with the addition of some text. It also has to remain a jpeg file. I was a little surprised that adding a small amount of text would almost triple the file size.
In terms of saving it with a quality of 75-90, each time the file is edited and saved, the quality of the image will gradually decrease compared to the original(?)
I will have to re-evaluate how I am doing this.
Your input has been very helpful!
 
Top