image_science-1.3.1/0000755000175000017500000000000013635306067013552 5ustar danieldanielimage_science-1.3.1/Rakefile0000444000175000017500000000074213635306067015220 0ustar danieldaniel# -*- ruby -*- require 'rubygems' require 'hoe' Hoe.add_include_dirs("../../RubyInline/dev/lib", "../../ZenTest/dev/lib") Hoe.plugin :seattlerb Hoe.plugin :inline Hoe.plugin :isolate Hoe.spec 'image_science' do developer 'Ryan Davis', 'ryand-ruby@zenspider.com' license "MIT" clean_globs << 'blah*png' << 'images/*_thumb.*' end task :debug => :isolate do file = ENV["F"] ruby "-Ilib bin/image_science_thumb 100 #{file}" end # vim: syntax=Ruby image_science-1.3.1/test/0000755000175000017500000000000013635306067014531 5ustar danieldanielimage_science-1.3.1/test/pix.png0000444000175000017500000000220413635306067016033 0ustar danieldanielPNG  IHDR22] pHYs  6IDATXKoEU=c>X"% D*$P@H `q &+?wgCֶ^*HvgzSUSU=MD"+z zz,"惏<P=GX>k` 8` 8zz0 bx auS"@@Ŷ/ fSMn(k֫[?./G|iOs B`'[jhJ1 >XiοJ\ s"vw[}erf_|7?۸s_<ؓG[?Iޮ0@l֚ۛ֒;mލp" 43 B:*̖\A%.̕HERef{@Ȝ#[`he‚sj5uN@Wkt&ظZ|nlp)9t[@z2B"֫>~҂9p~f jr9f3Zx-"&mSIzpCM> UOӓ`{=W~+]嫴IiZ8}$_,ZA$>DM7Gj &(+gAQg_ `BBjTW`q:Uq1:uiќ MKug8Q 6*#)'Q5 q-uQm5x㏍\\ZAc$&PdP\ !]fWTbD1k;nICybFS1IR) E˘xf:TLsy<7 -HDZXZɞ8ȍ{&Ǭj Kմ:mBEMLyng/vK)&X&[ I?O[|G{f'1|K6*ik;aP IƝ(SW⸞3=<ʵӥm^1T%ICM&m?/ߴ~1r-({qFW1fK9H䀲r+ =’lXd1Z_|B:摩$rd1ƚƉ+qkjbʑUf{$-Ttm+Ya%YVw{mC>\0 3kXf)xrȱ8oq~*̿Nab(S"x?o(BUpؚj}*r9Ǝ.0Nx)үj5RwiS+5pO !qv4Ry~֊1;u-?g!qxa}2y5XK6Gy&[u)nf$ƒ$w˴6?ےDX*1;<tW|\sjS/|/lՕuۿ\I1iR4^f)%r4'!ŤC/8%pLT<._cF2U %\FUԵ,lb*PN' 58OIGݔ\\g&+6%e'{x:;[m4kq&mᅰXnmuK W+s.VIeY>|>iw:?UoXxGCKyX@yʂ:tosGUt4$B[EZDqZHFEWX ñVՙ%cD$UH8TDUEUzr/~6KC/g:rm~'h@?N1E쿫y._/^^~dpsӏQn>T\ޤc:QES]jpz{6im{~rG%^+_XA%S*p y^xW_ L. h ? w : h) self.resize((w * scale).round, (h * scale).round) do |image| yield image end end ## # Creates a square thumbnail of the image cropping the longest edge # to match the shortest edge, resizes to +size+, and yields the new # image. def cropped_thumbnail(size) # :yields: image w, h = width, height l, t, r, b, half = 0, 0, w, h, (w - h).abs / 2 l, r = half, half + h if w > h t, b = half, half + w if h > w with_crop(l, t, r, b) do |img| img.thumbnail(size) do |thumb| yield thumb end end end inline do |builder| %w[/opt/local /usr/local].each do |dir| if File.directory? "#{dir}/include" then builder.add_compile_flags "-I#{dir}/include" builder.add_link_flags "-L#{dir}/lib" end end builder.add_link_flags "-lfreeimage" unless RUBY_PLATFORM =~ /mswin/ builder.add_link_flags "-lfreeimage" # TODO: detect PPC builder.add_link_flags "-lstdc++" # only needed on PPC for some reason else builder.add_link_flags "freeimage.lib" end builder.include '"FreeImage.h"' builder.prefix <<-"END" #define GET_BITMAP(name) Data_Get_Struct(self, FIBITMAP, (name)); if (!(name)) rb_raise(rb_eTypeError, "Bitmap has already been freed"); static ID err_key; /* used as thread-local key */ static void clear_error(void); static void raise_error(void); END builder.prefix <<-"END" VALUE unload(VALUE self) { FIBITMAP *bitmap; GET_BITMAP(bitmap); FreeImage_Unload(bitmap); DATA_PTR(self) = NULL; clear_error(); return Qnil; } END builder.prefix <<-"END" VALUE wrap_and_yield(FIBITMAP *image, VALUE self, FREE_IMAGE_FORMAT fif) { unsigned int self_is_class = rb_type(self) == T_CLASS; VALUE klass = self_is_class ? self : CLASS_OF(self); VALUE type = self_is_class ? INT2FIX(fif) : rb_iv_get(self, "@file_type"); VALUE obj = Data_Wrap_Struct(klass, NULL, NULL, image); rb_iv_set(obj, "@file_type", type); return rb_ensure(rb_yield, obj, unload, obj); } END builder.prefix <<-"END" void copy_icc_profile(VALUE self, FIBITMAP *from, FIBITMAP *to) { FREE_IMAGE_FORMAT fif = FIX2INT(rb_iv_get(self, "@file_type")); if (fif != FIF_PNG && FreeImage_FIFSupportsICCProfiles(fif)) { FIICCPROFILE *profile = FreeImage_GetICCProfile(from); if (profile && profile->data) { FreeImage_CreateICCProfile(to, profile->data, profile->size); } } } END # we defer raising the error until it we find a safe point to do so # We cannot use rb_ensure in these cases because FreeImage may internally # make allocations via which our code will never see. builder.prefix <<-"END" void FreeImageErrorHandler(FREE_IMAGE_FORMAT fif, const char *message) { VALUE err = rb_sprintf( "FreeImage exception for type %s: %s", (fif == FIF_UNKNOWN) ? "???" : FreeImage_GetFormatFromFIF(fif), message); rb_thread_local_aset(rb_thread_current(), err_key, err); } END # do not call this until necessary variables are wrapped up for GC # otherwise there will be leaks builder.prefix <<-"END" __attribute__((__noreturn__)) static void raise_error(void) { VALUE err = rb_thread_local_aref(rb_thread_current(), err_key); if (NIL_P(err)) { rb_raise(rb_eRuntimeError, "FreeImage exception"); } else { rb_thread_local_aset(rb_thread_current(), err_key, Qnil); rb_raise(rb_eRuntimeError, "%s", StringValueCStr(err)); } } END builder.prefix <<-"END" static void clear_error(void) { if (!NIL_P(rb_thread_local_aref(rb_thread_current(), err_key))) { rb_thread_local_aset(rb_thread_current(), err_key, Qnil); } } END builder.prefix <<-"END" FIBITMAP* ReOrient(FIBITMAP *bitmap) { FITAG *tagValue = NULL; FIBITMAP *oldBitmap = bitmap; FreeImage_GetMetadata(FIMD_EXIF_MAIN, bitmap, "Orientation", &tagValue); switch (tagValue == NULL ? 0 : *((short *) FreeImage_GetTagValue(tagValue))) { case 6: bitmap = FreeImage_RotateClassic(bitmap, 270); break; case 3: bitmap = FreeImage_RotateClassic(bitmap, 180); break; case 8: bitmap = FreeImage_RotateClassic(bitmap, 90); break; default: bitmap = FreeImage_Clone(bitmap); break; } FreeImage_Unload(oldBitmap); return bitmap; } END builder.add_to_init "FreeImage_SetOutputMessage(FreeImageErrorHandler);" builder.add_to_init 'err_key = rb_intern("__FREE_IMAGE_ERROR");' builder.c_singleton <<-"END" VALUE with_image(char * input) { FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; int flags; fif = FreeImage_GetFileType(input, 0); if (fif == FIF_UNKNOWN) fif = FreeImage_GetFIFFromFilename(input); if ((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(fif)) { FIBITMAP *bitmap; VALUE result = Qnil; flags = fif == FIF_JPEG ? JPEG_ACCURATE : 0; if (!(bitmap = FreeImage_Load(fif, input, flags))) raise_error(); if (!(bitmap = ReOrient(bitmap))) raise_error(); result = wrap_and_yield(bitmap, self, fif); return result; } rb_raise(rb_eTypeError, "Unknown file format"); return Qnil; } END builder.c_singleton <<-"END" VALUE with_image_from_memory(VALUE image_data) { FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; BYTE *image_data_ptr; DWORD image_data_length; FIMEMORY *stream; FIBITMAP *bitmap = NULL; VALUE result = Qnil; int flags; Check_Type(image_data, T_STRING); image_data_ptr = (BYTE*)RSTRING_PTR(image_data); image_data_length = (DWORD)RSTRING_LEN(image_data); stream = FreeImage_OpenMemory(image_data_ptr, image_data_length); if (NULL == stream) { rb_raise(rb_eTypeError, "Unable to open image_data"); } fif = FreeImage_GetFileTypeFromMemory(stream, 0); if ((fif == FIF_UNKNOWN) || !FreeImage_FIFSupportsReading(fif)) { FreeImage_CloseMemory(stream); rb_raise(rb_eTypeError, "Unknown file format"); } flags = fif == FIF_JPEG ? JPEG_ACCURATE : 0; bitmap = FreeImage_LoadFromMemory(fif, stream, flags); FreeImage_CloseMemory(stream); if (!bitmap) raise_error(); if (!(bitmap = ReOrient(bitmap))) raise_error(); result = wrap_and_yield(bitmap, self, fif); return result; } END builder.c <<-"END" VALUE with_crop(int l, int t, int r, int b) { FIBITMAP *copy, *bitmap; GET_BITMAP(bitmap); if (!(copy = FreeImage_Copy(bitmap, l, t, r, b))) raise_error(); copy_icc_profile(self, bitmap, copy); return wrap_and_yield(copy, self, 0); } END builder.c <<-"END" int height() { FIBITMAP *bitmap; GET_BITMAP(bitmap); return FreeImage_GetHeight(bitmap); } END builder.c <<-"END" int width() { FIBITMAP *bitmap; GET_BITMAP(bitmap); return FreeImage_GetWidth(bitmap); } END builder.c <<-"END" VALUE resize(int w, int h) { FIBITMAP *bitmap, *image; if (w <= 0) rb_raise(rb_eArgError, "Width <= 0"); if (h <= 0) rb_raise(rb_eArgError, "Height <= 0"); GET_BITMAP(bitmap); image = FreeImage_Rescale(bitmap, w, h, FILTER_CATMULLROM); if (!image) raise_error(); copy_icc_profile(self, bitmap, image); return wrap_and_yield(image, self, 0); } END builder.c <<-"END" VALUE rotate(int angle) { FIBITMAP *bitmap, *image; if ((angle % 45) != 0) rb_raise(rb_eArgError, "Angle must be 45 degree skew"); GET_BITMAP(bitmap); image = FreeImage_RotateClassic(bitmap, angle); if (image) { copy_icc_profile(self, bitmap, image); return wrap_and_yield(image, self, 0); } return Qnil; } END builder.c <<-"END" VALUE save(char * output) { int flags; FIBITMAP *bitmap; FREE_IMAGE_FORMAT fif = FreeImage_GetFIFFromFilename(output); if (fif == FIF_UNKNOWN) fif = FIX2INT(rb_iv_get(self, "@file_type")); if ((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsWriting(fif)) { BOOL result = 0, unload = 0; GET_BITMAP(bitmap); flags = fif == FIF_JPEG ? JPEG_QUALITYSUPERB : 0; if (fif == FIF_PNG) FreeImage_DestroyICCProfile(bitmap); if (fif == FIF_JPEG && FreeImage_GetBPP(bitmap) != 24) { bitmap = FreeImage_ConvertTo24Bits(bitmap), unload = 1; // sue me if (!bitmap) raise_error(); } result = FreeImage_Save(fif, bitmap, output, flags); if (unload) FreeImage_Unload(bitmap); if (!result) raise_error(); return Qtrue; } rb_raise(rb_eTypeError, "Unknown file format"); return Qnil; } END end end image_science-1.3.1/bench.rb0000555000175000017500000000247213635306067015164 0ustar danieldaniel#!/usr/local/bin/ruby -w require 'benchmark' require 'rubygems' require 'image_science' max = (ARGV.shift || 100).to_i ext = ARGV.shift || "png" file = "blah_big.#{ext}" if RUBY_PLATFORM =~ /darwin/ then # how fucking cool is this??? puts "taking screenshot for thumbnailing benchmarks" system "screencapture -SC #{file}" else abort "You need to plonk down #{file} or buy a mac" end unless test ?f, "#{file}" ImageScience.with_image(file.sub(/#{ext}$/, 'png')) do |img| img.save(file) end if ext != "png" puts "# of iterations = #{max}" Benchmark::bm(20) do |x| x.report("null_time") { for i in 0..max do # do nothing end } x.report("cropped") { for i in 0..max do ImageScience.with_image(file) do |img| img.cropped_thumbnail(100) do |thumb| thumb.save("blah_cropped.#{ext}") end end end } x.report("proportional") { for i in 0..max do ImageScience.with_image(file) do |img| img.thumbnail(100) do |thumb| thumb.save("blah_thumb.#{ext}") end end end } x.report("resize") { for i in 0..max do ImageScience.with_image(file) do |img| img.resize(200, 200) do |resize| resize.save("blah_resize.#{ext}") end end end } end # File.unlink(*Dir["blah*#{ext}"]) image_science-1.3.1/bin/0000755000175000017500000000000013635306067014322 5ustar danieldanielimage_science-1.3.1/bin/image_science_thumb0000555000175000017500000000132513635306067020221 0ustar danieldaniel#!/usr/local/bin/ruby -ws $s ||= false abort "#{File.basename $0} max_length files..." unless ARGV.size > 1 require 'rubygems' require 'image_science' max_length = ARGV.shift.to_i msg = $s ? :cropped_thumbnail : :thumbnail ARGV.each do |file| begin result = ImageScience.with_image file do |img| begin img.send(msg, max_length) do |thumb| # add _thumb and switch from gif to png. Really. gif just sucks. out = file.sub(/(\.[^\.]+)$/, '_thumb\1').sub(/gif$/, 'png') thumb.save(out) end rescue => e warn "Exception thumbnailing #{file}: #{e}" end end p file => result rescue => e warn "Exception opening #{file}: #{e}" end end image_science-1.3.1/Manifest.txt0000444000175000017500000000023513635306067016057 0ustar danieldanielHistory.txt Manifest.txt README.txt Rakefile bench.rb bin/image_science_thumb lib/image_science.rb test/pix.png test/portrait.jpg test/test_image_science.rb image_science-1.3.1/History.txt0000444000175000017500000000567313635306067015765 0ustar danieldaniel=== 1.3.1 / 2019-12-14 * 1 bug fix: * Added noreturn attribute to raise_error since it always calls rb_raise. === 1.3.0 / 2016-05-16 * 1 minor enhancement: * Add rotate method. (bhenderson) * 1 bug fix: * Fixed error-raising behaviour. (omp) === 1.2.6 / 2014-01-15 * 1 bug fix: * Defer raising exception until wrapped by rb_ensure. (nocode) === 1.2.5 / 2013-06-20 * 1 bug fix: * Fixed memory leak in the ReOrient function. (Blargel/abnerqian) === 1.2.4 / 2012-11-23 * 1 minor enhancement: * Extract out orientation logic and add it to with_image_from_memory. (bhenderson) === 1.2.3 / 2012-05-07 * 2 bug fixes: * Fixed thumbnail float imprecision errors. (jdelStrother) * Switched to using tmpdir for INLINEDIR to avoid failures in CI environments w/o homedirs === 1.2.2 / 2012-03-13 * 1 minor enhancement: * Extended build to look for homebrew setup (hcatlin) * 2 bug fixes: * Fixed 1.9 warnings, clang warnings, etc... * Fixed compilation error when ruby's config mandates C89 style decls. (nocode) === 1.2.1 / 2009-08-14 * 2 minor enhancements: * Added luis' patches to make it build properly on windows. * with_image now raises on missing/bad files. === 1.2.0 / 2009-06-23 * 7 minor enhancements: * Moved quick_thumb to bin/image_science_thumb and properly added. * Added -s (square) flag to bin/image_science_thumb * Added autorotating on image load. (choonkeat) * Added ruby_inline to clean globs * Added with_image_from_memory. (sumbach) * Switched to minitest. * Updated rakefile for now hoe capabilities. * 3 bug fixes: * Check and convert to 24 BPP if save type is jpg. Caused by 32bpp png to jpg. * Fixed 1.9isms * Fixed BMP support. Tweaked whitespace. === 1.1.3 / 2007-05-30 * 2 minor enhancements: * Added quick_thumb as an example to look at. * Error handler doesn't raise by default. Raises if $DEBUG==true. === 1.1.2 / 2007-04-18 * 2 bug fixes: * reports bad height/width values for resize * explicitly removes ICC color profiles from PNGs (bug in freeimage). === 1.1.1 / 2007-03-08 * 5 minor enhancements: * Added error handler that raises with information about what went wrong. * thumbnail is now pure ruby, everything now uses resize. * Produces cleaner JPEG files, with a small cost to file size/speed. * resize now uses Catmull-Rom spline filter for better quality. * resize copies existing ICC Profile to thumbnail, producing better color. * ICC Profile NOT copied for PNG as it seems to be buggy. * 1 bug fix: * Fixed rdoc === 1.1.0 / 2007-01-05 * 3 major enhancements: * Added resize(width, height) * Added save(path) * All thumbnail and resize methods yield instead of saving directly. * 1 minor enhancement: * Will now try to use FreeImage from ports if /opt/local exists. * 2 bug fixes: * Fixed the linker issue on PPC. * Rakefile will now clean the image files created by bench.rb === 1.0.0 / 2006-12-01 * 1 major enhancement * Birthday! image_science-1.3.1/image_science.gemspec0000644000175000017500000000723413635306067017700 0ustar danieldaniel######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: image_science 1.3.1 ruby lib Gem::Specification.new do |s| s.name = "image_science".freeze s.version = "1.3.1" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "bug_tracker_uri" => "https://github.com/seattlerb/image_science/issues", "homepage_uri" => "https://github.com/seattlerb/image_science" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Ryan Davis".freeze] s.cert_chain = ["-----BEGIN CERTIFICATE-----\nMIIDPjCCAiagAwIBAgIBBDANBgkqhkiG9w0BAQsFADBFMRMwEQYDVQQDDApyeWFu\nZC1ydWJ5MRkwFwYKCZImiZPyLGQBGRYJemVuc3BpZGVyMRMwEQYKCZImiZPyLGQB\nGRYDY29tMB4XDTE5MTIxMzAwMDIwNFoXDTIwMTIxMjAwMDIwNFowRTETMBEGA1UE\nAwwKcnlhbmQtcnVieTEZMBcGCgmSJomT8ixkARkWCXplbnNwaWRlcjETMBEGCgmS\nJomT8ixkARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALda\nb9DCgK+627gPJkB6XfjZ1itoOQvpqH1EXScSaba9/S2VF22VYQbXU1xQXL/WzCkx\ntaCPaLmfYIaFcHHCSY4hYDJijRQkLxPeB3xbOfzfLoBDbjvx5JxgJxUjmGa7xhcT\noOvjtt5P8+GSK9zLzxQP0gVLS/D0FmoE44XuDr3iQkVS2ujU5zZL84mMNqNB1znh\nGiadM9GHRaDiaxuX0cIUBj19T01mVE2iymf9I6bEsiayK/n6QujtyCbTWsAS9Rqt\nqhtV7HJxNKuPj/JFH0D2cswvzznE/a5FOYO68g+YCuFi5L8wZuuM8zzdwjrWHqSV\ngBEfoTEGr7Zii72cx+sCAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAw\nHQYDVR0OBBYEFEfFe9md/r/tj/Wmwpy+MI8d9k/hMA0GCSqGSIb3DQEBCwUAA4IB\nAQCkkcHqAa6IKLYGl93rn78J3L+LnqyxaA059n4IGMHWN5bv9KBQnIjOrpLadtYZ\nvhWkunWDKdfVapBEq5+T4HzqnsEXC3aCv6JEKJY6Zw7iSzl0M8hozuzRr+w46wvT\nfV2yTN6QTVxqbMsJJyjosks4ZdQYov2zdvQpt1HsLi+Qmckmg8SPZsd+T8uiiBCf\nb+1ORSM5eEfBQenPXy83LZcoQz8i6zVB4aAfTGGdhxjoMGUEmSZ6xpkOzmnGa9QK\nm5x9IDiApM+vCELNwDXXGNFEnQBBK+wAe4Pek8o1V1TTOxL1kGPewVOitX1p3xoN\nh7iEjga8iM1LbZUfiISZ+WrB\n-----END CERTIFICATE-----\n".freeze] s.date = "2019-12-15" s.description = "ImageScience is a clean and happy Ruby library that generates\nthumbnails -- and kicks the living crap out of RMagick. Oh, and it\ndoesn't leak memory like a sieve. :)\n\nFor more information including build steps, see http://seattlerb.rubyforge.org/".freeze s.email = ["ryand-ruby@zenspider.com".freeze] s.executables = ["image_science_thumb".freeze] s.extra_rdoc_files = ["History.txt".freeze, "Manifest.txt".freeze, "README.txt".freeze] s.files = ["History.txt".freeze, "Manifest.txt".freeze, "README.txt".freeze, "Rakefile".freeze, "bench.rb".freeze, "bin/image_science_thumb".freeze, "lib/image_science.rb".freeze, "test/pix.png".freeze, "test/portrait.jpg".freeze, "test/test_image_science.rb".freeze] s.homepage = "https://github.com/seattlerb/image_science".freeze s.licenses = ["MIT".freeze] s.rdoc_options = ["--main".freeze, "README.txt".freeze] s.rubygems_version = "2.5.2.1".freeze s.summary = "ImageScience is a clean and happy Ruby library that generates thumbnails -- and kicks the living crap out of RMagick".freeze if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_runtime_dependency(%q.freeze, ["~> 3.9"]) s.add_development_dependency(%q.freeze, ["~> 3.20"]) s.add_development_dependency(%q.freeze, ["< 7", ">= 4.0"]) else s.add_dependency(%q.freeze, ["~> 3.9"]) s.add_dependency(%q.freeze, ["~> 3.20"]) s.add_dependency(%q.freeze, ["< 7", ">= 4.0"]) end else s.add_dependency(%q.freeze, ["~> 3.9"]) s.add_dependency(%q.freeze, ["~> 3.20"]) s.add_dependency(%q.freeze, ["< 7", ">= 4.0"]) end end