{"course":{"slug":"image-editing","title":"Editing an Image","url":"https://addaly.com/learn/image-editing","level":"beginner","track":"create"},"lesson":{"slug":"inside-a-jpeg","title":"Eight by eight, and what the encoder threw away","url":"https://addaly.com/learn/image-editing/inside-a-jpeg","position":7,"of":78,"minutes":9,"answer":"JPEG loses data at exactly one step, rounding the 8×8 transform coefficients, so blocking, ringing and colour fringes each map to a specific stage of the encoder and tell you whether to change the quality.","keyIdea":"JPEG loses data at exactly one step, rounding the 8×8 transform coefficients, so blocking, ringing and colour fringes each map to a specific stage of the encoder and tell you whether to change the quality, the subsampling or the format."},"body":{"format":"markdown","text":"## The encoder's four steps\n\nJPEG has been the default photographic format since 1992 and the reason it works is worth twenty minutes of your life, because every artefact you will ever have to argue with a client about comes from one of four steps.\n\n**1. Convert to brightness and two colour differences.** RGB becomes Y (luminance) plus Cb and Cr (blue-difference and red-difference). Your eye is far more sensitive to brightness detail than colour detail, and this split lets the encoder treat them differently.\n\n**2. Throw away colour resolution.** In 4:2:0 subsampling, the near-universal default, the Cb and Cr channels are stored at half resolution in each direction, so one colour sample covers a 2×2 block of pixels. That is three quarters of the colour data discarded immediately, and on a face or a landscape you cannot see it. On a thin red line on white you can, which is the next section.\n\n**3. Split into 8×8 blocks and transform each one.** Each block goes through a discrete cosine transform, which re-describes those 64 values as 64 coefficients: one average level plus 63 patterns of increasing fineness. Nothing is lost yet; this is a lossless re-description.\n\n**4. Quantise.** Each coefficient is divided by a number from a quantisation table and rounded. The fine-detail coefficients get divided by large numbers, so most of them round to zero. **This is the lossy step, and it is the only one.** The quality slider does nothing except scale that table: quality 90 uses small divisors, quality 40 uses large ones.\n\nThe zeros then compress to almost nothing, which is where the file size comes from.\n\n## Reading an artefact backwards\n\nKnowing the four steps means you can look at a bad file and say which step caused it.\n\n> Figure: Four steps, and the artefact each one leaves behind. Steps: 1. Split brightness from colour (RGB becomes Y, Cb and Cr. Nothing is lost here; the split exists so the next step can treat them differently.); 2. Store colour at half resolution (4:2:0 subsampling discards three quarters of the colour data. Invisible on a face. This is where red type on white gets its soft dirty fringe, and the quality slider is downstream of it.); 3. Transform each 8 by 8 block (Sixty-four values become sixty-four coefficients: one average level and 63 patterns of increasing fineness. Still lossless: a re-description, not a reduction.); 4. Quantise, the only lossy step (Divide each coefficient and round. Too coarse and only the block average survives, which is blocking. Truncating the sum at a hard edge leaves overshoot, which is the ringing around black text.); 5. Open, edit, save again (The decoded values no longer sit on the new grid, so every round rounds again. After thirty the edges crawl and the flat areas terrace, and a rotate in an ordinary editor costs a whole generation.). Read an artefact backwards and it tells you which control to reach for. Blocking in a sky is the quality slider. A dirty fringe on red type is the subsampling, and raising the quality cannot touch it because that step happens earlier. Both are reasons the file should have been a PNG or a WebP.\n\n**Blocking**: visible 8×8 squares, usually in sky or shadow. Quantisation was so coarse that only the block's average level survived. Cause: quality set too low, or an image re-saved many times.\n\n**Ringing, also called mosquito noise**: a shimmer of light and dark just outside a hard edge, most obvious around black text on white. The encoder is describing a step edge as a sum of smooth cosine waves; truncating that sum leaves overshoot on either side. This is a mathematical property of the transform, not a bug, and it is why JPEG is wrong for screenshots, diagrams and text.\n\n**Colour bleed and dirty fringes on saturated text**: step 2. Red type on white is a hard edge in a channel that was stored at half resolution. Raising the quality slider does not fix it, because subsampling happens before quantisation. Some encoders let you force 4:4:4 (no subsampling); Photoshop does it automatically at quality 7 and above in *Save for Web*, and `cjpeg -sample 1x1` does it explicitly. Or just use PNG or WebP, which is the real answer.\n\n**Generation loss**: open a JPEG, make one small edit, save at quality 85, repeat. After 30 rounds the edges crawl and the flat areas terrace. What happens is that each decode lands on values that do not sit neatly on the new quantisation grid, so each re-encode rounds again. It does eventually converge to a stable state, but well below where it started. Re-saving *without any edit* in a tool that re-encodes still loses a little each time; re-saving with identical settings converges faster but never recovers.\n\n## Things you can do losslessly\n\nNot every JPEG operation has to re-encode. `jpegtran` does several transformations by rearranging the coefficient blocks directly:\n\n```bash\njpegtran -rotate 90 -perfect -copy all in.jpg > out.jpg\njpegtran -crop 800x600+16+16 in.jpg > cropped.jpg\n```\n\nRotation is exact if the dimensions are multiples of 8 or 16, which `-perfect` enforces. Cropping is lossless only on block boundaries, so the offsets must be multiples of 8. Most phone gallery apps that offer \"rotate\" do this correctly; most desktop editors do not, and a rotate in an ordinary editor costs you a full generation.\n\n**Progressive JPEG** is worth knowing: the same data reordered so a coarse version arrives first and refines. It is typically 2 to 10 per cent smaller than baseline and shows something useful earlier on a slow connection. There is no quality difference. Turn it on for anything going on the web.\n\n**MozJPEG**, a free encoder from Mozilla, produces files roughly 5 to 15 per cent smaller than the standard library at matched visual quality by choosing better tables and trellis-optimising the coefficients. Squoosh in a browser uses it; `cjpeg` from the mozjpeg package uses it on the command line; it is free either way.\n\n## The honest limits\n\n**Quality numbers are not comparable between encoders.** Quality 80 in libjpeg, in MozJPEG, in Photoshop's *Save As* and in Photoshop's *Save for Web* are four different things producing four different file sizes. Photoshop's *Save As* runs 0 to 12 and its *Save for Web* runs 0 to 100 and they do not correspond. Never specify a quality number in a brief without naming the tool; specify a file size or a visual test instead.\n\n**Artefacts cannot be removed.** There are deblocking filters and AI restoration models, and what they do is smooth or regenerate, not recover. The coefficients were rounded to zero; the information is gone. The only fix for a badly compressed file is a better source file.\n\n**And the whole thing is aimed at photographs of the real world.** JPEG's assumptions (smooth gradients, soft edges, colour that changes slowly) are true of a photograph and false of a screenshot, a chart, a line drawing or a logo. Using it on those is not a bad setting; it is the wrong tool."},"check":{"question":"A designer saves a poster with thin red headline type on white as a JPEG. The type has a soft, dirty fringe. They raise the quality slider from 70 to 95 and the fringe is still there. Why?","options":[{"text":"The fringe is ringing from the cosine transform, and ringing is unaffected by the quantisation table.","correct":false,"tempting":"Names a real artefact that does respond to quality: ringing is caused by coarse quantisation of high-frequency coefficients and does visibly reduce at quality 95."},{"text":"The colour channels were stored at half resolution before any quantisation happened, so the quality setting never touches that loss.","correct":true},{"text":"Red is the channel JPEG compresses most aggressively, so red content always needs a different format regardless of settings.","correct":false,"tempting":"Invents a per-colour rule: the encoder works on luminance and two colour-difference channels, and saturated edges in any hue behave the same way."},{"text":"The file was re-saved several times, and accumulated generation loss cannot be undone by a later high-quality save.","correct":false,"tempting":"Offers a real phenomenon that does not match the evidence: generation loss shows as blocking and crawling edges across the whole image, not a fringe specific to saturated type."}]},"prev":{"slug":"raw-and-the-mosaic","title":"Two thirds of the colour in your photo was calculated","url":"https://addaly.com/learn/image-editing/raw-and-the-mosaic"},"next":{"slug":"colour-spaces-and-gamut","title":"Some colours have no number","url":"https://addaly.com/learn/image-editing/colour-spaces-and-gamut"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Eight by eight, and what the encoder threw away', in Editing an Image. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/image-editing/inside-a-jpeg (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Eight by eight, and what the encoder threw away","courseTitle":"Editing an Image","module":{"index":1,"slug":"what-an-image-is-made-of","title":"What an image is made of"},"answer":null,"keyIdea":"JPEG loses data at exactly one step, rounding the 8×8 transform coefficients, so blocking, ringing and colour fringes each map to a specific stage of the encoder and tell you whether to change the quality, the subsampling or the format.","hero":{"kind":"figure","figure":{"t":"figure","label":"Figure","figure":{"type":"flow","steps":[{"label":"Split brightness from colour","detail":"RGB becomes Y, Cb and Cr. Nothing is lost here; the split exists so the next step can treat them differently."},{"label":"Store colour at half resolution","detail":"4:2:0 subsampling discards three quarters of the colour data. Invisible on a face. This is where red type on white gets its soft dirty fringe, and the quality slider is downstream of it."},{"label":"Transform each 8 by 8 block","detail":"Sixty-four values become sixty-four coefficients: one average level and 63 patterns of increasing fineness. Still lossless: a re-description, not a reduction."},{"label":"Quantise, the only lossy step","detail":"Divide each coefficient and round. Too coarse and only the block average survives, which is blocking. Truncating the sum at a hard edge leaves overshoot, which is the ringing around black text."},{"label":"Open, edit, save again","detail":"The decoded values no longer sit on the new grid, so every round rounds again. After thirty the edges crawl and the flat areas terrace, and a rotate in an ordinary editor costs a whole generation."}],"title":"Four steps, and the artefact each one leaves behind","caption":"Read an artefact backwards and it tells you which control to reach for. Blocking in a sky is the quality slider. A dirty fringe on red type is the subsampling, and raising the quality cannot touch it because that step happens earlier. Both are reasons the file should have been a PNG or a WebP."},"alt":"Figure: Four steps, and the artefact each one leaves behind. Steps: 1. Split brightness from colour (RGB becomes Y, Cb and Cr. Nothing is lost here; the split exists so the next step can treat them differently.); 2. Store colour at half resolution (4:2:0 subsampling discards three quarters of the colour data. Invisible on a face. This is where red type on white gets its soft dirty fringe, and the quality slider is downstream of it.); 3. Transform each 8 by 8 block (Sixty-four values become sixty-four coefficients: one average level and 63 patterns of increasing fineness. Still lossless: a re-description, not a reduction.); 4. Quantise, the only lossy step (Divide each coefficient and round. Too coarse and only the block average survives, which is blocking. Truncating the sum at a hard edge leaves overshoot, which is the ringing around black text.); 5. Open, edit, save again (The decoded values no longer sit on the new grid, so every round rounds again. After thirty the edges crawl and the flat areas terrace, and a rotate in an ordinary editor costs a whole generation.). Read an artefact backwards and it tells you which control to reach for. Blocking in a sky is the quality slider. A dirty fringe on red type is the subsampling, and raising the quality cannot touch it because that step happens earlier. Both are reasons the file should have been a PNG or a WebP."}},"blocks":[{"t":"h2","id":"the-encoder-s-four-steps","runs":[{"text":"The encoder’s four steps"}]},{"t":"p","runs":[{"text":"JPEG has been the default photographic format since 1992 and the reason it works is worth twenty minutes of your life, because every artefact you will ever have to argue with a client about comes from one of four steps."}]},{"t":"p","runs":[{"text":"1. Convert to brightness and two colour differences.","b":true},{"text":" RGB becomes Y (luminance) plus Cb and Cr (blue-difference and red-difference). Your eye is far more sensitive to brightness detail than colour detail, and this split lets the encoder treat them differently."}]},{"t":"p","runs":[{"text":"2. Throw away colour resolution.","b":true},{"text":" In 4:2:0 subsampling, the near-universal default, the Cb and Cr channels are stored at half resolution in each direction, so one colour sample covers a 2×2 block of pixels. That is three quarters of the colour data discarded immediately, and on a face or a landscape you cannot see it. On a thin red line on white you can, which is the next section."}]},{"t":"p","runs":[{"text":"3. Split into 8×8 blocks and transform each one.","b":true},{"text":" Each block goes through a discrete cosine transform, which re-describes those 64 values as 64 coefficients: one average level plus 63 patterns of increasing fineness. Nothing is lost yet; this is a lossless re-description."}]},{"t":"p","runs":[{"text":"4. Quantise.","b":true},{"text":" Each coefficient is divided by a number from a quantisation table and rounded. The fine-detail coefficients get divided by large numbers, so most of them round to zero. "},{"text":"This is the lossy step, and it is the only one.","b":true},{"text":" The quality slider does nothing except scale that table: quality 90 uses small divisors, quality 40 uses large ones."}]},{"t":"p","runs":[{"text":"The zeros then compress to almost nothing, which is where the file size comes from."}]},{"t":"h2","id":"reading-an-artefact-backwards","runs":[{"text":"Reading an artefact backwards"}]},{"t":"p","runs":[{"text":"Knowing the four steps means you can look at a bad file and say which step caused it."}]},{"t":"p","runs":[{"text":"Blocking","b":true},{"text":": visible 8×8 squares, usually in sky or shadow. Quantisation was so coarse that only the block’s average level survived. Cause: quality set too low, or an image re-saved many times."}]},{"t":"p","runs":[{"text":"Ringing, also called mosquito noise","b":true},{"text":": a shimmer of light and dark just outside a hard edge, most obvious around black text on white. The encoder is describing a step edge as a sum of smooth cosine waves; truncating that sum leaves overshoot on either side. This is a mathematical property of the transform, not a bug, and it is why JPEG is wrong for screenshots, diagrams and text."}]},{"t":"p","runs":[{"text":"Colour bleed and dirty fringes on saturated text","b":true},{"text":": step 2. Red type on white is a hard edge in a channel that was stored at half resolution. Raising the quality slider does not fix it, because subsampling happens before quantisation. Some encoders let you force 4:4:4 (no subsampling); Photoshop does it automatically at quality 7 and above in "},{"text":"Save for Web","i":true},{"text":", and "},{"text":"cjpeg -sample 1x1","code":true},{"text":" does it explicitly. Or just use PNG or WebP, which is the real answer."}]},{"t":"p","runs":[{"text":"Generation loss","b":true},{"text":": open a JPEG, make one small edit, save at quality 85, repeat. After 30 rounds the edges crawl and the flat areas terrace. What happens is that each decode lands on values that do not sit neatly on the new quantisation grid, so each re-encode rounds again. It does eventually converge to a stable state, but well below where it started. Re-saving "},{"text":"without any edit","i":true},{"text":" in a tool that re-encodes still loses a little each time; re-saving with identical settings converges faster but never recovers."}]},{"t":"h2","id":"things-you-can-do-losslessly","runs":[{"text":"Things you can do losslessly"}]},{"t":"p","runs":[{"text":"Not every JPEG operation has to re-encode. "},{"text":"jpegtran","code":true},{"text":" does several transformations by rearranging the coefficient blocks directly:"}]},{"t":"code","lang":"bash","label":"Bash","text":"jpegtran -rotate 90 -perfect -copy all in.jpg > out.jpg\njpegtran -crop 800x600+16+16 in.jpg > cropped.jpg"},{"t":"p","runs":[{"text":"Rotation is exact if the dimensions are multiples of 8 or 16, which "},{"text":"-perfect","code":true},{"text":" enforces. Cropping is lossless only on block boundaries, so the offsets must be multiples of 8. Most phone gallery apps that offer “rotate” do this correctly; most desktop editors do not, and a rotate in an ordinary editor costs you a full generation."}]},{"t":"p","runs":[{"text":"Progressive JPEG","b":true},{"text":" is worth knowing: the same data reordered so a coarse version arrives first and refines. It is typically 2 to 10 per cent smaller than baseline and shows something useful earlier on a slow connection. There is no quality difference. Turn it on for anything going on the web."}]},{"t":"p","runs":[{"text":"MozJPEG","b":true},{"text":", a free encoder from Mozilla, produces files roughly 5 to 15 per cent smaller than the standard library at matched visual quality by choosing better tables and trellis-optimising the coefficients. Squoosh in a browser uses it; "},{"text":"cjpeg","code":true},{"text":" from the mozjpeg package uses it on the command line; it is free either way."}]},{"t":"h2","id":"the-honest-limits","runs":[{"text":"The honest limits"}],"role":"limit"},{"t":"p","runs":[{"text":"Quality numbers are not comparable between encoders.","b":true},{"text":" Quality 80 in libjpeg, in MozJPEG, in Photoshop’s "},{"text":"Save As","i":true},{"text":" and in Photoshop’s "},{"text":"Save for Web","i":true},{"text":" are four different things producing four different file sizes. Photoshop’s "},{"text":"Save As","i":true},{"text":" runs 0 to 12 and its "},{"text":"Save for Web","i":true},{"text":" runs 0 to 100 and they do not correspond. Never specify a quality number in a brief without naming the tool; specify a file size or a visual test instead."}]},{"t":"p","runs":[{"text":"Artefacts cannot be removed.","b":true},{"text":" There are deblocking filters and AI restoration models, and what they do is smooth or regenerate, not recover. The coefficients were rounded to zero; the information is gone. The only fix for a badly compressed file is a better source file."}]},{"t":"p","runs":[{"text":"And the whole thing is aimed at photographs of the real world.","b":true},{"text":" JPEG’s assumptions (smooth gradients, soft edges, colour that changes slowly) are true of a photograph and false of a screenshot, a chart, a line drawing or a logo. Using it on those is not a bad setting; it is the wrong tool."}]}],"check":{"question":[{"text":"A designer saves a poster with thin red headline type on white as a JPEG. The type has a soft, dirty fringe. They raise the quality slider from 70 to 95 and the fringe is still there. Why?"}],"options":[{"text":[{"text":"The fringe is ringing from the cosine transform, and ringing is unaffected by the quantisation table."}],"correct":false,"tempting":[{"text":"Names a real artefact that does respond to quality: ringing is caused by coarse quantisation of high-frequency coefficients and does visibly reduce at quality 95."}]},{"text":[{"text":"The colour channels were stored at half resolution before any quantisation happened, so the quality setting never touches that loss."}],"correct":true,"tempting":null},{"text":[{"text":"Red is the channel JPEG compresses most aggressively, so red content always needs a different format regardless of settings."}],"correct":false,"tempting":[{"text":"Invents a per-colour rule: the encoder works on luminance and two colour-difference channels, and saturated edges in any hue behave the same way."}]},{"text":[{"text":"The file was re-saved several times, and accumulated generation loss cannot be undone by a later high-quality save."}],"correct":false,"tempting":[{"text":"Offers a real phenomenon that does not match the evidence: generation loss shows as blocking and crawling edges across the whole image, not a fringe specific to saturated type."}]}],"order":[3,0,1,2],"why":null},"prev":{"slug":"raw-and-the-mosaic","title":"Two thirds of the colour in your photo was calculated","minutes":10},"next":{"slug":"colour-spaces-and-gamut","title":"Some colours have no number","minutes":9}}}