<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Picture &#8211; Xojo Programming Blog</title>
	<atom:link href="https://blog.xojo.com/tag/picture/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.xojo.com</link>
	<description>Blog about the Xojo programming language and IDE</description>
	<lastBuildDate>Thu, 28 Aug 2025 14:55:07 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>Implementing a Non-Blocking Image Processor with Xojo Threads</title>
		<link>https://blog.xojo.com/2025/08/28/implementing-a-non-blocking-image-processor-with-xojo-threads/</link>
		
		<dc:creator><![CDATA[Gabriel Ludosanu]]></dc:creator>
		<pubDate>Thu, 28 Aug 2025 14:56:33 +0000</pubDate>
				<category><![CDATA[Desktop]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Mac]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Dictionary]]></category>
		<category><![CDATA[FolderItem]]></category>
		<category><![CDATA[Graphics]]></category>
		<category><![CDATA[Picture]]></category>
		<category><![CDATA[Threads]]></category>
		<guid isPermaLink="false">https://blog.xojo.com/?p=15302</guid>

					<description><![CDATA[Have you ever written an app that needs to do some heavy lifting, like processing a big batch of files? Maybe you click a button,&#8230;]]></description>
										<content:encoded><![CDATA[
<p>Have you ever written an app that needs to do some heavy lifting, like processing a big batch of files? Maybe you click a button, and suddenly your entire app just … stops. The cursor turns into a spinning wheel, the windows won’t respond, and your users are left wondering if it crashed. It’s a frustrating experience, but don’t worry, there’s a solution:&nbsp;<a href="https://documentation.xojo.com/api/language/threading/thread.html" target="_blank" rel="noreferrer noopener">Threads</a>!</p>



<p>Let&#8217;s build a simple but incredibly powerful Desktop app: a threaded image resizer. This tool will let you pick a folder of images and resize them all without ever locking up the app. It’s a perfect showcase of how easy and effective <a href="https://documentation.xojo.com/topics/threading/index.html" data-type="link" data-id="https://documentation.xojo.com/topics/threading/index.html" target="_blank" rel="noreferrer noopener">threading</a> can be in Xojo.</p>



<p>Let’s get started!</p>



<h4 class="wp-block-heading" id="step-1-designing-the-user-interface">Step 1: Designing the User Interface</h4>



<p>First things first, let’s lay out our app’s window. We’re going for a clean and simple design. All you need to do is create a new Xojo Desktop project and add the following controls from the Library onto your main window:</p>



<ol class="wp-block-list">
<li><strong>DesktopButton</strong>: This will be our “start” button. The user will click this to select a folder and begin the resizing process. Let’s set its&nbsp;<code>Caption</code>&nbsp;to “Select folder &amp;&amp; Start”.</li>



<li><strong>DesktopProgressBar</strong>: This will give the user visual feedback on the progress of the resizing operation.</li>



<li><strong>DesktopLabel</strong>: We’ll use this label to display status updates, like which file is currently being processed or when the job is complete. Let’s call it&nbsp;<code>StatusLabel</code>.</li>
</ol>



<p>Your app layout should look something like this:</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="692" src="https://blog.xojo.com/wp-content/uploads/2025/08/thread_image_resizer_1-1024x692.jpg" alt="" class="wp-image-15303" srcset="https://blog.xojo.com/wp-content/uploads/2025/08/thread_image_resizer_1-1024x692.jpg 1024w, https://blog.xojo.com/wp-content/uploads/2025/08/thread_image_resizer_1-300x203.jpg 300w, https://blog.xojo.com/wp-content/uploads/2025/08/thread_image_resizer_1-768x519.jpg 768w, https://blog.xojo.com/wp-content/uploads/2025/08/thread_image_resizer_1.jpg 1525w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h4 class="wp-block-heading" id="step-2-adding-the-magic-ingredient-the-thread">Step 2: Adding the Magic Ingredient: The Thread</h4>



<div class="wp-block-group is-nowrap is-layout-flex wp-container-core-group-is-layout-ad2f72ca wp-block-group-is-layout-flex">
<p>Now for the star of the show! Go to the Library and find the&nbsp;<code>Thread</code>&nbsp;object. Drag and drop one onto your window. By default, it might be named&nbsp;<code>Thread1</code>, but I’ve renamed mine to&nbsp;<code>ResizeThread</code>&nbsp;to make its purpose crystal clear. This object is where our background work will happen.</p>



<figure class="wp-block-image size-full is-resized"><img decoding="async" width="298" height="287" src="https://blog.xojo.com/wp-content/uploads/2025/08/thread_image_resizer_2.jpg" alt="" class="wp-image-15304" style="width:500px"/></figure>
</div>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Quick note on thread types: Cooperative threads (the default) share a single CPU core with the main/UI thread and other cooperative threads. Preemptive threads can run on separate CPU cores and deliver true parallelism for CPU-bound work. More info on this subject: <a href="https://blog.xojo.com/2024/10/01/cooperative-to-preemptive-weaving-new-threads-into-your-apps/" target="_blank" rel="noreferrer noopener">Cooperative to Preemptive: Weaving New Threads into your Apps</a></p>
</blockquote>



<p>Why this project uses Preemptive threads: resizing many images is CPU-bound and benefits from parallel cores without blocking the UI.</p>



<h4 class="wp-block-heading" id="step-3-kicking-off-the-process">Step 3: Kicking Off the Process</h4>



<p>With the UI and thread in place, let’s add the code to get things moving. Double-click the “Select folder &amp; Start” button to add its&nbsp;<code>Pressed</code>&nbsp;event. This is what will run when the user clicks it.</p>



<p>Here’s the code for the&nbsp;<code>Pressed</code>&nbsp;event:</p>



<pre class="wp-block-code"><code>' Ask the user for a source folder (contains images)
Var f As FolderItem = FolderItem.ShowSelectFolderDialog
If f = Nil Then Return ' user cancelled

mSourceFolder = f

' Reset UI
ProgressBar1.Value = 0
StatusLabel.Text = "Starting…"

' Kick off background work!
ResizeThread.Start</code></pre>



<p>Let’s break this down. First, we ask the user to select a folder. If they don’t pick one, we simply exit. If they do, we store the selected folder in a window property called&nbsp;<code>mSourceFolder</code>&nbsp;(<code>Public Property mSourceFolder As FolderItem</code>). We then reset our progress bar and status label and, most importantly, we call&nbsp;<code>ResizeThread.Start</code>. That one simple line tells our thread to wake up and get to work by running its&nbsp;<code>Run</code>&nbsp;event.</p>



<h4 class="wp-block-heading" id="step-4-the-heavy-lifting-in-the-background">Step 4: The Heavy Lifting (in the Background)</h4>



<p>The&nbsp;<code>Run</code>&nbsp;event of the&nbsp;<code>ResizeThread</code>&nbsp;is where the core logic lives. This is where we’ll find, load, resize, and save the images. Remember, the golden rule of threads is:&nbsp;never touch the UI directly from the Run event. Doing so can cause crashes and unpredictable behavior.</p>



<p>Instead, we perform our task and then&nbsp;<em>send a message</em>&nbsp;back to the main thread with an update. We do this using a method called&nbsp;<code>AddUserInterfaceUpdate</code>.</p>



<p>Here’s the&nbsp;<code>Run</code>&nbsp;event code:</p>



<pre class="wp-block-code"><code>' Quick validation up-front
If mSourceFolder = Nil Or Not mSourceFolder.Exists Or Not mSourceFolder.IsFolder Then
  Me.AddUserInterfaceUpdate(New Dictionary("progress":0, "msg":"No folder selected"))
  Return
End If

Try
  ' Ensure output subfolder "&lt;selected&gt;/resized" exists
  Var outFolder As FolderItem = mSourceFolder.Child("resized")
  If Not outFolder.Exists Then outFolder.CreateFolder
  
  ' Build a list of candidate image files.
  ' Note: Using Picture.Open to validate images is simple and robust.
  '       (For very large folders, you could pre-filter by extension first.)
  Var images() As FolderItem
  For Each it As FolderItem In mSourceFolder.Children
    If it = Nil Or it.IsFolder Then Continue ' Ignore subfolders
    Try
      Var p As Picture = Picture.Open(it)
      If p &lt;&gt; Nil Then images.Add(it)
    Catch e As RuntimeException
      ' Non-image or unreadable; skip
    End Try
  Next
  
  Var total As Integer = images.Count
  If total = 0 Then
    Me.AddUserInterfaceUpdate(New Dictionary("progress":0, "msg":"No images found"))
    Return
  End If
  
  ' Resize settings (simple “fit within 800x800” bounding box)
  Const kMaxW As Double = 800.0
  Const kMaxH As Double = 800.0
  
  ' Short delay after each file so the main thread can repaint the UI
  Const kDelayMS As Integer = 50
  
  For i As Integer = 0 To total - 1
    Var src As FolderItem = images(i)
    Try
      ' Load source (immutable)
      Var pic As Picture = Picture.Open(src)
      If pic = Nil Or pic.Width &lt;= 0 Or pic.Height &lt;= 0 Then Continue
      
      ' Compute proportional scale (never upscale)
      Var sW As Double = kMaxW / pic.Width
      Var sH As Double = kMaxH / pic.Height
      Var scale As Double = Min(Min(sW, sH), 1.0)
      
      Var newW As Integer = Max(1, pic.Width * scale)
      Var newH As Integer = Max(1, pic.Height * scale)
      
      ' Render into a new mutable bitmap of the target size
      Var outPic As New Picture(newW, newH)
      outPic.Graphics.DrawPicture(pic, 0, 0, newW, newH, 0, 0, pic.Width, pic.Height)
      
      ' Build a safe base name (strip the last extension; handle dotfiles)
      Var name As String = src.Name
      Var ext As String = name.LastField(".")
      Var baseName As String
      If ext = name Then
        ' No dot in the name
        baseName = name
      Else
        baseName = name.Left(name.Length - ext.Length - 1)
      End If
      If baseName.Trim = "" Then baseName = "image"
      
      ' Save JPEG (fallback PNG if JPEG export not supported)
      Var outFile As FolderItem = outFolder.Child(baseName + "_resized.jpg")
      If Picture.IsExportFormatSupported(Picture.Formats.JPEG) Then
        outPic.Save(outFile, Picture.Formats.JPEG, Picture.QualityHigh) ' adjust the quality of the jpeg here
      Else
        outPic.Save(outFolder.Child(baseName + "_resized.png"), Picture.Formats.PNG)
      End If
      
    Catch io As IOException
      ' Likely a write/permission/disk issue; skip this file
    Catch u As UnsupportedOperationException
      ' Unsupported format/operation on this platform; skip
    End Try
    
    ' Progress + message to the UI (safe handoff)
    Var pct As Integer = ((i + 1) * 100) / total
    Me.AddUserInterfaceUpdate(New Dictionary("progress":pct, _
    "msg":"Resized " + src.Name + " (" + pct.ToString + "%)"))
    
    ' Let the main thread paint the update
    Me.Sleep(kDelayMS, True) ' wakeEarly=True allows early resume when idle
  Next
  
  ' Final UI message
  Me.AddUserInterfaceUpdate(New Dictionary("progress":100, "msg":"Done"))
  
Catch e As RuntimeException
  ' Any unexpected failure: report a friendly error
  Me.AddUserInterfaceUpdate(New Dictionary("progress":0, "msg":"Error: " + e.Message))
End Try</code></pre>



<h4 class="wp-block-heading" id="step-5-receiving-updates-and-safely-changing-the-ui">Step 5: Receiving Updates and Safely Changing the UI</h4>



<p>So, how does the main thread “hear” these updates? Through the&nbsp;<code>UserInterfaceUpdate</code>&nbsp;event on the&nbsp;<code>ResizeThread</code>&nbsp;itself! This event fires on the main thread, making it the one and only safe place to update our controls.</p>



<p>Here’s the code for the&nbsp;<code>UserInterfaceUpdate</code>&nbsp;event:</p>



<pre class="wp-block-code"><code>' This event runs on the main thread – SAFE to update controls here.
If data.Count = 0 Then Return

Var latest As Dictionary = data(data.LastIndex)

If latest.HasKey("progress") Then ProgressBar1.Value = latest.Value("progress")
If latest.HasKey("msg") Then StatusLabel.Text = latest.Value("msg")

' A little bonus: when we are done, open the output folder!
If latest.HasKey("msg") And latest.Value("msg") = "Done" Then
  Var outFolder As FolderItem = mSourceFolder.Child("resized")
  If outFolder &lt;&gt; Nil And outFolder.Exists Then
    outFolder.Open
  End If
End If</code></pre>



<p>In this event, we receive an array of Dictionaries (all the updates that have queued up). We usually only care about the very latest one, so we grab&nbsp;<code>data(data.LastIndex)</code>. Then, we safely access the values from the dictionary and assign them to&nbsp;<code>ProgressBar1.Value</code>&nbsp;and&nbsp;<code>StatusLabel.Text</code>. That’s it!</p>



<h3 class="wp-block-heading" id="conclusion">Conclusion</h3>



<p>And there you have it! A fully functional, non-blocking image resizer. By moving the heavy work to a&nbsp;<code>Thread</code>, we’ve created an application that provides a smooth, professional user experience.</p>



<p>The pattern is simple:</p>



<ol class="wp-block-list">
<li><strong>Start</strong>&nbsp;the task from the main thread (<code>ResizeThread.Start</code>).</li>



<li><strong>Work</strong>&nbsp;inside the thread’s&nbsp;<code>Run</code>&nbsp;event.</li>



<li><strong>Communicate</strong>&nbsp;back to the UI with&nbsp;<code>AddUserInterfaceUpdate</code>.</li>



<li><strong>Update</strong>&nbsp;the UI safely in the&nbsp;<code>UserInterfaceUpdate</code>&nbsp;event.</li>
</ol>



<p>That’s pretty much it! Go ahead, <a href="https://github.com/xolabsro/Thread-Image-Resizer" data-type="link" data-id="https://github.com/xolabsro/Thread-Image-Resizer" target="_blank" rel="noreferrer noopener">download this project’s source code</a> from GitHub, play around with it, and think about how you can use threads in your own applications!</p>



<p>Happy coding!</p>



<p><em>Gabriel is a digital marketing enthusiast who loves coding with Xojo to create cool software tools for any platform. He is always eager to learn and share new ideas!</em></p>



<ul class="wp-block-social-links has-normal-icon-size is-content-justification-center is-layout-flex wp-container-core-social-links-is-layout-16018d1d wp-block-social-links-is-layout-flex"><li class="wp-social-link wp-social-link-facebook  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.facebook.com/goxojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12 2C6.5 2 2 6.5 2 12c0 5 3.7 9.1 8.4 9.9v-7H7.9V12h2.5V9.8c0-2.5 1.5-3.9 3.8-3.9 1.1 0 2.2.2 2.2.2v2.5h-1.3c-1.2 0-1.6.8-1.6 1.6V12h2.8l-.4 2.9h-2.3v7C18.3 21.1 22 17 22 12c0-5.5-4.5-10-10-10z"></path></svg><span class="wp-block-social-link-label screen-reader-text">Facebook</span></a></li>

<li class="wp-social-link wp-social-link-x  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://x.com/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M13.982 10.622 20.54 3h-1.554l-5.693 6.618L8.745 3H3.5l6.876 10.007L3.5 21h1.554l6.012-6.989L15.868 21h5.245l-7.131-10.378Zm-2.128 2.474-.697-.997-5.543-7.93H8l4.474 6.4.697.996 5.815 8.318h-2.387l-4.745-6.787Z" /></svg><span class="wp-block-social-link-label screen-reader-text">X</span></a></li>

<li class="wp-social-link wp-social-link-linkedin  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.linkedin.com/company/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path></svg><span class="wp-block-social-link-label screen-reader-text">LinkedIn</span></a></li>

<li class="wp-social-link wp-social-link-github  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://github.com/topics/xojo" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path></svg><span class="wp-block-social-link-label screen-reader-text">GitHub</span></a></li>

<li class="wp-social-link wp-social-link-youtube  wp-block-social-link"><a rel="noopener nofollow" target="_blank" href="https://www.youtube.com/c/XojoInc" class="wp-block-social-link-anchor"><svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"><path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path></svg><span class="wp-block-social-link-label screen-reader-text">YouTube</span></a></li></ul>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
