dimensions icon indicating copy to clipboard operation
dimensions copied to clipboard

WebP support

Open ai opened this issue 12 years ago • 3 comments

WebP is really cool format with support in Chrome, Opera and planning to support in Firefox.

Now we can send special CSS only for this browsers and reduce image size to 25 %.

ai avatar Jun 07 '13 07:06 ai

Yes. Please do implement this and send a pull request. ;)

sstephenson avatar Jun 07 '13 14:06 sstephenson

I made a quick hackish solution which you could improve and send a pull request. It uses webp-ffi gem.

require 'dimensions/jpeg_scanner'

module Dimensions
  class Reader
    GIF_HEADER    = [0x47, 0x49, 0x46, 0x38]
    PNG_HEADER    = [0x89, 0x50, 0x4E, 0x47]
    JPEG_HEADER   = [0xFF, 0xD8, 0xFF]
    TIFF_HEADER_I = [0x49, 0x49, 0x2A, 0x00]
    TIFF_HEADER_M = [0x4D, 0x4D, 0x00, 0x2A]
    RIFF_HEADER   = [0x52, 0x49, 0x46, 0x46]
    WEBP_HEADER   = [0x57, 0x45, 0x42, 0x50]

    attr_reader :type, :width, :height, :angle

    def initialize
      @process = :determine_type
      @type    = nil
      @width   = nil
      @height  = nil
      @angle   = nil
      @size    = 0
      @data    = ""
      @data.force_encoding("BINARY") if @data.respond_to?(:force_encoding)
    end

    def <<(data)
      if @process
        @data << data
        @size = @data.length
        process
      end
    end

    def process(process = @process)
      send(@process) if @process = process
    end

    def determine_type
      if @size >= 4
        bytes = @data.unpack("C4")

        if match_header(GIF_HEADER, bytes)
          @type = :gif
        elsif match_header(PNG_HEADER, bytes)
          @type = :png
        elsif match_header(JPEG_HEADER, bytes)
          @type = :jpeg
        elsif match_header(TIFF_HEADER_I, bytes) || match_header(TIFF_HEADER_M, bytes)
          @type = :tiff
        elsif match_header(RIFF_HEADER, bytes)
          bytes = @data.unpack("C12")[8..-1]

          if match_header(WEBP_HEADER, bytes)
            @type = :webp
          end
        end

        process @type ? :"extract_#{type}_dimensions" : nil
      end
    end

    def extract_gif_dimensions
      if @size >= 10
        @width, @height = @data.unpack("x6v2")
        process nil
      end
    end

    def extract_png_dimensions
      if @size >= 24
        @width, @height = @data.unpack("x16N2")
        process nil
      end
    end

    def extract_jpeg_dimensions
      scanner = JpegScanner.new(@data)
      if scanner.scan
        @width  = scanner.width
        @height = scanner.height
        @angle  = scanner.angle

        if @angle == 90 || @angle == 270
          @width, @height = @height, @width
        end

        process nil
      end
    rescue JpegScanner::ScanError
    end

    def extract_tiff_dimensions
      scanner = TiffScanner.new(@data)
      if scanner.scan
        @width  = scanner.width
        @height = scanner.height
        process nil
      end
    rescue TiffScanner::ScanError
    end

    def extract_webp_dimensions
      @width, @height = WebP.webp_size(@data)
      process nil
    end

    def match_header(header, bytes)
      bytes[0, header.length] == header
    end
  end
end

thehappycoder avatar Nov 15 '15 10:11 thehappycoder

The purpose of dimensions library was originally to be pure-Ruby and without dependencies. I think that adding a WebP dependency is unnacceptabie, especially because it has native extensions.

It would be great if you sent a PR for WebP support, but it would have to be implemented manually.

mislav avatar Nov 15 '15 12:11 mislav