pure_jpeg 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66b5d6fe1b663128f62aae8111b55a9b2ddbe9739d501fcf1146c459286433da
4
- data.tar.gz: 02ae6cdc25f520221fee4adfe9d6070c4a975602e5de2b34a0b9ab8b4e005829
3
+ metadata.gz: 7a0015f811a2250264bfa73727aa37af15fee1af10e4897243045f2f4f54ae07
4
+ data.tar.gz: 5085ab8c4bd1d9941c94e116b39ea7ca38f658fdf0e48523cae65fc913f765d0
5
5
  SHA512:
6
- metadata.gz: f476b8fec25f1f0402f297d534f52887aed90778ddf1217668a0889755856436dae8a0d8e4e9d648bc2a33879165b32fa0560e5b506549467c21a648fd6ecf29
7
- data.tar.gz: 5438c161519149458fad8cd28dea1f9f073a2646c5f91f619a357d375b77456ed5f366dd3ed78e75f5c0d2cbe4156f4ec8a5a6dbaa9e19d86198dcd8a2fcacfc
6
+ metadata.gz: b507962b2ec9650e743b365b8ace5ddb2a9c1d04de126206ac6062c9da46001dc3e8a1f04df356187b3a2458f3383625d864fe12ee0b3c5e3df589755aa540aa
7
+ data.tar.gz: 7bf019ea4702bbd7379ad3a1d295acaadd47b185815461b810c08c33299248235b326e9d0cdcfbebe23646f32014487c55212517a9e8a246d4fd5d40891eb62f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.1
4
+
5
+ Fixes:
6
+
7
+ - Fixed shared `Pixel` instance bug in decoder that could corrupt pixel data
8
+ - Encoder validates return values from `quantization_modifier` blocks
9
+
3
10
  ## 0.3.0
4
11
 
5
12
  New features:
data/README.md CHANGED
@@ -198,12 +198,13 @@ Possible future improvements: AAN/fixed-point DCT (but it's a LOT of work), ICC
198
198
 
199
199
  ## Performance
200
200
 
201
- On a 1024x1024 image (Ruby 4.0.1 on my M1 Max):
201
+ On a 1024x1024 image (Ruby 4.0.1 on my M5):
202
202
 
203
203
  | Operation | Time |
204
204
  |-----------|------|
205
- | Encode (color, q85) | ~1.7s |
206
- | Decode (color) | ~1.8s |
205
+ | Encode (color, q85) | ~1.2s |
206
+ | Decode (baseline) | ~1.2s |
207
+ | Decode (progressive) | ~1.3s |
207
208
 
208
209
  Both the encoder and decoder use a separable DCT with a precomputed cosine matrix and reuse all per-block buffers to minimize GC pressure. Pixel data is stored as packed integers internally to avoid per-pixel object allocation.
209
210
 
@@ -212,7 +213,7 @@ Both the encoder and decoder use a separable DCT with a precomputed cosine matri
212
213
  ```
213
214
  bundle install
214
215
  rake test # run the test suite
215
- rake benchmark # benchmark encoding (3 runs against examples/a.png)
216
+ rake benchmark # benchmark encoding and decoding (3 runs each)
216
217
  rake profile # CPU profile with StackProf (requires the stackprof gem)
217
218
  ```
218
219
 
@@ -222,7 +223,7 @@ rake profile # CPU profile with StackProf (requires the stackprof gem)
222
223
 
223
224
  **I have read all of the code produced up to v0.2.0.** The algorithms are above my paygrade, but I'm OK with what has been produced, and I manually fixed a variety of stylistic things along the way. For example, CC seems to like wrapping entire functions in `if` statements rather than bailing on the opposite condition. *Later update: I have not read the ICC and optimized Huffman code yet, but it is heavily tested.*
224
225
 
225
- **CC needed a lot of guidance.** Its initial JPEG algorithm was somewhat naive and output odd looking JPEGs akin to those of my Kodak digital camera from 2001. After some back and forth and image comparisons, we figured out it was doing the quantization entirely wrong (specifically not using the zigzag approach during quanitization but just going in raster order). I *like* this aesthetic, but fixed it up so that it works as a generally usable JPEG library, while adding ways to customize things so you can recreate the effect, if preferred (see `CREATIVE.md` for more on that).
226
+ **CC needed a lot of guidance.** Its initial JPEG algorithm was somewhat naive and output odd looking JPEGs akin to those of my [Casio QV-10 digital camera](https://medium.com/people-gadgets/the-gadget-we-miss-the-casio-qv-10-digital-camera-c25ab786ce49) from the late 1990s. After some back and forth and image comparisons, we figured out it was doing the quantization entirely wrong (specifically not using the zigzag approach during quanitization but just going in raster order). I *like* this aesthetic, but fixed it up so that it works as a generally usable JPEG library, while adding ways to customize things so you can recreate the effect, if preferred (see `CREATIVE.md` for more on that).
226
227
 
227
228
  **CC is lazy.** The initial implementation was VERY SLOW. It took 15 seconds to turn a 1024x1024 PNG into a JPEG, so we went down the profiling rabbit hole and found many optimizations to make it ~6x faster. CC is poor at considering the role of Ruby's GC when implementing low level algorithms and needs some prodding to make the correct optimizations. CC is also lazy to the point of recommending that you just use another language (e.g. Go or Rust) rather than do a pure Ruby version of something - despite it being possible with some extra work.
228
229
 
@@ -76,17 +76,27 @@ module PureJPEG
76
76
 
77
77
  def build_lum_qtable
78
78
  table = @luminance_table || Quantization.scale_table(Quantization::LUMINANCE_BASE, quality)
79
- table = @quantization_modifier.call(table, :luminance) if @quantization_modifier
79
+ table = apply_quantization_modifier(table, :luminance) if @quantization_modifier
80
80
  table
81
81
  end
82
82
 
83
83
  def build_chr_qtable
84
84
  table = @chrominance_table || Quantization.scale_table(Quantization::CHROMINANCE_BASE, @chroma_quality)
85
- table = @quantization_modifier.call(table, :chrominance) if @quantization_modifier
85
+ table = apply_quantization_modifier(table, :chrominance) if @quantization_modifier
86
86
  table
87
87
  end
88
88
 
89
+ def apply_quantization_modifier(table, channel)
90
+ modified = @quantization_modifier.call(table, channel)
91
+ validate_qtable!(modified, "quantization_modifier result for #{channel}")
92
+ modified
93
+ end
94
+
89
95
  def validate_qtable!(table, name)
96
+ unless table.respond_to?(:length) && table.respond_to?(:all?)
97
+ raise ArgumentError, "#{name} must be a 64-element array of integers between 1 and 255"
98
+ end
99
+
90
100
  raise ArgumentError, "#{name} must have exactly 64 elements (got #{table.length})" unless table.length == 64
91
101
  unless table.all? { |v| v.is_a?(Integer) && v >= 1 && v <= 255 }
92
102
  raise ArgumentError, "#{name} elements must be integers between 1 and 255"
@@ -27,8 +27,7 @@ module PureJPEG
27
27
  def initialize(width, height, &block)
28
28
  @width = width
29
29
  @height = height
30
- black = Pixel.new(0, 0, 0)
31
- @pixels = Array.new(width * height, black)
30
+ @pixels = Array.new(width * height) { Pixel.new(0, 0, 0) }
32
31
 
33
32
  if block
34
33
  height.times do |y|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PureJPEG
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pure_jpeg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Cooper
@@ -86,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
86
  - !ruby/object:Gem::Version
87
87
  version: '0'
88
88
  requirements: []
89
- rubygems_version: 3.6.9
89
+ rubygems_version: 4.0.3
90
90
  specification_version: 4
91
91
  summary: Pure Ruby JPEG encoder and decoder
92
92
  test_files: []