From 0d5e804de9efdd2311e4dfec3a375eff9c9068cf Mon Sep 17 00:00:00 2001 From: zeljan1 Date: Sun, 2 Feb 2025 01:02:06 +0100 Subject: [PATCH] Gtk3: hopefully fixed color swapped images (r & b channels). --- lcl/interfaces/gtk3/gtk3objects.pas | 88 +++++++++++++++++++++++++++-- lcl/interfaces/gtk3/gtk3winapi.inc | 11 +++- 2 files changed, 92 insertions(+), 7 deletions(-) diff --git a/lcl/interfaces/gtk3/gtk3objects.pas b/lcl/interfaces/gtk3/gtk3objects.pas index e99a4ba636..9a181ff322 100644 --- a/lcl/interfaces/gtk3/gtk3objects.pas +++ b/lcl/interfaces/gtk3/gtk3objects.pas @@ -266,7 +266,7 @@ type procedure drawText(x, y: Integer; AText: PChar; ALen: Integer; const ABgFilled: boolean); procedure drawEllipse(x, y, w, h: Integer; AFill, ABorder: Boolean); procedure drawSurface(targetRect: PRect; Surface: Pcairo_surface_t; sourceRect: PRect; - mask: PGdkPixBuf; maskRect: PRect); + aPixBuf: PGdkPixBuf; mask: PGdkPixBuf; maskRect: PRect); procedure drawImage(targetRect: PRect; image: PGdkPixBuf; sourceRect: PRect; mask: PGdkPixBuf; maskRect: PRect); procedure drawImage1(targetRect: PRect; image: PGdkPixBuf; sourceRect: PRect; @@ -1098,6 +1098,23 @@ begin FFormat := CAIRO_FORMAT_RGB24; end; +function IsPixelDataEmpty(AData: PByte; const Height, BytesPerLine: Integer): Boolean; +var + y: Integer; + PixelPtr: PByte; +begin + Result := True; + + if AData = nil then Exit; + + for y := 0 to Height - 1 do + begin + PixelPtr := AData + (y * BytesPerLine); + if not CompareMem(PixelPtr, AData, BytesPerLine) then + exit(False); + end; +end; + constructor TGtk3Image.Create(AData: PByte; width: Integer; height: Integer; format: Tcairo_format_t; const ADataOwner: Boolean); var @@ -1160,7 +1177,16 @@ begin end; gdk_pixbuf_fill(FHandle, 0); end else + begin FHandle := TGdkPixbuf.new_from_data(AData, GDK_COLORSPACE_RGB, format=CAIRO_FORMAT_ARGB32, 8, width, height, bytesPerLine, nil, nil); + if (format = CAIRO_FORMAT_ARGB32) then + begin + if IsPixelDataEmpty(AData, Height, BytesPerLine) then + g_object_set_data(FHandle,'lcl_color_swap', Self) + else + g_object_set_data(FHandle,'lcl_no_color_swap', Self); + end; + end; end; function TGtk3Image.Select(ACtx: TGtk3DeviceContext): TGtk3ContextObject; @@ -2088,22 +2114,74 @@ begin end; end; +procedure SwapRedBlueChannels(PixBuf: PGdkPixbuf); +var + Pixels: PByte; + x, y, RowStride, Width, Height, Channels: Integer; + Temp: Byte; +begin + if PixBuf = nil then + begin + DebugLn('ERROR: SwapRedBlueChannels: Pixbuf is nil!'); + Exit; + end; + + Width := gdk_pixbuf_get_width(PixBuf); + Height := gdk_pixbuf_get_height(PixBuf); + RowStride := gdk_pixbuf_get_rowstride(PixBuf); + Channels := gdk_pixbuf_get_n_channels(PixBuf); + + if Channels < 3 then + begin + DebugLn('ERROR: SwapRedBlueChannels: Pixbuf does not have enough color channels (expected at least 3).'); + Exit; + end; + + Pixels := gdk_pixbuf_get_pixels(PixBuf); + + for y := 0 to Height - 1 do + begin + for x := 0 to Width - 1 do + begin + Temp := Pixels[x * Channels]; + Pixels[x * Channels] := Pixels[x * Channels + 2]; + Pixels[x * Channels + 2] := Temp; + end; + Inc(Pixels, RowStride); + end; +end; + procedure TGtk3DeviceContext.drawSurface(targetRect: PRect; - Surface: Pcairo_surface_t; sourceRect: PRect; mask: PGdkPixBuf; - maskRect: PRect); + Surface: Pcairo_surface_t; sourceRect: PRect; aPixBuf: PGdkPixBuf; + mask: PGdkPixBuf; maskRect: PRect); var M: Tcairo_matrix_t; begin {$IFDEF VerboseGtk3DeviceContext} DebugLn('TGtk3DeviceContext.DrawSurface '); {$ENDIF} - cairo_set_operator(pcr, CAIRO_OPERATOR_OVER); with targetRect^ do cairo_rectangle(pcr, Left + PixelOffset, Top + PixelOffset, Right - Left, Bottom - Top); - cairo_set_source_surface(pcr, Surface, 0, 0); + if aPixBuf <> nil then + begin + //this fixes problem with some images being R & B swapped when blitted onto dest. + if (cairo_surface_get_type(CairoSurface) <> cairo_surface_get_type(Surface)) and + (g_object_get_data(aPixBuf,'lcl_color_swap') <> nil) then + begin + SwapRedBlueChannels(aPixBuf); + gdk_cairo_set_source_pixbuf(pcr, aPixBuf, 0, 0) + end else + begin + if g_object_get_data(aPixBuf,'lcl_no_color_swap') <> nil then + gdk_cairo_set_source_pixbuf(pcr, aPixBuf, 0, 0) + else + cairo_set_source_surface(pcr, Surface, 0, 0); + end; + end else + cairo_set_source_surface(pcr, Surface, 0, 0); cairo_matrix_init_identity(@M); cairo_matrix_translate(@M, SourceRect^.Left, SourceRect^.Top); diff --git a/lcl/interfaces/gtk3/gtk3winapi.inc b/lcl/interfaces/gtk3/gtk3winapi.inc index 57c0c9dc54..3a62fbfcc1 100644 --- a/lcl/interfaces/gtk3/gtk3winapi.inc +++ b/lcl/interfaces/gtk3/gtk3winapi.inc @@ -2797,7 +2797,8 @@ begin Utf8Str := UTF8Copy(Utf8Encode(Str), 1, Count) else Utf8Str := UTF8Copy(Str, 1, Count); - TGtk3DeviceContext(DC).CurrentFont.Layout^.set_text(PChar(Utf8Str), -1); + + {%H-}TGtk3DeviceContext(DC).CurrentFont.Layout^.set_text(PChar(Utf8Str), -1); TGtk3DeviceContext(DC).CurrentFont.Layout^.get_pixel_size(@Size.Cx, @Size.CY); Result := True; @@ -4485,7 +4486,13 @@ begin if (mask<>0) then DestContext.drawImage1(@ATargetRect, PgdkPixbuf(SrcContext.CurrentImage.Handle) , @ASrcRect, nil, nil) else - DestContext.drawSurface(@ATargetRect,SrcContext.CairoSurface , @ASrcRect, nil, nil); + begin + if (cairo_surface_get_type(SrcContext.CairoSurface) = CAIRO_SURFACE_TYPE_IMAGE) and + Assigned(SrcContext.CurrentImage) and Assigned(SrcContext.CurrentImage.Handle) then + DestContext.drawSurface(@ATargetRect,SrcContext.CairoSurface , @ASrcRect, SrcContext.CurrentImage.Handle, nil, nil) + else + DestContext.drawSurface(@ATargetRect,SrcContext.CairoSurface , @ASrcRect, nil, nil, nil); + end; Result := True; end;