Platforms to show: All Mac Windows Linux Cross-Platform

/Images/LCMS2/Drawing on Mac with Colorspaces/Drawing on Mac with Colorspaces


Required plugins for this example: MBS MacBase Plugin, MBS Main Plugin, MBS MacCocoa Plugin, MBS Picture Plugin, MBS MacCG Plugin, MBS MacCF Plugin, MBS Images Plugin

You find this example project in your Plugins Download as a Xojo project file within the examples folder: /Images/LCMS2/Drawing on Mac with Colorspaces/Drawing on Mac with Colorspaces

This example is the version from Sat, 28th May 2021.

Project "Drawing on Mac with Colorspaces.xojo_binary_project"
MiscFileTypes
Filetype ICC profile (*.ICC)
Filetype ICC profile (*.ICM)
Filetype All profiles
End MiscFileTypes
Class App Inherits Application
Const kEditClear = "&Delete"
Const kFileQuit = "&Quit"
Const kFileQuitShortcut = ""
End Class
Class Window1 Inherits Window
Control CanvasCarbon Inherits Canvas
ControlInstance CanvasCarbon Inherits Canvas
EventHandler Sub MouseExit() 'set the patch number out-of-range newPatchNumber = -1 'redraw the canvas picture and update the canvas DrawCanvasPict me.Invalidate End EventHandler
EventHandler Sub MouseMove(X As Integer, Y As Integer) Dim column, row As Integer 'determine row row = floor(Y / patchDim) 'determine column column = floor(X / patchDim) 'check if the mouse if over a patch newPatchNumber = (column * patchRows) + row if newPatchNumber < 0 then 'the mouse position corresponds to a negative patch number; assign an out-of-range value newPatchNumber = -1 elseif newPatchNumber < patchQTY then ' the mouse is over a patch; check if the patch number changed since the last move if newPatchNumber <> oldPatchNumber then 'redraw the canvas picture and update the canvas 'Note : the old patch number will be re-assigned when redrawing the canvas DrawCanvasPict me.Invalidate end if else 'the patch number is higher than the patch quantity newPatchNumber = -1 end if End EventHandler
EventHandler Sub Open() ' assign an out-of-range value to the index of the patch under the mouse newPatchNumber = -1 End EventHandler
EventHandler Sub Paint(g As Graphics, areas() As REALbasic.Rect) // Note: This event redraws the canvas picture (CanvasPict). // The canvas background (CanvasBackground) is made (Method/DrawCanvasBackground) only when // the canvas is resized or the patches positions are changed (these events cannot happen in this demo). // The canvas picture is made from the canvas background picture to which is added the borders of the patch // under the mouse cursor in Method/DrawCanvasPict. // The canvas picture is redrawn when the mouse cursor moves to another patch or out of the canvas. // Because the background does not need to be redrawn each time, this method can rapidly refresh the canvas // for many thousand patches. // The code is based on the Carbon Framework and the colors are NOT displayed accurately in relation to the monitor profile. 'check if this is the first instance of the event if CanvasBackground = nil then 'prepare the buffer and redraw the canvas picture; this is a picture with all the patches DrawCanvasBackground 'add a frame indicating over which patch the mouse is located DrawCanvasPict end if 'transfer the canvas picture to the canvas g.DrawPicture CanvasPict, 0, 0 End EventHandler
End Control
Control CanvasCocoaSlow Inherits Canvas
ControlInstance CanvasCocoaSlow Inherits Canvas
EventHandler Sub MouseExit() 'set the patch number out-of-range newPatchNumber = -1 'redraw the canvas picture and update the canvas me.Invalidate End EventHandler
EventHandler Sub MouseMove(X As Integer, Y As Integer) Dim column, row As Integer 'determine row row = floor(Y / patchDim) 'determine column column = floor(X / patchDim) 'check if the mouse if over a patch newPatchNumber = (column * patchRows) + row if newPatchNumber < 0 then 'the mouse position corresponds to a negative patch number; assign an out-of-range value newPatchNumber = -1 elseif newPatchNumber < patchQTY then ' the mouse is over a patch; check if the patch number changed since the last move if newPatchNumber <> oldPatchNumber then 'redraw the canvas picture and update the canvas 'Note : the old patch number will be re-assigned when redrawing the canvas me.Invalidate end if else 'the patch number is higher than the patch quantity newPatchNumber = -1 end if End EventHandler
EventHandler Sub Open() ' assign an out-of-range value to the index of the patch under the mouse newPatchNumber = -1 End EventHandler
EventHandler Sub Paint(g As Graphics, areas() As REALbasic.Rect) dim CanvasGraphics as new NSGraphicsMBS dim NSspace as NSColorSpaceMBS dim c1 as NSColorMBS Dim RectMBS As NSRectMBS dim row, column As Integer Dim sampleRow, sampleColumn As Integer // Note: This event redraws the canvas background AND the borders of the patch under the mouse cursor at every refresh. // It is fast enough up to many hundred patches but will show delays for more patches. // The code is optimized for the Cocoa Framework and the colors are displayed accurately in relation to the monitor profile. // define the data color space // Note: we assign the display profile since we computed the display RGB for this profile if profileFolderItem = nil then // a display profile was not found; use sRGB as a default space NSspace = NSColorSpaceMBS.sRGBColorSpace else 'NSspace = NSColorSpaceMBS.adobeRGB1998ColorSpace NSspace = NSColorSpaceMBS.ColorSpaceWithICCProfileData(profileFolderItem) end if 'define a rectangle to be filled with the color of each patch RectMBS = New NSRectMBS RectMBS.Width = patchDim RectMBS.Height = patchDim for row = 0 to 1 for column = 0 to 1 'set the patch/rectangle position RectMBS.X = patchDim * column RectMBS.Y = (CanvasCocoaSlow.Height - patchDim) - (patchDim * row) 'define and set the patch color c1 = NSColorMBS.colorWithColorSpace(NSspace, patchColors(0, row+2*column)/255, patchColors(1, row+2*column)/255, patchColors(2, row+2*column)/255,1.0) CanvasGraphics.setColor(c1) 'draw the filled rectangle CanvasGraphics.fillRect(RectMBS) next column next row ' add a border to the patch under the mouse; check if the mouse is over a patch if newPatchNumber <> -1 then 'the mouse is over a patch; define and set the border color (black) c1 = NSColorMBS.colorWithColorSpace(NSspace, 0/255, 0/255, 0/255,1.0) CanvasGraphics.setColor(c1) 'derive the patch row and column // Note: computing the row must take into account the reversed Y origin of the Cocoa canvas relative to how the patches are displayed (from top to bottom) // sampleRow = (NumberOfRows - 1) - newPatchNumber Mod patchRows sampleRow = 1 - newPatchNumber Mod patchRows sampleColumn = floor (newPatchNumber / patchRows) 'draw the patch border CanvasGraphics.drawRect(sampleColumn * patchDim, sampleRow * patchDim, patchDim, patchDim) CanvasGraphics.drawRect((sampleColumn * patchDim)+1, (sampleRow * patchDim)+1, patchDim-2, patchDim-2) end if 'assign the new patch number as the old number oldPatchNumber = newPatchNumber End EventHandler
End Control
Control CanvasCocoaFast Inherits Canvas
ControlInstance CanvasCocoaFast Inherits Canvas
EventHandler Sub MouseExit() 'set the patch number out-of-range newPatchNumber = -1 'redraw the canvas picture and update the canvas DrawCanvasPict CanvasCocoaFast.Invalidate End EventHandler
EventHandler Sub MouseMove(X As Integer, Y As Integer) Dim column, row As Integer 'determine row row = floor(Y / patchDim) 'determine column column = floor(X / patchDim) 'check if the mouse if over a patch newPatchNumber = (column * patchRows) + row if newPatchNumber < 0 then 'the mouse position corresponds to a negative patch number; assign an out-of-range value newPatchNumber = -1 elseif newPatchNumber < patchQTY then ' the mouse is over a patch; check if the patch number changed since the last move if newPatchNumber <> oldPatchNumber then 'redraw the canvas picture and update the canvas 'Note : the old patch number will be re-assigned when redrawing the canvas DrawCanvasPict CanvasCocoaFast.Invalidate end if else 'the patch number is higher than the patch quantity newPatchNumber = -1 end if End EventHandler
EventHandler Sub Open() ' assign an out-of-range value to the index of the patch under the mouse newPatchNumber = -1 End EventHandler
EventHandler Sub Paint(g As Graphics, areas() As REALbasic.Rect) Dim BitmapContext As CGBitmapContextMBS Dim BitmapImageCG As CGImageMBS Dim BitmapMB As MemoryBlock dim ICCProfileData as memoryblock dim ICCProfile As LCMS2ProfileMBS dim w as CGContextMBS // of window // Note: This event redraws the canvas picture (CanvasPict). // The canvas background (CanvasBackground) is made (Method/DrawCanvasBackground) only when // the canvas is resized or the patches positions are changed (these events cannot happen in this demo). // The canvas picture is made from the canvas background picture to which is added the borders of the patch // under the mouse cursor in Method/DrawCanvasPict. // The canvas picture is redrawn when the mouse cursor moves to another patch or out of the canvas. // Because the background does not need to be redrawn each time, this method can rapidly refresh the canvas // for many thousand patches. // The code is optimized for the Cocoa Framework and the colors are displayed accurately in relation to the monitor profile. // get profile info in MemoryBlock form in order to define the color space if profileFolderItem = nil then // a display profile was not found; define an sRGB profile ICCProfile = LCMS2ProfileMBS.CreateSRGBProfile() else ICCProfile = LCMS2ProfileMBS.OpenProfileFromFile(profileFolderItem) end if ICCProfileData = ICCProfile.SaveProfileToMemory // define the color space dim colorspace as CGColorSpaceMBS = CGColorSpaceMBS.CreateWithICCProfile(ICCProfileData) 'check if this is the first instance of the event if CanvasBackground = nil then 'prepare the buffer and redraw the canvas picture; this is a picture with all the patches DrawCanvasBackground 'add a frame indicating over which patch the mouse is located DrawCanvasPict end if // define a MemoryBlock for the bitmap BitmapMB = new MemoryBlock(200*3*200) // Method-A // paint the Canvas Picture (using CGImageMBS) // Note: drawing is done through CGContextMBS if CanvasPict.CopyRGBtoMemoryblockMBS(BitmapMB, 0, 0, -1, 0, 0) then BitmapContext = CGBitmapContextMBS.CreateRGB(BitmapMB, 200, 200, 600, colorspace) BitmapImageCG = BitmapContext.CGImage(False, 0) // we are inside paint event! w = CGContextMBS.contextWithCGContext(g.Handle(g.HandleTypeCGContextRef)) w.DrawPicture(BitmapImageCG, CGMakeRectMBS(0,0,200,200)) end if // Method-B // paint the Canvas Picture (using NSImageMBS) // Note: drawing is done through the Paint event g (Graphics) class // if CanvasPict.CopyRGBtoMemoryblockMBS(BitmapMB, 0, 0, -1, 0, 0) then // BitmapContext = CGBitmapContextMBS.CreateRGB(BitmapMB, 200, 200, 600, colorspace) // BitmapImageCG = BitmapContext.CGImage(False, 0) // // create an instance of the NSImageMBS with the content of CGImageMBS // BitmapImageNS = NSImageMBS.imageWithCGImage(BitmapImageCG) // g.DrawPicture BitmapImageNS.CopyPicture, 0, 0 // end if End EventHandler
End Control
Control TestLabel Inherits Label
ControlInstance TestLabel(0) Inherits Label
ControlInstance TestLabel(1) Inherits Label
ControlInstance TestLabel(2) Inherits Label
End Control
Control RGBlabel Inherits Label
ControlInstance RGBlabel(0) Inherits Label
ControlInstance RGBlabel(1) Inherits Label
ControlInstance RGBlabel(2) Inherits Label
ControlInstance RGBlabel(3) Inherits Label
End Control
Control SelectProfileButton Inherits PushButton
ControlInstance SelectProfileButton Inherits PushButton
EventHandler Sub Action() dim f As FolderItem dim dlg As OpenDialog ' let the user browse for an external profile '************************* ' define a new OpenDialog dlg= New OpenDialog #If TargetMachO Then dlg.Filter = MiscFileTypes.ICCProfileICC + MiscFileTypes.ICCProfileICM #else ' for Windows dlg.Filter = MiscFileTypes.AllProfiles + MiscFileTypes.ICCProfileICC + MiscFileTypes.ICCProfileICM // All profiles or separate categories #endif dlg.Title = "Open an RGB profile (*.icc, *.icm)" '************************* ' open the dialog f = dlg.ShowModal If f = Nil then ' the user did not select a file Return End if if Not f.IsReadable then ' the file cannot be read; it may be locked by another application MsgBox "ERROR: The < " + f.DisplayName + " > file cannot be read!" + EndOfLine + "It may be opened in another program." Return end if '************************* '************************* profileFolderItem = f ProfileNameLabel.Text = f.DisplayName // update the display CanvasCarbon.Invalidate CanvasCocoaSlow.Invalidate CanvasCocoaFast.Invalidate End EventHandler
End Control
Control ProfileNameLabel Inherits Label
ControlInstance ProfileNameLabel Inherits Label
End Control
EventHandler Sub Close() #if TargetMacOS then Quit #endif End EventHandler
EventHandler Sub Open() Dim i As Integer 'load the 4 patches colors; these values presume that they were computed to correspond to a specific RGB space 'Note: the ICC computation for the "Carbon" case are not included; the "Cocoa" case requires that the user selects an external profile patchColors(0,0) = 65 patchColors(1,0) = 135 patchColors(2,0) = 164 patchColors(0,1) = 165 patchColors(1,1) = 85 patchColors(2,1) = 147 patchColors(0,2) = 227 patchColors(1,2) = 198 patchColors(2,2) = 53 patchColors(0,3) = 182 patchColors(1,3) = 148 patchColors(2,3) = 129 ' write the patch RGB values in the window for i = 0 to 3 RGBlabel(i).text = Str(patchColors(0, i)) + ", " + Str(patchColors(1, i)) + ", " + Str(patchColors(2, i)) next i ' set the patch size patchDim = 100 'set the patch qty patchQty = 4 'set the number of rows patchRows = 2 ' refresh only CanvasCarbon since the other two canvases require that a profile be loaded beforehand CanvasCarbon.Refresh End EventHandler
Sub DrawCanvasBackground() Dim i, xpos, ypos As Integer 'define a new Canvas background CanvasBackground = New Picture(CanvasCarbon.width, CanvasCarbon.height, 32) 'define a new Canvas Picture CanvasPict = New Picture(CanvasCarbon.width, CanvasCarbon.height, Screen(0).Depth) ' draw the patches on the Canvas background for i = 0 to patchQty -1 ypos = (i Mod patchRows) * patchDim xpos = Floor(i / patchRows) * patchDim CanvasBackground.Graphics.ForeColor = RGB(patchColors(0,i), patchColors(1,i), patchColors(2,i)) CanvasBackground.Graphics.FillRect(xpos, ypos, patchDim, patchDim) next i End Sub
Sub DrawCanvasPict() Dim sampleRow, sampleColumn As Integer 'check if the canvas picture was defined 'Note: it will not be defined when this Method is called during window initialization if CanvasPict = nil then Return end if 'transfer the Canvas background to the Canvas Picture CanvasPict.Graphics.DrawPicture CanvasBackground, 0, 0 '***************************************************** ' add a border to the patch under the mouse; check if the mouse is over a patch if newPatchNumber <> -1 then 'the mouse is over a patch; select the border color (black) CanvasPict.Graphics.foreColor = RGB(0,0,0) 'draw the patch border sampleRow = newPatchNumber Mod patchRows sampleColumn = floor (newPatchNumber / patchRows) CanvasPict.Graphics.drawRect (sampleColumn * patchDim), (sampleRow * patchDim), patchDim, patchDim CanvasPict.Graphics.drawRect (sampleColumn * patchDim)+1, (sampleRow * patchDim)+1, patchDim-2, patchDim-2 end if 'assign the new patch number as the old number oldPatchNumber = newPatchNumber End Sub
Note "About"
This example is provided by Danny Pascal dpascale@babelcolor.com It shows various ways to draw color measurement patch fields using color spaces.
Property Protected CanvasBackground As Picture
Property Protected CanvasPict As Picture
Property newPatchNumber As Integer
Property oldPatchNumber As Integer
Property profileFolderItem As FolderItem
End Class
MenuBar MainMenuBar
MenuItem FileMenu = "&File"
MenuItem FileQuit = "#App.kFileQuit"
MenuItem EditMenu = "&Edit"
MenuItem EditUndo = "&Undo"
MenuItem EditSeparator1 = "-"
MenuItem EditCut = "Cu&t"
MenuItem EditCopy = "&Copy"
MenuItem EditPaste = "&Paste"
MenuItem EditClear = "#App.kEditClear"
MenuItem EditSeparator2 = "-"
MenuItem EditSelectAll = "Select &All"
End MenuBar
Module common
Property PrefLastFolderProfile As String
Property patchColors(2,3) As Integer
Property patchDim As Integer
Property patchQty As Integer
Property patchRows As Integer
End Module
End Project

The items on this page are in the following plugins: MBS Images Plugin.


The biggest plugin in space...