View Issue Details

IDProjectCategoryView StatusLast Update
0036311LazarusLCLpublic2019-12-08 05:30
ReporterTKAssigned ToDmitry Boyarintsev 
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
Product Version2.1 (SVN)Product Build 
Target VersionFixed in Version 
Summary0036311: Cocoa support for BGRA or ABGR bitmaps
DescriptionThere was problem using bitmaps with BGRA or ABGR bitmaps (Windows bitmaps) in Cocoa.
There was TODO exception.
Even the IDE could not read forms with these and crashed.

I made a fix for this.
Since I could not find how to pass those bitmaps to Cocoa API directly I just made a simple workaround - swapping B and R components.
TagsNo tags attached.
Fixed in Revision62351
LazTarget-
WidgetsetCocoa
Attached Files
  • cocoa_bitmap_bgra_support.patch (4,983 bytes)
    Index: lcl/interfaces/cocoa/cocoagdiobjects.pas
    ===================================================================
    --- lcl/interfaces/cocoa/cocoagdiobjects.pas	(revision 62190)
    +++ lcl/interfaces/cocoa/cocoagdiobjects.pas	(working copy)
    @@ -31,7 +31,9 @@
         cbtGray,  // grayscale bitmap
         cbtRGB,   // color bitmap 8-8-8 R-G-B
         cbtARGB,  // color bitmap with alpha channel first 8-8-8-8 A-R-G-B
    -    cbtRGBA   // color bitmap with alpha channel last 8-8-8-8 R-G-B-A
    +    cbtRGBA,  // color bitmap with alpha channel last 8-8-8-8 R-G-B-A
    +    cbtABGR,  // color bitmap with alpha channel first 8-8-8-8 A-B-G-R
    +    cbtBGRA   // color bitmap with alpha channel last 8-8-8-8 B-G-R-A
       );
     
     const
    @@ -870,6 +872,43 @@
     constructor TCocoaBitmap.Create(AWidth, AHeight, ADepth, ABitsPerPixel: Integer;
       AAlignment: TCocoaBitmapAlignment; AType: TCocoaBitmapType;
       AData: Pointer; ACopyData: Boolean);
    +
    +type
    +  TColorEntry = packed record
    +    C1, C2, C3, C4: Byte;
    +  end;
    +  PColorEntry = ^TColorEntry;
    +
    +  TColorEntryArray = array[0..MaxInt div SizeOf(TColorEntry) - 1] of TColorEntry;
    +  PColorEntryArray = ^TColorEntryArray;
    +
    +
    +  procedure CopySwappedColorComponents(ASrcData, ADestData: PColorEntryArray; ADataSize: Integer; AType: TCocoaBitmapType);
    +  var
    +    I: Integer;
    +  begin
    +    //switch B and R components
    +    for I := 0 to ADataSize div SizeOf(TColorEntry) - 1 do
    +    begin
    +      case AType of
    +        cbtABGR:
    +        begin
    +          ADestData^[I].C1 := ASrcData^[I].C1;
    +          ADestData^[I].C2 := ASrcData^[I].C4;
    +          ADestData^[I].C3 := ASrcData^[I].C3;
    +          ADestData^[I].C4 := ASrcData^[I].C2;
    +        end;
    +        cbtBGRA:
    +        begin
    +          ADestData^[I].C1 := ASrcData^[I].C3;
    +          ADestData^[I].C2 := ASrcData^[I].C2;
    +          ADestData^[I].C3 := ASrcData^[I].C1;
    +          ADestData^[I].C4 := ASrcData^[I].C4;
    +        end;
    +      end;
    +    end;
    +  end;
    +
     begin
       inherited Create(False);
       {$ifdef VerboseBitmaps}
    @@ -885,7 +924,15 @@
         System.GetMem(FData, FDataSize);
         FFreeData := True;
         if AData <> nil then
    -      System.Move(AData^, FData^, FDataSize) // copy data
    +    begin
    +      if AType in [cbtABGR, cbtBGRA] then
    +      begin
    +        Assert(AWidth * AHeight * SizeOf(TColorEntry) = FDataSize);
    +        CopySwappedColorComponents(AData, FData, FDataSize, AType);
    +      end
    +      else
    +        System.Move(AData^, FData^, FDataSize) // copy data
    +    end
         else
           FillDWord(FData^, FDataSize shr 2, 0); // clear bitmap
       end
    @@ -982,7 +1029,7 @@
       HasAlpha: Boolean;
       BitmapFormat: NSBitmapFormat;
     begin
    -  HasAlpha := FType in [cbtARGB, cbtRGBA];
    +  HasAlpha := FType in [cbtARGB, cbtRGBA, cbtABGR, cbtBGRA];
       // Non premultiplied bitmaps can't be used for bitmap context
       // So we need to pre-multiply ourselves, but only if we were allowed
       // to copy the data, otherwise we might corrupt the original
    @@ -989,7 +1036,7 @@
       if FFreeData then
         PreMultiplyAlpha();
       BitmapFormat := 0;
    -  if FType in [cbtARGB, cbtRGB] then
    +  if FType in [cbtARGB, cbtABGR, cbtRGB] then
         BitmapFormat := BitmapFormat or NSAlphaFirstBitmapFormat;
     
       //WriteLn('[TCocoaBitmap.Create] FSamplesPerPixel=', FSamplesPerPixel,
    Index: lcl/interfaces/cocoa/cocoaobject.inc
    ===================================================================
    --- lcl/interfaces/cocoa/cocoaobject.inc	(revision 62190)
    +++ lcl/interfaces/cocoa/cocoaobject.inc	(working copy)
    @@ -776,11 +776,23 @@
           and (ADesc.BlueShift  = 0 )
           then bmpType := cbtARGB
           else
    -      if  (ADesc.AlphaShift = 0)
    +      if  (ADesc.AlphaShift = 24)
    +      and (ADesc.RedShift   = 0 )
    +      and (ADesc.GreenShift = 8 )
    +      and (ADesc.BlueShift  = 16)
    +      then bmpType := cbtABGR
    +      else
    +      if  (ADesc.AlphaShift = 0 )
           and (ADesc.RedShift   = 24)
    -      and (ADesc.GreenShift = 16 )
    +      and (ADesc.GreenShift = 16)
           and (ADesc.BlueShift  = 8 )
           then bmpType := cbtRGBA
    +      else
    +      if  (ADesc.AlphaShift = 0 )
    +      and (ADesc.RedShift   = 8 )
    +      and (ADesc.GreenShift = 16)
    +      and (ADesc.BlueShift  = 24)
    +      then bmpType := cbtBGRA
           else Exit;
         end
         else begin
    @@ -790,11 +802,23 @@
           and (ADesc.BlueShift  = 24)
           then bmpType := cbtARGB
           else
    -      if  (ADesc.AlphaShift = 24 )
    +      if  (ADesc.AlphaShift = 0 )
    +      and (ADesc.RedShift   = 24)
    +      and (ADesc.GreenShift = 16)
    +      and (ADesc.BlueShift  = 8 )
    +      then bmpType := cbtABGR
    +      else
    +      if  (ADesc.AlphaShift = 24)
           and (ADesc.RedShift   = 0 )
    -      and (ADesc.GreenShift = 8)
    +      and (ADesc.GreenShift = 8 )
           and (ADesc.BlueShift  = 16)
           then bmpType := cbtRGBA
    +      else
    +      if  (ADesc.AlphaShift = 24)
    +      and (ADesc.RedShift   = 16)
    +      and (ADesc.GreenShift = 8 )
    +      and (ADesc.BlueShift  = 0 )
    +      then bmpType := cbtBGRA
           else Exit;
         end;
       end
    

Activities

TK

2019-11-13 15:49

reporter  

cocoa_bitmap_bgra_support.patch (4,983 bytes)
Index: lcl/interfaces/cocoa/cocoagdiobjects.pas
===================================================================
--- lcl/interfaces/cocoa/cocoagdiobjects.pas	(revision 62190)
+++ lcl/interfaces/cocoa/cocoagdiobjects.pas	(working copy)
@@ -31,7 +31,9 @@
     cbtGray,  // grayscale bitmap
     cbtRGB,   // color bitmap 8-8-8 R-G-B
     cbtARGB,  // color bitmap with alpha channel first 8-8-8-8 A-R-G-B
-    cbtRGBA   // color bitmap with alpha channel last 8-8-8-8 R-G-B-A
+    cbtRGBA,  // color bitmap with alpha channel last 8-8-8-8 R-G-B-A
+    cbtABGR,  // color bitmap with alpha channel first 8-8-8-8 A-B-G-R
+    cbtBGRA   // color bitmap with alpha channel last 8-8-8-8 B-G-R-A
   );
 
 const
@@ -870,6 +872,43 @@
 constructor TCocoaBitmap.Create(AWidth, AHeight, ADepth, ABitsPerPixel: Integer;
   AAlignment: TCocoaBitmapAlignment; AType: TCocoaBitmapType;
   AData: Pointer; ACopyData: Boolean);
+
+type
+  TColorEntry = packed record
+    C1, C2, C3, C4: Byte;
+  end;
+  PColorEntry = ^TColorEntry;
+
+  TColorEntryArray = array[0..MaxInt div SizeOf(TColorEntry) - 1] of TColorEntry;
+  PColorEntryArray = ^TColorEntryArray;
+
+
+  procedure CopySwappedColorComponents(ASrcData, ADestData: PColorEntryArray; ADataSize: Integer; AType: TCocoaBitmapType);
+  var
+    I: Integer;
+  begin
+    //switch B and R components
+    for I := 0 to ADataSize div SizeOf(TColorEntry) - 1 do
+    begin
+      case AType of
+        cbtABGR:
+        begin
+          ADestData^[I].C1 := ASrcData^[I].C1;
+          ADestData^[I].C2 := ASrcData^[I].C4;
+          ADestData^[I].C3 := ASrcData^[I].C3;
+          ADestData^[I].C4 := ASrcData^[I].C2;
+        end;
+        cbtBGRA:
+        begin
+          ADestData^[I].C1 := ASrcData^[I].C3;
+          ADestData^[I].C2 := ASrcData^[I].C2;
+          ADestData^[I].C3 := ASrcData^[I].C1;
+          ADestData^[I].C4 := ASrcData^[I].C4;
+        end;
+      end;
+    end;
+  end;
+
 begin
   inherited Create(False);
   {$ifdef VerboseBitmaps}
@@ -885,7 +924,15 @@
     System.GetMem(FData, FDataSize);
     FFreeData := True;
     if AData <> nil then
-      System.Move(AData^, FData^, FDataSize) // copy data
+    begin
+      if AType in [cbtABGR, cbtBGRA] then
+      begin
+        Assert(AWidth * AHeight * SizeOf(TColorEntry) = FDataSize);
+        CopySwappedColorComponents(AData, FData, FDataSize, AType);
+      end
+      else
+        System.Move(AData^, FData^, FDataSize) // copy data
+    end
     else
       FillDWord(FData^, FDataSize shr 2, 0); // clear bitmap
   end
@@ -982,7 +1029,7 @@
   HasAlpha: Boolean;
   BitmapFormat: NSBitmapFormat;
 begin
-  HasAlpha := FType in [cbtARGB, cbtRGBA];
+  HasAlpha := FType in [cbtARGB, cbtRGBA, cbtABGR, cbtBGRA];
   // Non premultiplied bitmaps can't be used for bitmap context
   // So we need to pre-multiply ourselves, but only if we were allowed
   // to copy the data, otherwise we might corrupt the original
@@ -989,7 +1036,7 @@
   if FFreeData then
     PreMultiplyAlpha();
   BitmapFormat := 0;
-  if FType in [cbtARGB, cbtRGB] then
+  if FType in [cbtARGB, cbtABGR, cbtRGB] then
     BitmapFormat := BitmapFormat or NSAlphaFirstBitmapFormat;
 
   //WriteLn('[TCocoaBitmap.Create] FSamplesPerPixel=', FSamplesPerPixel,
Index: lcl/interfaces/cocoa/cocoaobject.inc
===================================================================
--- lcl/interfaces/cocoa/cocoaobject.inc	(revision 62190)
+++ lcl/interfaces/cocoa/cocoaobject.inc	(working copy)
@@ -776,11 +776,23 @@
       and (ADesc.BlueShift  = 0 )
       then bmpType := cbtARGB
       else
-      if  (ADesc.AlphaShift = 0)
+      if  (ADesc.AlphaShift = 24)
+      and (ADesc.RedShift   = 0 )
+      and (ADesc.GreenShift = 8 )
+      and (ADesc.BlueShift  = 16)
+      then bmpType := cbtABGR
+      else
+      if  (ADesc.AlphaShift = 0 )
       and (ADesc.RedShift   = 24)
-      and (ADesc.GreenShift = 16 )
+      and (ADesc.GreenShift = 16)
       and (ADesc.BlueShift  = 8 )
       then bmpType := cbtRGBA
+      else
+      if  (ADesc.AlphaShift = 0 )
+      and (ADesc.RedShift   = 8 )
+      and (ADesc.GreenShift = 16)
+      and (ADesc.BlueShift  = 24)
+      then bmpType := cbtBGRA
       else Exit;
     end
     else begin
@@ -790,11 +802,23 @@
       and (ADesc.BlueShift  = 24)
       then bmpType := cbtARGB
       else
-      if  (ADesc.AlphaShift = 24 )
+      if  (ADesc.AlphaShift = 0 )
+      and (ADesc.RedShift   = 24)
+      and (ADesc.GreenShift = 16)
+      and (ADesc.BlueShift  = 8 )
+      then bmpType := cbtABGR
+      else
+      if  (ADesc.AlphaShift = 24)
       and (ADesc.RedShift   = 0 )
-      and (ADesc.GreenShift = 8)
+      and (ADesc.GreenShift = 8 )
       and (ADesc.BlueShift  = 16)
       then bmpType := cbtRGBA
+      else
+      if  (ADesc.AlphaShift = 24)
+      and (ADesc.RedShift   = 16)
+      and (ADesc.GreenShift = 8 )
+      and (ADesc.BlueShift  = 0 )
+      then bmpType := cbtBGRA
       else Exit;
     end;
   end

Anton Kavalenka

2019-11-13 19:51

reporter   ~0119274

IMO graphtype has platform-agnostic set of routines for rawimage manipulation

TK

2019-11-13 23:02

reporter   ~0119282

I knew something like that existed but didn't remember anymore how to use it.
I just needed this feature working.
When looking on graphtype.pp source now (RawImage_WriteBits etc.) it seems to me that at least my solution is a lot faster because TRawImage is more generous.

Dmitry Boyarintsev

2019-12-08 05:30

developer   ~0119688

thanks for the patch, applied

Issue History

Date Modified Username Field Change
2019-11-13 15:49 TK New Issue
2019-11-13 15:49 TK File Added: cocoa_bitmap_bgra_support.patch
2019-11-13 19:51 Anton Kavalenka Note Added: 0119274
2019-11-13 23:02 TK Note Added: 0119282
2019-12-08 05:30 Dmitry Boyarintsev Assigned To => Dmitry Boyarintsev
2019-12-08 05:30 Dmitry Boyarintsev Status new => resolved
2019-12-08 05:30 Dmitry Boyarintsev Resolution open => fixed
2019-12-08 05:30 Dmitry Boyarintsev Fixed in Revision => 62351
2019-12-08 05:30 Dmitry Boyarintsev LazTarget => -
2019-12-08 05:30 Dmitry Boyarintsev Widgetset Cocoa => Cocoa
2019-12-08 05:30 Dmitry Boyarintsev Note Added: 0119688