Ticket #6: image.patch

File image.patch, 11.8 KB (added by abwillis, 6 days ago)
  • gtk/dw.c

     
    92219221   DW_MUTEX_LOCK;
    92229222#if GTK_MAJOR_VERSION > 1
    92239223   pixmap->pixbuf = gdk_pixbuf_new_from_file(file, NULL);
     9224   if(!pixmap->pixbuf)
     9225   {
     9226      free(pixmap);
     9227      DW_MUTEX_UNLOCK;
     9228      return NULL;
     9229   }
    92249230   pixmap->width = gdk_pixbuf_get_width(pixmap->pixbuf);
    92259231   pixmap->height = gdk_pixbuf_get_height(pixmap->pixbuf);
    92269232   gdk_pixbuf_render_pixmap_and_mask(pixmap->pixbuf, &pixmap->pixmap, &pixmap->bitmap, 1);
    … …  
    92629268#endif
    92639269   char template[] = "/tmp/dwpixmapXXXXXX";
    92649270
    9265    if(!data || !(pixmap = calloc(1,sizeof(struct _hpixmap))))
     9271   if(!data || len < 1 || !(pixmap = calloc(1,sizeof(struct _hpixmap))))
    92669272      return NULL;
    92679273
    92689274   DW_MUTEX_LOCK;
    … …  
    92789284   /* Bail if we couldn't write full file */
    92799285   if(fd == -1 || written != len)
    92809286   {
     9287      if(fd != -1)
     9288         unlink(template);
     9289      free(pixmap);
    92819290      DW_MUTEX_UNLOCK;
    9282       return 0;
     9291      return NULL;
    92839292   }
    92849293#if GTK_MAJOR_VERSION > 1
    92859294   pixbuf = gdk_pixbuf_new_from_file(template, NULL);
     9295   if(!pixbuf)
     9296   {
     9297      unlink(template);
     9298      free(pixmap);
     9299      DW_MUTEX_UNLOCK;
     9300      return NULL;
     9301   }
    92869302   pixmap->width = gdk_pixbuf_get_width(pixbuf);
    92879303   pixmap->height = gdk_pixbuf_get_height(pixbuf);
    92889304   gdk_pixbuf_render_pixmap_and_mask(pixbuf, &pixmap->pixmap, &pixmap->bitmap, 1);
  • gtk3/dw.c

     
    82638263      return 0;
    82648264   }
    82658265   pixmap->pixbuf = gdk_pixbuf_new_from_file(template, NULL);
    8266    pixmap->image = cairo_image_surface_create_from_png(template);
    8267    pixmap->width = gdk_pixbuf_get_width(pixmap->pixbuf);
    8268    pixmap->height = gdk_pixbuf_get_height(pixmap->pixbuf);
     8266   /* Decode once with GdkPixbuf, then create the Cairo drawing surface from
     8267    * that decoded image.  cairo_image_surface_create_from_png() made the
     8268    * previous implementation silently PNG-only for drawing even though
     8269    * GdkPixbuf had successfully decoded GIF, JPEG and other formats. */
     8270   if(pixmap->pixbuf)
     8271   {
     8272      pixmap->image = gdk_cairo_surface_create_from_pixbuf(pixmap->pixbuf, 1, NULL);
     8273      pixmap->width = gdk_pixbuf_get_width(pixmap->pixbuf);
     8274      pixmap->height = gdk_pixbuf_get_height(pixmap->pixbuf);
     8275   }
    82698276   /* remove our temporary file */
    82708277   unlink(template);
     8278   if(!pixmap->pixbuf || !pixmap->image ||
     8279      cairo_surface_status(pixmap->image) != CAIRO_STATUS_SUCCESS)
     8280   {
     8281      if(pixmap->pixbuf)
     8282         g_object_unref(pixmap->pixbuf);
     8283      if(pixmap->image)
     8284         cairo_surface_destroy(pixmap->image);
     8285      free(pixmap);
     8286      DW_MUTEX_UNLOCK;
     8287      return NULL;
     8288   }
    82718289   pixmap->handle = handle;
    82728290   DW_MUTEX_UNLOCK;
    82738291   return pixmap;
  • gtk4/dw.c

     
    90269026   DW_FUNCTION_RETURN_NOTHING;
    90279027}
    90289028
     9029/* Internal function to copy a decoded GdkTexture into a new ARGB32 Cairo surface. */
     9030static cairo_surface_t *_dw_surface_from_texture(GdkTexture *texture)
     9031{
     9032   cairo_surface_t *surface;
     9033
     9034   if(!texture)
     9035      return NULL;
     9036
     9037   surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
     9038                                        gdk_texture_get_width(texture),
     9039                                        gdk_texture_get_height(texture));
     9040   if(cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
     9041   {
     9042      cairo_surface_destroy(surface);
     9043      return NULL;
     9044   }
     9045   gdk_texture_download(texture, cairo_image_surface_get_data(surface),
     9046                        cairo_image_surface_get_stride(surface));
     9047   cairo_surface_mark_dirty(surface);
     9048   return surface;
     9049}
     9050
     9051/* Internal function to copy a decoded GdkPixbuf into a new ARGB32 Cairo surface.
     9052 * Converted by hand to a premultiplied native-endian ARGB32 so it does not
     9053 * depend on any GdkPixbuf/Cairo helper that GTK 4 may deprecate.
     9054 */
     9055static cairo_surface_t *_dw_surface_from_pixbuf(GdkPixbuf *pixbuf)
     9056{
     9057   cairo_surface_t *surface;
     9058   const guchar *src;
     9059   guchar *dst;
     9060   int x, y, width, height, srcstride, dststride, channels;
     9061
     9062   if(!pixbuf || gdk_pixbuf_get_colorspace(pixbuf) != GDK_COLORSPACE_RGB ||
     9063      gdk_pixbuf_get_bits_per_sample(pixbuf) != 8)
     9064      return NULL;
     9065
     9066   channels = gdk_pixbuf_get_n_channels(pixbuf);
     9067   if(channels < 3)
     9068      return NULL;
     9069
     9070   width = gdk_pixbuf_get_width(pixbuf);
     9071   height = gdk_pixbuf_get_height(pixbuf);
     9072   srcstride = gdk_pixbuf_get_rowstride(pixbuf);
     9073   src = gdk_pixbuf_read_pixels(pixbuf);
     9074
     9075   surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
     9076   if(cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
     9077   {
     9078      cairo_surface_destroy(surface);
     9079      return NULL;
     9080   }
     9081   cairo_surface_flush(surface);
     9082   dst = cairo_image_surface_get_data(surface);
     9083   dststride = cairo_image_surface_get_stride(surface);
     9084
     9085   for(y = 0; y < height; y++)
     9086   {
     9087      const guchar *s = src + (size_t)y * srcstride;
     9088      guint32 *d = (guint32 *)(dst + (size_t)y * dststride);
     9089
     9090      for(x = 0; x < width; x++, s += channels)
     9091      {
     9092         guint32 a = (channels == 4) ? s[3] : 255;
     9093         guint32 r = (s[0] * a + 127) / 255;
     9094         guint32 g = (s[1] * a + 127) / 255;
     9095         guint32 b = (s[2] * a + 127) / 255;
     9096
     9097         d[x] = (a << 24) | (r << 16) | (g << 8) | b;
     9098      }
     9099   }
     9100   cairo_surface_mark_dirty(surface);
     9101   return surface;
     9102}
     9103
     9104/* Internal function to create a Cairo surface from image data in memory.
     9105 * GdkTexture is tried first (PNG, JPEG and TIFF are guaranteed), then
     9106 * gdk-pixbuf is used for anything else it has a loader for (GIF, BMP, ICO,
     9107 * WebP with webp-pixbuf-loader, ...).  Animations yield the first frame.
     9108 */
     9109static cairo_surface_t *_dw_surface_from_memory(const char *data, gsize len)
     9110{
     9111   cairo_surface_t *surface = NULL;
     9112   GBytes *bytes = g_bytes_new(data, len);
     9113   GdkTexture *texture = gdk_texture_new_from_bytes(bytes, NULL);
     9114
     9115   g_bytes_unref(bytes);
     9116   if(texture)
     9117   {
     9118      surface = _dw_surface_from_texture(texture);
     9119      g_object_unref(G_OBJECT(texture));
     9120   }
     9121   else
     9122   {
     9123      GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
     9124
     9125      if(loader)
     9126      {
     9127         GError *error = NULL;
     9128         gboolean ok = gdk_pixbuf_loader_write(loader, (const guchar *)data, len, &error);
     9129
     9130         g_clear_error(&error);
     9131         /* Always close the loader, even after a failed write */
     9132         if(!gdk_pixbuf_loader_close(loader, &error))
     9133            ok = FALSE;
     9134         g_clear_error(&error);
     9135         if(ok)
     9136            surface = _dw_surface_from_pixbuf(gdk_pixbuf_loader_get_pixbuf(loader));
     9137         g_object_unref(G_OBJECT(loader));
     9138      }
     9139   }
     9140   return surface;
     9141}
     9142
     9143/* Internal function to create a Cairo surface from an image file,
     9144 * with the same GdkTexture then gdk-pixbuf fallback order.
     9145 */
     9146static cairo_surface_t *_dw_surface_from_file(const char *file)
     9147{
     9148   cairo_surface_t *surface = NULL;
     9149   GdkTexture *texture = gdk_texture_new_from_filename(file, NULL);
     9150
     9151   if(texture)
     9152   {
     9153      surface = _dw_surface_from_texture(texture);
     9154      g_object_unref(G_OBJECT(texture));
     9155   }
     9156   else
     9157   {
     9158      GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(file, NULL);
     9159
     9160      if(pixbuf)
     9161      {
     9162         surface = _dw_surface_from_pixbuf(pixbuf);
     9163         g_object_unref(G_OBJECT(pixbuf));
     9164      }
     9165   }
     9166   return surface;
     9167}
     9168
    90299169/*
    90309170 * Creates a pixmap with given parameters.
    90319171 * Parameters:
    … …  
    90789218   if(filename && *filename && (pixmap = calloc(1,sizeof(struct _hpixmap))))
    90799219   {
    90809220      char *file = alloca(strlen(filename) + 6);
    9081       GdkTexture *texture = NULL;
    90829221
    90839222      strcpy(file, filename);
    90849223
    … …  
    90889227         int i = 0;
    90899228
    90909229         /* Try with various extentions */
    9091          while(_dw_image_exts[i] && !texture)
     9230         while(_dw_image_exts[i] && !pixmap->image)
    90929231         {
    90939232            strcpy(file, filename);
    90949233            strcat(file, _dw_image_exts[i]);
    90959234            if(access(file, 04) == 0)
    9096                texture = gdk_texture_new_from_filename(file, NULL);
     9235               pixmap->image = _dw_surface_from_file(file);
    90979236            i++;
    90989237         }
    90999238      }
    91009239      else
    9101          texture = gdk_texture_new_from_filename(file, NULL);
     9240         pixmap->image = _dw_surface_from_file(file);
    91029241
    9103       if(texture)
     9242      if(pixmap->image)
    91049243      {
    9105          pixmap->width = gdk_texture_get_width(texture);
    9106          pixmap->height = gdk_texture_get_height(texture);
    9107          pixmap->image = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
    9108                                       pixmap->width,
    9109                                       pixmap->height);
    9110          gdk_texture_download(texture, cairo_image_surface_get_data(pixmap->image),
    9111                               cairo_image_surface_get_stride(pixmap->image));
    9112          cairo_surface_mark_dirty(pixmap->image);
     9244         pixmap->width = cairo_image_surface_get_width(pixmap->image);
     9245         pixmap->height = cairo_image_surface_get_height(pixmap->image);
    91139246         pixmap->handle = handle;
    9114          g_object_unref(G_OBJECT(texture));
    91159247      }
    91169248      else
    91179249      {
    … …  
    91419273
    91429274   if(data && len > 0 && (pixmap = calloc(1,sizeof(struct _hpixmap))))
    91439275   {
    9144       int fd, written = -1;
    9145       char template[] = "/tmp/dwpixmapXXXXXX";
    9146       GdkTexture *texture;
    9147 
    9148       /*
    9149        * A real hack; create a temporary file and write the contents
    9150        * of the data to the file
    9151        */
    9152       if((fd = mkstemp(template)) != -1)
     9276      /* Decode directly from memory, no temporary file required */
     9277      if((pixmap->image = _dw_surface_from_memory(data, (gsize)len)))
    91539278      {
    9154          written = write(fd, data, len);
    9155          close(fd);
    9156       }
    9157       /* Bail if we couldn't write full file */
    9158       if(fd != -1 && written == len &&
    9159          (texture = gdk_texture_new_from_filename(template, NULL)))
    9160       {
    9161          pixmap->width = gdk_texture_get_width(texture);
    9162          pixmap->height = gdk_texture_get_height(texture);
    9163          pixmap->image = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
    9164                                                     pixmap->width, pixmap->height);
    9165          gdk_texture_download(texture, cairo_image_surface_get_data(pixmap->image),
    9166                               cairo_image_surface_get_stride(pixmap->image));
    9167          cairo_surface_mark_dirty(pixmap->image);
    9168          /* remove our temporary file */
    9169          unlink(template);
     9279         pixmap->width = cairo_image_surface_get_width(pixmap->image);
     9280         pixmap->height = cairo_image_surface_get_height(pixmap->image);
    91709281         pixmap->handle = handle;
    9171          g_object_unref(G_OBJECT(texture));
    91729282      }
    91739283      else
    91749284      {
    … …  
    92109320   if((pixmap = calloc(1,sizeof(struct _hpixmap))))
    92119321   {
    92129322      GdkTexture *texture = _dw_find_texture((HICN)id, 0, 0);
    9213       pixmap->width = gdk_texture_get_width(texture);
    9214       pixmap->height = gdk_texture_get_height(texture);
    9215       pixmap->image = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
    9216                                                  pixmap->width, pixmap->height);
    9217       gdk_texture_download(texture, cairo_image_surface_get_data(pixmap->image),
    9218                            cairo_image_surface_get_stride(pixmap->image));
    9219       cairo_surface_mark_dirty(pixmap->image);
    9220       pixmap->handle = handle;
    9221       g_object_unref(G_OBJECT(texture));
     9323
     9324      if(texture && (pixmap->image = _dw_surface_from_texture(texture)))
     9325      {
     9326         pixmap->width = gdk_texture_get_width(texture);
     9327         pixmap->height = gdk_texture_get_height(texture);
     9328         pixmap->handle = handle;
     9329      }
     9330      else
     9331      {
     9332         free(pixmap);
     9333         pixmap = 0;
     9334      }
     9335      if(texture)
     9336         g_object_unref(G_OBJECT(texture));
    92229337   }
    92239338   DW_FUNCTION_RETURN_THIS(pixmap);
    92249339}