fcl-image: what changed
Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.
| Old | New | Line | |
|---|---|---|---|
| 1 | + | **English (en)** | [[fcl-image/ja|**日本語 (ja)**]] | |
| 2 | + | ||
| 1 | 3 | ## Introduction | |
| 2 | 4 | ||
| 3 | - | ||
| 5 | + | FCL-Image started life as "fpimage", pretty much as an attempt to implement a generally portable TImage like class, without the deep win32 and VCL ties. | |
| 4 | 6 | ||
| 5 | 7 | ## Current file list | |
| 6 | 8 | ||
| 7 | - | ||
| 9 | + | The central units are _[fpimage](<http://lazarus-ccr.sourceforge.net/docs/fcl/fpimage> "doc:fcl/fpimage")_ and _[fpcanvas](<http://lazarus-ccr.sourceforge.net/docs/fcl/fpcanvas> "doc:fcl/fpcanvas")_ and these are further enhanced by OOP derivation. | |
| 8 | 10 | ||
| 9 | - | ||
| 10 | - | ||
| 11 | - | ||
| 12 | - | ||
| 13 | - | ||
| 14 | - | ||
| 15 | - | ||
| 16 | - | ||
| 17 | - | ||
| 18 | - | ||
| 19 | - | ||
| 20 | - | ||
| 21 | - | ||
| 22 | - | ||
| 23 | - | ||
| 24 | - | ||
| 25 | - | ||
| 26 | - | ||
| 27 | - | ||
| 28 | - | ||
| 29 | - | ||
| 30 | - | ||
| 31 | - | ||
| 32 | - | ||
| 33 | - | ||
| 34 | - | ||
| 35 | - | ||
| 36 | - | ||
| 37 | - | ||
| 38 | - | ||
| 39 | - | ||
| 40 | - | ||
| 41 | - | ||
| 42 | - | ||
| 43 | - | ||
| 44 | - | ||
| 45 | - | ||
| 46 | - | ||
| 47 | - | ||
| 48 | - | ||
| 49 | - | ||
| 50 | - | ||
| 51 | - | ||
| 52 | - | ||
| 53 | - | ||
| 54 | - | ||
| 55 | - | ||
| 56 | - | ||
| 57 | - | ||
| 58 | - | ||
| 59 | - | ||
| 60 | - | ||
| 61 | - | ||
| 62 | - | ||
| 63 | - | ||
| 64 | - | ||
| 65 | - | ||
| 66 | - | ||
| 67 | - | ||
| 68 | - | ||
| 69 | - | ||
| 70 | - | ||
| 71 | - | ||
| 72 | - | ||
| 73 | - | ||
| 74 | - | ||
| 75 | - | ||
| 76 | - | ||
| 77 | - | ||
| 78 | - | ||
| 79 | - | ||
| 80 | - | ||
| 81 | - | ||
| 82 | - | ||
| 83 | - | ||
| 84 | - | ||
| 11 | + | * **bmpcomn** A few structures and constants for the BMP fileformat | |
| 12 | + | * **clipping** Some utility routines that help with clipping and intersecting of rects. | |
| 13 | + | * **ellipses** Drawing of ellipses and arcs, and filling ellipses and pies. | |
| 14 | + | * TEllipseInfo | |
| 15 | + | * **extinterpolation** interpolation filters for TFPCanvas.StretchDraw ( | |
| 16 | + | * TBlackmanInterpolation | |
| 17 | + | * TBlackmanSincInterpolation | |
| 18 | + | * TBlackmanBesselInterpolation | |
| 19 | + | * TGaussianInterpolation | |
| 20 | + | * TBoxInterpolation | |
| 21 | + | * THermiteInterpolation | |
| 22 | + | * TLanczosInterpolation | |
| 23 | + | * TQuadraticInterpolation | |
| 24 | + | * TCubicInterpolation | |
| 25 | + | * TCatromInterpolation | |
| 26 | + | * TBilineairInterpolation | |
| 27 | + | * THanningInterpolation | |
| 28 | + | * THammingInterpolation | |
| 29 | + | * **fpcanvas** Generic Canvas classes. | |
| 30 | + | * [TFPCanvasException](<http://lazarus-ccr.sourceforge.net/docs/fcl/fpcanvas/tfpcanvasexception.html> "doc:fcl/fpcanvas/tfpcanvasexception.html") | |
| 31 | + | * TFPPenException | |
| 32 | + | * TFPBrushException | |
| 33 | + | * TFPFontException | |
| 34 | + | * TFPCustomCanvas | |
| 35 | + | * TFPCanvasHelper | |
| 36 | + | * TFPCustomFont | |
| 37 | + | * TFPCustomFontClass | |
| 38 | + | * TFPCustomPen | |
| 39 | + | * TFPCustomPenClass | |
| 40 | + | * TFPCustomBrush | |
| 41 | + | * TFPCustomBrushClass | |
| 42 | + | * TFPCustomInterpolation | |
| 43 | + | * TFPBaseInterpolation | |
| 44 | + | * [TMitchelInterpolation](<http://lazarus-ccr.sourceforge.net/docs/fcl/fpcanvas/tmitchelinterpolation.html> "doc:fcl/fpcanvas/tmitchelinterpolation.html") | |
| 45 | + | * TFPCustomCanvas | |
| 46 | + | * TFPCustomDrawFont | |
| 47 | + | * TFPEmptyFont | |
| 48 | + | * TFPCustomDrawPen | |
| 49 | + | * TFPEmptyPen | |
| 50 | + | * TFPCustomDrawBrush | |
| 51 | + | * TFPEmptyBrush | |
| 52 | + | * **fpcolhash** an implementation of a color hash table. | |
| 53 | + | * TFPColorHashException | |
| 54 | + | * TFPColorHashTable | |
| 55 | + | * **fpditherer** contains classes used to dither images. | |
| 56 | + | * FPDithererException | |
| 57 | + | * TFPBaseDitherer | |
| 58 | + | * TFPFloydSteinbergDitherer | |
| 59 | + | * **fpimage** fpImage base definitions and classes | |
| 60 | + | * TFPCustomImageReader | |
| 61 | + | * TFPCustomImageWriter | |
| 62 | + | * TFPCustomImage | |
| 63 | + | * FPImageException | |
| 64 | + | * TFPPalette | |
| 65 | + | * TFPCustomImage | |
| 66 | + | * TFPMemoryImage | |
| 67 | + | * TFPCustomImageHandler | |
| 68 | + | * TFPCustomImageReader | |
| 69 | + | * TFPCustomImageWriter | |
| 70 | + | * TIHData | |
| 71 | + | * TImageHandlersManager | |
| 72 | + | * **fpimgcanv** Image Canvas - canvas which draws on an image. | |
| 73 | + | * TFPImageCanvas | |
| 74 | + | * **fpimgcmn** Image Common: small procedural Helpers (swap,crc) | |
| 75 | + | * **fppixlcanv** | |
| 76 | + | * TPixelCanvas | |
| 77 | + | * **fpquantizer** classes used to quantize images. See [[1]](<http://lazarus.freepascal.org/index.php/topic,13468.0>) | |
| 78 | + | * **freetype** Encapsulating classes over freetype | |
| 79 | + | * **freetypeh** Freetype header translation | |
| 80 | + | * **ftfont** More freetype related font classes | |
| 81 | + | * **pcxcomn** PCX fileformat records and types | |
| 82 | + | * **pixtools** Pixel drawing routines. | |
| 83 | + | * **pngcomn** PNG fileformat records and types | |
| 84 | + | * **polygonfilltools** Routines for filling polygons (FPC 3.3.1 or newer) | |
| 85 | + | * **pscanvas** TPostScriptCanvas implementation. | |
| 86 | + | * **targacmn** Targa fileformat records and types | |
| 85 | 87 | ||
| 86 | - | ||
| 88 | + | **Readers and writers for various image formats** reside in dedicated units: | |
| 87 | 89 | ||
| 88 | - | ||
| 89 | - | ||
| 90 | - | ||
| 91 | - | ||
| 92 | - | ||
| 93 | - | ||
| 94 | - | ||
| 95 | - | ||
| 90 | + | * **fpreadbmp** , **fpwritebmp** : Reader and writer units for .bmp files | |
| 91 | + | * **fpreadjpeg** , **fpwritejpeg** : Reader and writer units for .jpeg/.jpg files | |
| 92 | + | * **fpreadpcx** , **fprwritepcs** : Reader and writer units for .pcx files | |
| 93 | + | * **fpreadpng** , **fpwritepng** : Reader and writer units for .png files | |
| 94 | + | * **fpreadpnm** , **fpwritepnm** : Reader and writer units for .pnm files | |
| 95 | + | * **fpreadtga** , **fpwritetga** : Reader and writer units for .tga files | |
| 96 | + | * **fpreadtiff** , **fpwritetiff** : Reader and writer units for .tiff/.tif files | |
| 97 | + | * **fpreadxpm** , **fpwritexpm** : Reader and writer units for .xpm files | |
| 96 | 98 | ||
| 97 | 99 | **INC files uses in fpcanvas** | |
| 98 | 100 | ||
| 99 | - | ||
| 100 | - | ||
| 101 | - | ||
| 102 | - | ||
| 103 | - | ||
| 104 | - | ||
| 105 | - | ||
| 101 | + | * fpbrush | |
| 102 | + | * fpcanvas | |
| 103 | + | * fpcdrawh | |
| 104 | + | * fpfont | |
| 105 | + | * fphelper | |
| 106 | + | * fpinterpolation | |
| 107 | + | * fppen | |
| 106 | 108 | ||
| 107 | - | ||
| 109 | + | INC files uses in fpmake | |
| 108 | 110 | ||
| 109 | - | ||
| 110 | - | ||
| 111 | - | ||
| 112 | - | ||
| 113 | - | ||
| 111 | + | * fpcolors | |
| 112 | + | * fpimage | |
| 113 | + | * fphandler | |
| 114 | + | * fpcolcnv | |
| 115 | + | * fppalette | |
| 114 | 116 | ||
| 115 | - | ||
| 117 | + | Demos | |
| 116 | 118 | ||
| 117 | - | ||
| 118 | - | ||
| 119 | + | * drawing | |
| 120 | + | * imgconv | |
| 119 | 121 | ||
| 120 | 122 | ## Known Issues and limitations | |
| 121 | 123 | ||
| 122 | - | ||
| 123 | - | ||
| 124 | - | ||
| 125 | - | ||
| 126 | - | ||
| 124 | + | * fcl-image is written for maximal portability and maintainability and is quite slow. The main storage type is 16-bit RGBA, storing always 64-bit per pixel, and a function is called to get each pixel. | |
| 125 | + | * pngwriter doesn't implement automatic detection of filters, and thus is always "filter none" | |
| 126 | + | * most writers don't autodetect what format to save. You must correctly set the relevant writer options ( see also [bug 17621](<http://bugs.freepascal.org/view.php?id=17621>)). | |
| 127 | + | * the color format can be counter intuitive, read [this thread](<http://www.mail-archive.com/fpc-devel@lists.freepascal.org/msg16506.html>) | |
| 128 | + | * png only implements basic chunk types (idat,trns,plte,ihdr and iend). It e.g. can't handle chunks like pHYs,iCCP,gAMA,cHRM yet. ([bug 19209](<http://bugs.freepascal.org/view.php?id=19209>)). | |
| 127 | 129 | ||
| 128 | 130 | ## Image formats | |
| 129 | 131 | ||
| 130 | - | ||
| 131 | - | ||
| 132 | - | ||
| 133 | - | ||
| 134 | - | ||
| 135 | - | ||
| 136 | - | ||
| 137 | - | ||
| 138 | - | ||
| 139 | - | ||
| 140 | - | ||
| 141 | - | ||
| 142 | - | ||
| 143 | - | ||
| 144 | - | ||
| 145 | - | ||
| 146 | - | ||
| 147 | - | ||
| 132 | + | Here is a table with the correct class to use for each image format, as well as comparing to the LCL. | |
| 148 | 133 | ||
| 134 | + | Format | fcl-image reader unit | fcl-image writer unit | LCL class | |
| 135 | + | ---|---|---|--- | |
| 136 | + | Windows Bitmap. A very simple format, usually used without compression and without transparency, but it may also have them, although many applications don't support those extra features. (*.bmp) | fpreadbmp | fpwritebmp | TBitmap | |
| 137 | + | A popular format for websites. It supports up to 8 bits per pixels, which are 256 distinct colors chosen from the 24-bit RGB color space. This limitation makes it unsuitable for complex images, but useful for simple graphics. The image is compressed without loss in quality and the format also supports animations. (*.gif) | fpreadgif | - | TGIFImage | |
| 138 | + | The most popular format for websites, and an ISO standard since 1994. The format compressed the image but generates a small loss of quality. (*.jpeg, *.jpg) | fpreadjpeg | fpwritejpeg | TJpegImage | |
| 139 | + | A format widely used in DOS applications, but now less popular. (*.pcx) | fpreadpcx | fpwritepcx | \- | |
| 140 | + | Portable Network Graphics. A popular format for it's efficient compression without quality loss. (*.png) | fpreadpng | fpwritepng | TPortableNetworkGraphic | |
| 141 | + | Supports the formats Portable BitMaps (*.pbm), Portable GrayMaps (*.pgm) and Portable PixMaps (*.ppm) | fpreadpnm | fpwritepnm | TPortableAnyMapGraphic | |
| 142 | + | The default file format for Adobe Photoshop® (*.psd) | fpreadpsd | - | \- | |
| 143 | + | Called either Truevision TGA File Format or TARGA File Format, this is a compressed format commonly used in games. (*.tga, *.tpic) | fpreadtga | fpwritetga | \- | |
| 144 | + | Tagged Image File Format (TIFF), originally created to have a common image format for scanners. (*.tiff, *.tif). Controlled by Adobe® | fpreadtiff | fpwritetiff | TTiffImage | |
| 145 | + | X PixMap, an image format used by the X Window System which stores the data in ASCII text. Is also used as an icon format in UNIX applications. (*.xpm) | fpreadxpm | fpwritexpm | TPixmap | |
| 146 | + | X Window Dump file format. Used by the program xwd which generates screenshots in the X Window System. (*.xwd) | fpreadxwd | - | \- | |
| 147 | + | Windows Icon. Is a list of small bitmaps in various color depths and sizes, with transparency support and no compression. (*.ico) | - | - | TIcon | |
| 148 | + | Windows Cursor. Exactly the same format as the Windows Icon, but with an extra field in the header for the cursor hotspot. (*.cur) | - | - | TCursorImage | |
| 149 | + | macOS icon. List of small images in various sizes. (*.icns) | - | - | TIcnsIcon | |
| 150 | + | ||
| 149 | 151 | ### Tiff | |
| 150 | 152 | ||
| 151 | - | ||
| 153 | + | Working features of the reader: | |
| 152 | 154 | ||
| 153 | - | ||
| 154 | - | ||
| 155 | - | ||
| 156 | - | ||
| 157 | - | ||
| 158 | - | ||
| 159 | - | ||
| 160 | - | ||
| 161 | - | ||
| 155 | + | * Black and white/lineart/1 bit (FPC trunk only, not in FPC 2.6.x) | |
| 156 | + | * Grayscale 8,16bit (optional alpha), | |
| 157 | + | * RGB 8,16bit (optional alpha), | |
| 158 | + | * Orientation, except for rotated orientation | |
| 159 | + | * compression: packbits, LZW, deflate | |
| 160 | + | * endian | |
| 161 | + | * multiple images/multipage | |
| 162 | + | * strips and tiles | |
| 163 | + | * Sets properties in the image Extras properties, which is used by the writer, so that reading/writing keeps many common tags like Artist, Copyright, DateAndTime, HostComputer, ImageDescription, Maker, Model, Software, DocumentName, XResolution, YResolution, Orientation, bit depts, page name, isThumbnail. | |
| 162 | 164 | ||
| 163 | - | ||
| 165 | + | Here is an incomplete list of open / not yet implemented features of the reader: | |
| 164 | 166 | ||
| 165 | - | ||
| 166 | - | ||
| 167 | - | ||
| 168 | - | ||
| 169 | - | ||
| 170 | - | ||
| 171 | - | ||
| 172 | - | ||
| 173 | - | ||
| 167 | + | * Compression: jpeg, CCITT Group 3, CCITT Group 4 | |
| 168 | + | * PlanarConfiguration 2 | |
| 169 | + | * ColorMap | |
| 170 | + | * separate mask | |
| 171 | + | * fillorder - not needed by baseline tiff reader | |
| 172 | + | * bigtiff 64bit offsets | |
| 173 | + | * XMP tag 700 | |
| 174 | + | * ICC profile tag 34675 | |
| 175 | + | * Orientation with rotation | |
| 174 | 176 | ||
| 175 | - | ||
| 177 | + | Working features of the writer: | |
| 176 | 178 | ||
| 177 | - | ||
| 178 | - | ||
| 179 | - | ||
| 180 | - | ||
| 181 | - | ||
| 182 | - | ||
| 179 | + | * Grayscale 8,16bit (optional alpha) | |
| 180 | + | * RGB 8,16bit (optional alpha) | |
| 181 | + | * Orientation except rotated | |
| 182 | + | * multiple images, pages | |
| 183 | + | * thumbnail | |
| 184 | + | * Compression: deflate | |
| 183 | 185 | ||
| 184 | - | ||
| 186 | + | Here is an incomplete list of open / not yet implemented features of the writer: | |
| 185 | 187 | ||
| 186 | - | ||
| 187 | - | ||
| 188 | - | ||
| 189 | - | ||
| 190 | - | ||
| 191 | - | ||
| 192 | - | ||
| 193 | - | ||
| 188 | + | * Compression: LZW, packbits, jpeg, CCIT Group 3,CCIT Group 4,... | |
| 189 | + | * Planar | |
| 190 | + | * ColorMap | |
| 191 | + | * separate mask | |
| 192 | + | * fillorder - not needed by baseline tiff reader | |
| 193 | + | * bigtiff 64bit offsets | |
| 194 | + | * endian - currently using system endianess | |
| 195 | + | * Orientation with rotation | |
| 194 | 196 | ||
| 195 | 197 | ## Walk-through | |
| 196 | 198 | ||
| 197 | 199 | ### Basic Canvas Setup | |
| 198 | 200 | ||
| 199 | - | ||
| 200 | - | ||
| 201 | - | ||
| 201 | + | You'll need a few things to start up. A canvas, image, and writer. The writer is to write images like PNG. The sample below will give you a 100x100 black png. | |
| 202 | + | ``` | |
| 202 | 203 | {$mode objfpc}{$h+} | |
| 203 | - | ||
| 204 | - | ||
| 205 | - | ||
| 206 | - | ||
| 207 | - | ||
| 208 | - | ||
| 209 | - | ||
| 210 | - | ||
| 211 | - | ||
| 212 | - | ||
| 213 | - | ||
| 214 | - | ||
| 215 | - | ||
| 216 | - | ||
| 217 | - | ||
| 218 | - | ||
| 219 | - | ||
| 220 | - | ||
| 221 | - | ||
| 222 | - | ||
| 223 | - | ||
| 224 | - | ||
| 225 | - | ||
| 226 | - | ||
| 227 | - | ||
| 228 | - | ||
| 229 | - | ||
| 204 | + | program demo; | |
| 205 | + | ||
| 206 | + | uses classes, sysutils, | |
| 207 | + | FPImage, FPCanvas, FPImgCanv, | |
| 208 | + | FPWritePNG; | |
| 209 | + | ||
| 210 | + | var canvas : TFPCustomCanvas; | |
| 211 | + | image : TFPCustomImage; | |
| 212 | + | writer : TFPCustomImageWriter; | |
| 213 | + | begin | |
| 214 | + | { Create an image 100x100 pixels} | |
| 215 | + | image := TFPMemoryImage.Create (100,100); | |
| 216 | + | ||
| 217 | + | { Attach the image to the canvas } | |
| 218 | + | Canvas := TFPImageCanvas.Create (image); | |
| 219 | + | ||
| 220 | + | { Create the writer } | |
| 221 | + | Writer := TFPWriterPNG.Create; | |
| 222 | + | ||
| 223 | + | { Save to file } | |
| 224 | + | image.SaveToFile ('DrawTest.png', writer); | |
| 225 | + | ||
| 226 | + | { Clean up! } | |
| 227 | + | Canvas.Free; | |
| 228 | + | image.Free; | |
| 229 | + | writer.Free; | |
| 230 | + | end. | |
| 230 | 231 | ``` | |
| 231 | 232 | ||
| 232 | 233 | ### Drawing a Circle | |
| 233 | 234 | ||
| 234 | - | ||
| 235 | - | ||
| 236 | - | ||
| 235 | + | Drawing a circle requires a bit more then just giving a width. You'll need to setup the pen style, mode, width, and color to do this. FCL-Image comes with a few pen modes and styles, it will really be up to you to decide which you'll need. Its best for now to try with a few modes and styles to get a better understanding of what FCL-Image can do. | |
| 236 | + | ``` | |
| 237 | 237 | {$mode objfpc}{$h+} | |
| 238 | - | ||
| 239 | - | ||
| 240 | - | ||
| 241 | - | ||
| 242 | - | ||
| 243 | - | ||
| 244 | - | ||
| 245 | - | ||
| 246 | - | ||
| 247 | - | ||
| 248 | - | ||
| 249 | - | ||
| 250 | - | ||
| 251 | - | ||
| 252 | - | ||
| 253 | - | ||
| 254 | - | ||
| 255 | - | ||
| 256 | - | ||
| 257 | - | ||
| 258 | - | ||
| 259 | - | ||
| 260 | - | ||
| 261 | - | ||
| 262 | - | ||
| 263 | - | ||
| 264 | - | ||
| 265 | - | ||
| 266 | - | ||
| 267 | - | ||
| 268 | - | ||
| 269 | - | ||
| 270 | - | ||
| 271 | - | ||
| 272 | - | ||
| 273 | - | ||
| 274 | - | ||
| 275 | - | ||
| 276 | - | ||
| 277 | - | ||
| 238 | + | program demo; | |
| 239 | + | ||
| 240 | + | uses classes, sysutils, | |
| 241 | + | FPImage, FPCanvas, FPImgCanv, | |
| 242 | + | FPWritePNG; | |
| 243 | + | ||
| 244 | + | var canvas : TFPcustomCanvas; | |
| 245 | + | image : TFPCustomImage; | |
| 246 | + | writer : TFPCustomImageWriter; | |
| 247 | + | { | |
| 248 | + | Colors range from 0 to 65535 in each primary color. | |
| 249 | + | They can also show as hexadecimal: | |
| 250 | + | $FFFF = 65535, $0000 = 0 | |
| 251 | + | } | |
| 252 | + | passionRed: TFPColor = (Red: 65535; Green: 0; Blue: 0; Alpha: 65535); | |
| 253 | + | begin | |
| 254 | + | image := TFPMemoryImage.Create (100,100); | |
| 255 | + | Canvas := TFPImageCanvas.Create (image); | |
| 256 | + | Writer := TFPWriterPNG.Create; | |
| 257 | + | ||
| 258 | + | { Set the pen styles } | |
| 259 | + | with canvas do | |
| 260 | + | begin | |
| 261 | + | pen.mode := pmCopy; | |
| 262 | + | pen.style := psSolid; | |
| 263 | + | pen.width := 1; | |
| 264 | + | pen.FPColor := passionRed; | |
| 265 | + | end; | |
| 266 | + | ||
| 267 | + | { Draw a circle } | |
| 268 | + | canvas.Ellipse (10,10, 90,90); | |
| 269 | + | ||
| 270 | + | { Save to file } | |
| 271 | + | image.SaveToFile ('DrawTest.png', writer); | |
| 272 | + | ||
| 273 | + | { Clean up! } | |
| 274 | + | Canvas.Free; | |
| 275 | + | image.Free; | |
| 276 | + | writer.Free; | |
| 277 | + | end. | |
| 278 | 278 | ``` | |
| 279 | 279 | ||
| 280 | 280 | ### Drawing Alpha-Blended Shapes | |
| 281 | 281 | ||
| 282 | - | ||
| 283 | - | ||
| 284 | - | ||
| 282 | + | Alpha-blending means: When something is painted over an existing image the old pixels are allowed to shine through the new drawing. The amount of mixing is given by the `Alpha` value of the new color which is specified as fourth word parameter of the brush's FPColor. `Alpha=0` means: the new painting is fully transparent, `Alpha=$FFFF` means: the new drawing is fully opaque. | |
| 285 | 283 | ||
| 286 | - | ||
| 284 | + | In order to activate alpha-blending the `DrawingMode` of the canvas must be switched to `dmAlphaBlend`, and the brush color of a new shape must be extended by its `Alpha` value. | |
| 285 | + | ``` | |
| 287 | 286 | program alphablend; | |
| 288 | - | ||
| 289 | - | ||
| 290 | - | ||
| 291 | - | ||
| 292 | - | ||
| 293 | - | ||
| 294 | - | ||
| 295 | - | ||
| 296 | - | ||
| 297 | - | ||
| 298 | - | ||
| 299 | - | ||
| 300 | - | ||
| 301 | - | ||
| 302 | - | ||
| 303 | - | ||
| 304 | - | ||
| 305 | - | ||
| 306 | - | ||
| 307 | - | ||
| 308 | - | ||
| 309 | - | ||
| 310 | - | ||
| 311 | - | ||
| 312 | - | ||
| 313 | - | ||
| 314 | - | ||
| 315 | - | ||
| 316 | - | ||
| 317 | - | ||
| 318 | - | ||
| 319 | - | ||
| 320 | - | ||
| 321 | - | ||
| 322 | - | ||
| 287 | + | uses | |
| 288 | + | FPImage, FPImgCanv, FPCanvas, FPWritePNG; | |
| 289 | + | var | |
| 290 | + | img: TFPMemoryImage; | |
| 291 | + | canvas: TFPImageCanvas; | |
| 292 | + | begin | |
| 293 | + | img := TFPMemoryImage.Create(200, 200); | |
| 294 | + | canvas := TFPImageCanvas.Create(img); | |
| 295 | + | try | |
| 296 | + | canvas.DrawingMode := dmAlphaBlend; // This activates the alpha-blend mode | |
| 297 | + | ||
| 298 | + | // Background | |
| 299 | + | canvas.Pen.Style := psClear; | |
| 300 | + | canvas.Brush.FPColor := colWhite; | |
| 301 | + | canvas.FillRect(0, 0, img.Width, img.Height); | |
| 302 | + | ||
| 303 | + | // Yellow opaque rectangle | |
| 304 | + | canvas.Brush.FPColor := FPColor($FFFF, $FFFF, 0); | |
| 305 | + | canvas.Rectangle(10, 10, 190, 100); | |
| 306 | + | ||
| 307 | + | // Overlapping semi-transparent red circle | |
| 308 | + | canvas.Brush.FPColor := FPColor($FFFF, 0, 0, $4000); | |
| 309 | + | canvas.Ellipse(60, 60, 140, 140); | |
| 310 | + | ||
| 311 | + | // Overlapping semi-transparent blue circle | |
| 312 | + | canvas.Brush.FPColor := FPColor(0, 0, $FFFF, $8000); | |
| 313 | + | canvas.Ellipse(0, 100, 100, 200); | |
| 314 | + | ||
| 315 | + | // Save to image file | |
| 316 | + | img.SaveToFile('alphablend.png'); | |
| 317 | + | finally | |
| 318 | + | canvas.Free; | |
| 319 | + | img.Free; | |
| 320 | + | end; | |
| 321 | + | end. | |
| 323 | 322 | ``` | |
| 324 | 323 | ||
| 325 | 324 | ### Brush Styles | |
| 326 | 325 | ||
| 327 | - | ||
| 326 | + | The **brush** defines how a closed area will be filled. There are several predefined styles to determine the fill pattern. | |
| 328 | 327 | ||
| 329 | - | ||
| 330 | - | ||
| 331 | - | ||
| 332 | - | ||
| 333 | - | ||
| 334 | - | ||
| 335 | - | ||
| 336 | - | ||
| 328 | + | * `bsSolid` \- fills the area with uniform color | |
| 329 | + | * `bsClear` \- no fill | |
| 330 | + | * `bsHorizontal` \- draws horizontal lines of width 1 across the shape to be filled | |
| 331 | + | * `bsVertical` \- vertical lines | |
| 332 | + | * `bsFDiagonal` \- diagonal lines (45°, \\\\\\) | |
| 333 | + | * `bsBDiagonal` \- diagonal lines (45°, ///) | |
| 334 | + | * `bsCross` \- crossed vertical and horizontal lines (+) | |
| 335 | + | * `bsDiagCross` \- crossed diagonal lines (x) | |
| 337 | 336 | ||
| 338 | - | ||
| 337 | + | The fill color is given by the `FPColor` property of the `Brush`. The space between the lines of the non-solid patterns is not painted, i.e. is left transparent. If the canvas on which the brush is used descends from `TFPPixelCanvas` (e.g. `TFPImageCanvas` in unit _fpimgcanv_ , or `TLazCanvas` in the LazUtils unit _lazcanvas_) the distance between the pattern lines can be adjusted by means of the `Canvas.HashWidth` property (Note that this does not apply to the `TCanvas` introduced by the Lazarus LCL.) | |
| 339 | 338 | ||
| 340 | - | ||
| 339 | + | Furthermore there are two additional possibilities for user-defined patterns: | |
| 341 | 340 | ||
| 342 | - | ||
| 343 | - | ||
| 341 | + | * `bsImage` \- tiles the area to be filled with copies of the image provided by the `Brush.Image` which must be a `TFPCustomImage` descendant, e.g. `TFPMemoryImage`. The colors of the image are retained, and its alpha channel is supported. By default the image position refers to the image origin, but if the canvas descends from `TFPPixelCanvas` (e.g. `TFPImageCanvas` in unit _fpimgcanv_ , or `TLazCanvas` in the LazUtils unit _lazcanvas_), it can be changed to be relative to the top/left shape bounding box by setting the property `Canvas.RelativeBrushImage` to `true`. Again this does not apply to the LCL `TCanvas` of Lazarus. | |
| 342 | + | * `bsPattern` \- provides a user-defined pattern in the `Brush.Pattern` property of type `TBrushPattern`. This is a 32-element array of 32-bit integers where every set bit is interpreted as a painted pixel using the `Brush.FPColor`; cleared bits are not painted and thus are transparent. | |
| 344 | 343 | ||
| 345 | 344 | **Example** | |
| 346 | 345 | ||
| 347 | - | ||
| 346 | + | The following mini project fills a rectangle by a user-defined blue checkerboard pattern, and saves the image as a transparent png file: | |
| 348 | 347 | ||
| 349 | - | ||
| 348 | + | [](</File:checkerboard_rectangle.png>) | |
| 349 | + | ``` | |
| 350 | 350 | program FillTest; | |
| 351 | - | ||
| 352 | - | ||
| 353 | - | ||
| 354 | - | ||
| 355 | - | ||
| 356 | - | ||
| 357 | - | ||
| 358 | - | ||
| 359 | - | ||
| 360 | - | ||
| 361 | - | ||
| 362 | - | ||
| 363 | - | ||
| 364 | - | ||
| 365 | - | ||
| 366 | - | ||
| 367 | - | ||
| 368 | - | ||
| 369 | - | ||
| 370 | - | ||
| 371 | - | ||
| 372 | - | ||
| 373 | - | ||
| 374 | - | ||
| 375 | - | ||
| 376 | - | ||
| 377 | - | ||
| 378 | - | ||
| 379 | - | ||
| 380 | - | ||
| 381 | - | ||
| 382 | - | ||
| 383 | - | ||
| 384 | - | ||
| 385 | - | ||
| 386 | - | ||
| 387 | - | ||
| 351 | + | uses | |
| 352 | + | FPImage, FPCanvas, FPPixlCanv, FPImgCanv, FPWritePNG; | |
| 353 | + | const | |
| 354 | + | BRUSH_PATTERN: TBrushPattern = ( | |
| 355 | + | $FF00FF00, $FF00FF00, $FF00FF00, $FF00FF00, | |
| 356 | + | $FF00FF00, $FF00FF00, $FF00FF00, $FF00FF00, | |
| 357 | + | $00FF00FF, $00FF00FF, $00FF00FF, $00FF00FF, | |
| 358 | + | $00FF00FF, $00FF00FF, $00FF00FF, $00FF00FF, | |
| 359 | + | $FF00FF00, $FF00FF00, $FF00FF00, $FF00FF00, | |
| 360 | + | $FF00FF00, $FF00FF00, $FF00FF00, $FF00FF00, | |
| 361 | + | $00FF00FF, $00FF00FF, $00FF00FF, $00FF00FF, | |
| 362 | + | $00FF00FF, $00FF00FF, $00FF00FF, $00FF00FF | |
| 363 | + | ); | |
| 364 | + | var | |
| 365 | + | image: TFPMemoryImage; | |
| 366 | + | canvas: TFPImageCanvas; | |
| 367 | + | writer: TFPCustomImageWriter; | |
| 368 | + | begin | |
| 369 | + | image := TFPMemoryImage.Create(100, 100); | |
| 388 | 370 | try | |
| 389 | - | ||
| 390 | - | ||
| 391 | - | ||
| 371 | + | canvas := TFPImageCanvas.Create(image); | |
| 372 | + | try | |
| 373 | + | // Prepare the brush for the shape interior | |
| 374 | + | canvas.Brush.Style := bsPattern; | |
| 375 | + | canvas.Brush.FPColor := colBlue; | |
| 376 | + | canvas.Brush.Pattern := BRUSH_PATTERN; | |
| 377 | + | ||
| 378 | + | // Prepare the pen for the shape border | |
| 379 | + | canvas.Pen.Style := psSolid; | |
| 380 | + | canvas.Pen.Width := 3; | |
| 381 | + | canvas.Pen.FPColor := colDkBlue; | |
| 382 | + | ||
| 383 | + | // Fill shape | |
| 384 | + | canvas.Rectangle(10, 10, 90, 90); | |
| 385 | + | ||
| 386 | + | // Save to file | |
| 387 | + | writer := TFPWriterPNG.Create; | |
| 388 | + | try | |
| 389 | + | // Activate alpha channel in saved image | |
| 390 | + | TFPWriterPNG(writer).UseAlpha := true; | |
| 391 | + | image.SaveToFile('rectangle.png', writer); | |
| 392 | + | finally | |
| 393 | + | writer.Free; | |
| 394 | + | end; | |
| 395 | + | ||
| 396 | + | finally | |
| 397 | + | canvas.Free; | |
| 398 | + | end; | |
| 399 | + | ||
| 392 | 400 | finally | |
| 393 | - | ||
| 401 | + | image.Free; | |
| 394 | 402 | end; | |
| 403 | + | end. | |
| 404 | + | ``` | |
| 395 | 405 | ||
| 396 | - | ||
| 397 | - | ||
| 398 | - | ||
| 406 | + | ### Drawing a Gradient | |
| 399 | 407 | ||
| 400 | - | ||
| 401 | - | ||
| 402 | - | ||
| 403 | - | ||
| 408 | + | Filling a rectangle by a horizontal or vertical gradient is rather easy - we just draw vertical or horizontal lines with varying colors: | |
| 404 | 409 | ``` | |
| 410 | + | uses | |
| 411 | + | FPImage, FPCanvas, FPImgCanv; | |
| 412 | + | ||
| 413 | + | type | |
| 414 | + | TGradientDir = (gdHorizontal, gdVertical); | |
| 415 | + | ||
| 416 | + | procedure GradientFill(Canvas: TFPCustomCanvas; ARect: TRect; AStartColor, AEndColor: TFPColor; ADirection: TGradientDir); | |
| 417 | + | ||
| 418 | + | function InterpolateColor(x, xmax: Integer): TFPColor; | |
| 419 | + | var | |
| 420 | + | factor1, factor2: Single; | |
| 421 | + | begin | |
| 422 | + | factor2 := x / xmax; | |
| 423 | + | factor1 := 1.0 - factor2; | |
| 424 | + | Result.Red := trunc(AStartColor.Red * factor1 + AEndColor.Red * factor2); | |
| 425 | + | Result.Green := trunc(AStartColor.Green * factor1 + AEndColor.Green * factor2); | |
| 426 | + | Result.Blue := trunc(AStartColor.Blue * factor1 + AEndColor.Blue * factor2); | |
| 427 | + | Result.Alpha := trunc(AStartColor.Alpha * factor1 + AEndColor.Alpha * factor2); | |
| 428 | + | end; | |
| 429 | + | ||
| 430 | + | var | |
| 431 | + | x, y, max: Integer; | |
| 432 | + | color: TFPColor; | |
| 433 | + | begin | |
| 434 | + | if ADirection = gdHorizontal then | |
| 435 | + | begin | |
| 436 | + | max := ARect.Right - ARect.Left; | |
| 437 | + | for x := ARect.Left to ARect.Right do | |
| 438 | + | begin | |
| 439 | + | color := InterpolateColor(x - ARect.Left, max); | |
| 440 | + | for y := ARect.Top to ARect.Bottom do | |
| 441 | + | Canvas.DrawPixel(x, y, color); | |
| 442 | + | end; | |
| 443 | + | end else | |
| 444 | + | begin | |
| 445 | + | max := ARect.Bottom - ARect.Top + 1; | |
| 446 | + | for y := ARect.Top to ARect.Bottom do | |
| 447 | + | begin | |
| 448 | + | color := InterpolateColor(y - ARect.Top, max); | |
| 449 | + | for x := ARect.Left to ARect.Right do | |
| 450 | + | Canvas.DrawPixel(x, y, color); | |
| 451 | + | end; | |
| 452 | + | end; | |
| 453 | + | end; | |
| 454 | + | ``` | |
| 405 | 455 | ||
| 406 | 456 | ### Pen Modes | |
| 407 | 457 | ||
| 408 | - | ||
| 458 | + | The pen mode is how the pixel drawn will react to the pixels beneath it. So if its a white pixel and you draw a red pixel over it with the mode of pmXor then you'll get a vibrate blue pixel. | |
| 409 | 459 | ||
| 410 | - | ||
| 411 | - | ||
| 412 | - | ||
| 413 | - | ||
| 414 | - | ||
| 415 | - | ||
| 416 | - | ||
| 417 | - | ||
| 418 | - | ||
| 419 | - | ||
| 420 | - | ||
| 421 | - | ||
| 422 | - | ||
| 423 | - | ||
| 424 | - | ||
| 425 | - | ||
| 460 | + | * `pmBlack` | |
| 461 | + | * `pmWhite` | |
| 462 | + | * `pmNop` | |
| 463 | + | * `pmNot` | |
| 464 | + | * `pmCopy` | |
| 465 | + | * `pmNotCopy` | |
| 466 | + | * `pmMergePenNot` | |
| 467 | + | * `pmMaskPenNot` | |
| 468 | + | * `pmMergeNotPen` | |
| 469 | + | * `pmMaskNotPen` | |
| 470 | + | * `pmMerge` | |
| 471 | + | * `pmNotMerge` | |
| 472 | + | * `pmMask` | |
| 473 | + | * `pmNotMask` | |
| 474 | + | * `pmXor` | |
| 475 | + | * `pmNotXor` | |
| 426 | 476 | ||
| 427 | 477 | ### Pen Styles | |
| 428 | 478 | ||
| 429 | - | ||
| 430 | - | ||
| 431 | - | ||
| 432 | - | ||
| 433 | - | ||
| 434 | - | ||
| 435 | - | ||
| 436 | - | ||
| 479 | + | * `psSolid` | |
| 480 | + | * `psDash` | |
| 481 | + | * `psDot` | |
| 482 | + | * `psDashDot` | |
| 483 | + | * `psDashDotDot` | |
| 484 | + | * `psinsideFrame` | |
| 485 | + | * `psPattern` | |
| 486 | + | * `psClear` | |
| 437 | 487 | ||
| 438 | 488 | ### Drawing modes | |
| 439 | 489 | ||
| 440 | - | ||
| 441 | - | ||
| 442 | - | ||
| 443 | - | ||
| 444 | - | ||
| 490 | + | * `dmOpaque` \-- painting over an existing image overwrites the pixels previously written. | |
| 491 | + | * `dmAlphaBlend` \-- painting over an existing image blends the old and new colors in the ratio of their `Alpha` value. | |
| 492 | + | * `dmCustom` \-- similar to `dmAlphaBlend`, but a function must be provided which defines how old and new colors are combined (`TFPCanvasCombineColors = function(const color1, color2: TFPColor): TFPColor of object;`). | |
| 445 | 493 | ||
| 446 | - | ||
| 494 | + | The following code applies the `dmAlphaBlend` `DrawingMode` to draw two semi-transparent circles over an opaque rectangle: | |
| 495 | + | ``` | |
| 447 | 496 | program alphablend; | |
| 448 | - | ||
| 449 | - | ||
| 450 | - | ||
| 451 | - | ||
| 452 | - | ||
| 453 | - | ||
| 454 | - | ||
| 455 | - | ||
| 456 | - | ||
| 457 | - | ||
| 458 | - | ||
| 459 | - | ||
| 460 | - | ||
| 461 | - | ||
| 462 | - | ||
| 463 | - | ||
| 464 | - | ||
| 465 | - | ||
| 466 | - | ||
| 467 | - | ||
| 468 | - | ||
| 469 | - | ||
| 470 | - | ||
| 471 | - | ||
| 472 | - | ||
| 473 | - | ||
| 474 | - | ||
| 475 | - | ||
| 476 | - | ||
| 477 | - | ||
| 478 | - | ||
| 479 | - | ||
| 480 | - | ||
| 481 | - | ||
| 497 | + | uses | |
| 498 | + | FPImage, FPImgCanv, FPCanvas, FPWritePNG; | |
| 499 | + | var | |
| 500 | + | img: TFPMemoryImage; | |
| 501 | + | canvas: TFPImageCanvas; | |
| 502 | + | begin | |
| 503 | + | img := TFPMemoryImage.Create(200, 200); | |
| 504 | + | canvas := TFPImageCanvas.Create(img); | |
| 505 | + | try | |
| 506 | + | canvas.DrawingMode := dmAlphaBlend; // This activates the alpha-blend mode | |
| 507 | + | ||
| 508 | + | // Background | |
| 509 | + | canvas.Pen.Style := psClear; | |
| 510 | + | canvas.Brush.FPColor := colWhite; | |
| 511 | + | canvas.FillRect(0, 0, img.Width, img.Height); | |
| 512 | + | ||
| 513 | + | // Yellow opaque rectangle | |
| 514 | + | canvas.Brush.FPColor := FPColor($FFFF, $FFFF, 0); | |
| 515 | + | canvas.Rectangle(10, 10, 190, 100); | |
| 516 | + | ||
| 517 | + | // Overlapping semi-transparent red circle | |
| 518 | + | canvas.Brush.FPColor := FPColor($FFFF, 0, 0, $4000); | |
| 519 | + | canvas.Ellipse(60, 60, 140, 140); | |
| 520 | + | ||
| 521 | + | // Overlapping semi-transparent blue circle | |
| 522 | + | canvas.Brush.FPColor := FPColor(0, 0, $FFFF, $8000); | |
| 523 | + | canvas.Ellipse(0, 100, 100, 200); | |
| 524 | + | ||
| 525 | + | img.SaveToFile('alphablend.png'); | |
| 526 | + | finally | |
| 527 | + | canvas.Free; | |
| 528 | + | img.Free; | |
| 529 | + | end; | |
| 530 | + | end. | |
| 482 | 531 | ``` | |
| 483 | 532 | ||
| 484 | 533 | ### Drawing text | |
| 485 | 534 | ||
| 486 | - | ||
| 487 | - | ||
| 488 | - | ||
| 535 | + | Here is an example, how to create a 200x100 image, painting a white background and some text and saving it as .png: | |
| 536 | + | ``` | |
| 489 | 537 | program fontdraw; | |
| 490 | - | ||
| 491 | - | ||
| 492 | - | ||
| 493 | - | ||
| 494 | - | ||
| 495 | - | ||
| 496 | - | ||
| 497 | - | ||
| 498 | - | ||
| 499 | - | ||
| 500 | - | ||
| 501 | - | ||
| 502 | - | ||
| 503 | - | ||
| 504 | - | ||
| 505 | - | ||
| 506 | - | ||
| 507 | - | ||
| 508 | - | ||
| 509 | - | ||
| 510 | - | ||
| 511 | - | ||
| 512 | - | ||
| 513 | - | ||
| 514 | - | ||
| 515 | - | ||
| 516 | - | ||
| 517 | - | ||
| 518 | - | ||
| 519 | - | ||
| 520 | - | ||
| 521 | - | ||
| 522 | - | ||
| 523 | - | ||
| 524 | - | ||
| 525 | - | ||
| 526 | - | ||
| 527 | - | ||
| 528 | - | ||
| 529 | - | ||
| 530 | - | ||
| 531 | - | ||
| 532 | - | ||
| 533 | - | ||
| 534 | - | ||
| 535 | - | ||
| 536 | - | ||
| 537 | - | ||
| 538 | - | ||
| 539 | - | ||
| 540 | - | ||
| 541 | - | ||
| 542 | - | ||
| 543 | - | ||
| 544 | - | ||
| 545 | - | ||
| 546 | - | ||
| 547 | - | ||
| 548 | - | ||
| 549 | - | ||
| 550 | - | ||
| 538 | + | ||
| 539 | + | {$mode objfpc}{$H+} | |
| 540 | + | ||
| 541 | + | uses | |
| 542 | + | Classes, SysUtils, FPimage, FPImgCanv, FTFont, FPWritePNG, FPCanvas; | |
| 543 | + | ||
| 544 | + | procedure TestFPImgFont; | |
| 545 | + | var | |
| 546 | + | Img: TFPMemoryImage; | |
| 547 | + | Writer: TFPWriterPNG; | |
| 548 | + | ms: TMemoryStream; | |
| 549 | + | ImgCanvas: TFPImageCanvas; | |
| 550 | + | AFont: TFreeTypeFont; | |
| 551 | + | begin | |
| 552 | + | Img:=nil; | |
| 553 | + | ImgCanvas:=nil; | |
| 554 | + | Writer:=nil; | |
| 555 | + | ms:=nil; | |
| 556 | + | AFont:=nil; | |
| 557 | + | try | |
| 558 | + | // initialize free type font manager | |
| 559 | + | FTFont.InitEngine; | |
| 560 | + | FontMgr.SearchPath:='/usr/share/fonts/truetype/ttf-dejavu/'; // not needed on Windows | |
| 561 | + | AFont:=TFreeTypeFont.Create; | |
| 562 | + | ||
| 563 | + | // create an image of width 200, height 100 | |
| 564 | + | Img:=TFPMemoryImage.Create(200,100); | |
| 565 | + | Img.UsePalette:=false; | |
| 566 | + | // create the canvas with the drawing operations | |
| 567 | + | ImgCanvas:=TFPImageCanvas.Create(Img); | |
| 568 | + | ||
| 569 | + | // paint white background | |
| 570 | + | ImgCanvas.Brush.FPColor:=colWhite; | |
| 571 | + | ImgCanvas.Brush.Style:=bsSolid; | |
| 572 | + | ImgCanvas.Rectangle(0,0,Img.Width-1,Img.Height-1); | |
| 573 | + | ||
| 574 | + | // paint text | |
| 575 | + | ImgCanvas.Font:=AFont; | |
| 576 | + | ImgCanvas.Font.Name:='DejaVuSans'; | |
| 577 | + | ImgCanvas.Font.Size:=20; | |
| 578 | + | ImgCanvas.TextOut(10,30,'Test'); | |
| 579 | + | ||
| 580 | + | // write image as png to memory stream | |
| 581 | + | Writer:=TFPWriterPNG.create; | |
| 582 | + | ms:=TMemoryStream.Create; | |
| 583 | + | writer.ImageWrite(ms,Img); | |
| 584 | + | // write memory stream to file | |
| 585 | + | ms.Position:=0; | |
| 586 | + | ms.SaveToFile('testfont.png'); | |
| 587 | + | finally | |
| 588 | + | AFont.Free; | |
| 589 | + | ms.Free; | |
| 590 | + | Writer.Free; | |
| 591 | + | ImgCanvas.Free; | |
| 592 | + | Img.Free; | |
| 593 | + | end; | |
| 594 | + | end; | |
| 595 | + | ||
| 596 | + | begin | |
| 597 | + | TestFPImgFont; | |
| 598 | + | end. | |
| 551 | 599 | ``` | |
| 552 | 600 | ||
| 553 | 601 | ### Reading an image file | |
| 554 | 602 | ||
| 555 | - | ||
| 556 | - | ||
| 557 | - | ||
| 603 | + | It is very easy to read image files with fcl-image, as the example below shows: | |
| 604 | + | ``` | |
| 558 | 605 | uses | |
| 559 | - | ||
| 560 | - | ||
| 561 | - | ||
| 562 | - | ||
| 563 | - | ||
| 564 | - | ||
| 565 | - | ||
| 566 | - | ||
| 567 | - | ||
| 568 | - | ||
| 606 | + | fpreadgif, fpimage; | |
| 607 | + | ||
| 608 | + | var | |
| 609 | + | image: TFPCustomImage; | |
| 610 | + | reader: TFPCustomImageReader; | |
| 611 | + | begin | |
| 612 | + | Image := TFPMemoryImage.Create(10, 10); | |
| 613 | + | Reader := TFPReaderGIF.Create; | |
| 614 | + | Image.LoadFromFile(AFileName, Reader); | |
| 615 | + | // ... | |
| 569 | 616 | ``` | |
| 570 | 617 | ||
| 571 | 618 | ### Converting between two raster image formats | |
| 572 | 619 | ||
| 573 | - | ||
| 574 | - | ||
| 575 | - | ||
| 620 | + | To convert simply open in one format and save in the desired one, like in this example: | |
| 621 | + | ``` | |
| 576 | 622 | uses | |
| 577 | - | ||
| 578 | - | ||
| 579 | - | ||
| 580 | - | ||
| 581 | - | ||
| 582 | - | ||
| 583 | - | ||
| 584 | - | ||
| 585 | - | ||
| 586 | - | ||
| 587 | - | ||
| 588 | - | ||
| 589 | - | ||
| 590 | - | ||
| 623 | + | fpreadgif, fpimage, fpwritebmp; | |
| 624 | + | ||
| 625 | + | var | |
| 626 | + | image: TFPCustomImage; | |
| 627 | + | reader: TFPCustomImageReader; | |
| 628 | + | writer: TFPCustomImageWriter; | |
| 629 | + | begin | |
| 630 | + | Image := TFPMemoryImage.Create(10, 10); | |
| 631 | + | Reader := TFPReaderGIF.Create; | |
| 632 | + | Writer := TFPWriterBMP.Create; | |
| 633 | + | ||
| 634 | + | Image.LoadFromFile(AFileName, Reader); | |
| 635 | + | Image.SaveToFile(ADestFileName, Writer); | |
| 636 | + | //... | |
| 591 | 637 | ``` | |
| 592 | 638 | ||
| 593 | 639 | ### Rescaling a bitmap image | |
| 594 | 640 | ||
| 595 | - | ||
| 596 | - | ||
| 597 | - | ||
| 641 | + | In order to rescale a bitmap image to a new size we need an interpolation for calculating the intermediate new pixels. FCL-Image provides several interpolation classes; in all of them neighboring pixels are combined in some specific way to calculate the new pixel. The visual appearance of the result image depends on the type of the interpolation. Generally reducing the image size results in better image qualtity and magnification of the image size. | |
| 598 | 642 | ||
| 599 | - | ||
| 643 | + | In order to access an interpolation class we need a instance of TFPCustomCanvas. A convenient one is TFPImageCanvas which closely cooperates with a TFPCustomImage. | |
| 644 | + | ``` | |
| 600 | 645 | program project1; | |
| 601 | - | ||
| 602 | - | ||
| 603 | - | ||
| 604 | - | ||
| 605 | - | ||
| 606 | - | ||
| 607 | - | ||
| 608 | - | ||
| 609 | - | ||
| 610 | - | ||
| 611 | - | ||
| 612 | - | ||
| 613 | - | ||
| 614 | - | ||
| 615 | - | ||
| 616 | - | ||
| 617 | - | ||
| 618 | - | ||
| 619 | - | ||
| 620 | - | ||
| 621 | - | ||
| 622 | - | ||
| 623 | - | ||
| 624 | - | ||
| 625 | - | ||
| 626 | - | ||
| 627 | - | ||
| 628 | - | ||
| 629 | - | ||
| 630 | - | ||
| 631 | - | ||
| 632 | - | ||
| 633 | - | ||
| 634 | - | ||
| 635 | - | ||
| 636 | - | ||
| 637 | - | ||
| 638 | - | ||
| 639 | - | ||
| 640 | - | ||
| 641 | - | ||
| 646 | + | uses | |
| 647 | + | FPImage, FPCanvas, FPImgCanv, FPReadJPEG, FPWriteJPEG; | |
| 648 | + | const | |
| 649 | + | FILE_NAME = 'cheetah.jpg'; | |
| 650 | + | MAX_SIZE = 512; | |
| 651 | + | var | |
| 652 | + | srcImg, destImg: TFPMemoryImage; | |
| 653 | + | canvas: TFPImageCanvas; | |
| 654 | + | scaleFactor: Double; | |
| 655 | + | begin | |
| 656 | + | // Create and load the source image | |
| 657 | + | srcImg := TFPMemoryImage.Create(0, 0); | |
| 658 | + | srcImg.LoadFromFile(FILE_NAME); | |
| 659 | + | ||
| 660 | + | // Calculate the image scaling factor | |
| 661 | + | if srcImg.Width > srcImg.Height then | |
| 662 | + | scaleFactor := MAX_SIZE / srcImg.Width | |
| 663 | + | else | |
| 664 | + | scaleFactor := MAX_SIZE / srcImg.Height; | |
| 665 | + | ||
| 666 | + | // Create the destination image in the requested size | |
| 667 | + | destimg := TFPMemoryImage.Create(round(srcImg.Width*scaleFactor), round(srcImg.Height*scaleFactor)); | |
| 668 | + | ||
| 669 | + | // Create a canvas for the destination image ... | |
| 670 | + | canvas := TFPImageCanvas.Create(destImg); | |
| 671 | + | ||
| 672 | + | // ... and an interpolation | |
| 673 | + | canvas.Interpolation := TFPBaseInterpolation.Create; | |
| 674 | + | ||
| 675 | + | // Use the interpolation to stretch the source image to the new size | |
| 676 | + | canvas.StretchDraw(0, 0, destImg.Width, destImg.Height, srcImg); | |
| 677 | + | ||
| 678 | + | // Save destination image to file | |
| 679 | + | destImg.SaveToFile('resized.jpg'); | |
| 680 | + | ||
| 681 | + | // Clean up | |
| 682 | + | canvas.Interpolation.Free; | |
| 683 | + | canvas.Free; | |
| 684 | + | destImg.Free; | |
| 685 | + | srcImg.Free; | |
| 686 | + | end. | |
| 642 | 687 | ``` | |
| 643 | 688 | ||
| 644 | 689 | ### Rotating a bitmap image by an arbitrary angle | |
| 645 | 690 | ||
| 646 | - | ||
| 647 | - | ||
| 648 | - | ||
| 691 | + | Using the `RotatePoint()` and `RotateRect()` functions in the Lazarus unit `GraphMath` it is rather easy to rotate the pixels in a bitmap by an arbitrary angle. `RotateRect()` is needed to calculate the size of the rotated image, and `RotatePoint` is used to calculate back to the unrotated pixel for each point in the rotated image. | |
| 692 | + | ``` | |
| 649 | 693 | uses | |
| 650 | - | ||
| 651 | - | ||
| 652 | - | ||
| 653 | - | ||
| 654 | - | ||
| 655 | - | ||
| 656 | - | ||
| 657 | - | ||
| 658 | - | ||
| 659 | - | ||
| 660 | - | ||
| 661 | - | ||
| 662 | - | ||
| 663 | - | ||
| 664 | - | ||
| 665 | - | ||
| 666 | - | ||
| 667 | - | ||
| 668 | - | ||
| 669 | - | ||
| 670 | - | ||
| 671 | - | ||
| 672 | - | ||
| 673 | - | ||
| 674 | - | ||
| 675 | - | ||
| 676 | - | ||
| 694 | + | Types, Math, FPImage, GraphMath; | |
| 695 | + | ||
| 696 | + | function CreateRotatedImage(AImage: TFPMemoryImage; Angle: Double): TFPMemoryImage; // Angle is in radians | |
| 697 | + | var | |
| 698 | + | R: TRect; | |
| 699 | + | x, y: Integer; | |
| 700 | + | C, P: TPoint; | |
| 701 | + | delta: TPoint; | |
| 677 | 702 | begin | |
| 678 | - | ||
| 679 | - | ||
| 680 | - | ||
| 681 | - | ||
| 682 | - | ||
| 683 | - | ||
| 703 | + | // Calculate bounds of the rotated image | |
| 704 | + | R := RotateRect(AImage.Width, AImage.Height, Angle); | |
| 705 | + | OffsetRect(R, -R.Left, -R.Top); | |
| 706 | + | ||
| 707 | + | // Calculate center of rotated image | |
| 708 | + | C := Point((R.Left + R.Right) div 2, (R.Top + R.Bottom) div 2); | |
| 709 | + | ||
| 710 | + | // Create the resulting image. | |
| 711 | + | Result := TFPMemoryImage.Create(R.Right - R.Left, R.Bottom - R.Top); | |
| 712 | + | ||
| 713 | + | // Correction needed to center the rotated pixels | |
| 714 | + | delta := Point((AImage.Width - Result.Width) div 2, (AImage.Height - Result.Height) div 2); | |
| 715 | + | ||
| 716 | + | // Iterate over all pixels in the result image and look up the corresponding | |
| 717 | + | // pixels in the unrotated image. Pixels in the rotated image which are outside | |
| 718 | + | // the unrotated image are set to be transparent. | |
| 719 | + | for y := 0 to Result.Height-1 do | |
| 720 | + | for x := 0 to Result.Width-1 do | |
| 721 | + | begin | |
| 722 | + | P := RotatePoint(Point(x, y) - C, -Angle) + C; | |
| 723 | + | P := P + delta; | |
| 724 | + | if (P.X >= 0) and (P.Y >= 0) and (P.X < AImage.Width) and (P.Y < AImage.Height) then | |
| 725 | + | Result.Colors[x, y] := AImage.Colors[P.X, P.Y] | |
| 726 | + | else | |
| 727 | + | Result.Colors[x, y] := colTransparent; | |
| 728 | + | end; | |
| 684 | 729 | end; | |
| 685 | - | ||
| 686 | 730 | ``` | |
| 687 | 731 | ||
| 688 | 732 | ### Writing an 8-bit paletted bitmap file | |
| 689 | 733 | ||
| 690 | - | ||
| 734 | + | The following section is based on forum discussion <https://forum.lazarus.freepascal.org/index.php/topic,63215.msg478472.html#msg478472>. | |
| 691 | 735 | ||
| 692 | 736 | For creating a bmp file with a color palette set the `UsePalette` property of the `TFPCustomImage` to `true`. Add the FPColors of the palette entries to the image's `Palette` property. For painting on a canvas define colors by the FPColor values in the same way as with an unpaletted image; the palette index of the requested color is determined internally. When a color is not found in the existing palette, the color is added automatically. If this is not wanted you can also specify the colors by means of the palette index: `canvas.Brush.FPColor := image.Palette[1]` | |
| 693 | - | ||
| 694 | - | ||
| 737 | + | ``` | |
| 695 | 738 | program PaletteBMP; | |
| 696 | - | ||
| 697 | - | ||
| 698 | - | ||
| 699 | - | ||
| 700 | - | ||
| 701 | - | ||
| 702 | - | ||
| 703 | - | ||
| 704 | - | ||
| 705 | - | ||
| 706 | - | ||
| 707 | - | ||
| 708 | - | ||
| 709 | - | ||
| 710 | - | ||
| 711 | - | ||
| 712 | - | ||
| 713 | - | ||
| 714 | - | ||
| 715 | - | ||
| 716 | - | ||
| 717 | - | ||
| 718 | - | ||
| 719 | - | ||
| 720 | - | ||
| 721 | - | ||
| 722 | - | ||
| 723 | - | ||
| 724 | - | ||
| 725 | - | ||
| 726 | - | ||
| 727 | - | ||
| 728 | - | ||
| 729 | - | ||
| 730 | - | ||
| 731 | - | ||
| 732 | - | ||
| 733 | - | ||
| 734 | - | ||
| 735 | - | ||
| 736 | - | ||
| 737 | - | ||
| 738 | - | ||
| 739 | - | ||
| 740 | - | ||
| 741 | - | ||
| 742 | - | ||
| 739 | + | ||
| 740 | + | {$mode objfpc}{$H+} | |
| 741 | + | ||
| 742 | + | uses | |
| 743 | + | classes, fpcanvas, fpimage, fpimgcanv, fpwritebmp; | |
| 744 | + | ||
| 745 | + | var | |
| 746 | + | img: TFPMemoryImage; | |
| 747 | + | cnv: TFPImageCanvas; | |
| 748 | + | writer: TFPCustomimageWriter; | |
| 749 | + | begin | |
| 750 | + | img := TFPMemoryImage.Create(100, 100); | |
| 751 | + | try | |
| 752 | + | img.UsePalette := true; // IMPORTANT! | |
| 753 | + | // colBlack is always available at index 0. | |
| 754 | + | img.Palette.Add(colYellow); // Index 1 | |
| 755 | + | img.Palette.Add(colBlue); // Index 2 | |
| 756 | + | img.Palette.Add(colGreen); // Index 3 | |
| 757 | + | ||
| 758 | + | cnv := TFPImageCanvas.Create(img); | |
| 759 | + | try | |
| 760 | + | cnv.Brush.FPColor := colYellow; | |
| 761 | + | cnv.Rectangle(10, 10, 45, 45); | |
| 762 | + | ||
| 763 | + | cnv.Brush.FPColor := colBlue; | |
| 764 | + | cnv.Rectangle(55, 10, 90, 45); | |
| 765 | + | ||
| 766 | + | cnv.Brush.FPColor := img.Palette[3]; | |
| 767 | + | cnv.Rectangle(10, 55, 45, 90); | |
| 768 | + | ||
| 769 | + | cnv.Brush.FPColor := colWhite; // not yet in palette | |
| 770 | + | cnv.Rectangle(55, 55, 90, 90); | |
| 771 | + | finally | |
| 772 | + | cnv.Free; | |
| 773 | + | end; | |
| 774 | + | ||
| 775 | + | writer := TFPWriterBMP.Create; | |
| 776 | + | TFPWriterBMP(writer).BitsPerPixel := 8; | |
| 777 | + | try | |
| 778 | + | img.SaveToFile('test-8bpp.bmp', writer); | |
| 779 | + | finally | |
| 780 | + | writer.Free; | |
| 781 | + | end; | |
| 782 | + | finally | |
| 783 | + | img.Free; | |
| 784 | + | end; | |
| 785 | + | end. | |
| 743 | 786 | ``` | |
| 744 | 787 | ||
| 745 | 788 | ### Color reduction | |
| 746 | 789 | ||
| 747 | - | ||
| 790 | + | When images are to be displayed on devices which support only a limited number of colors the number of distinct colors in the image must be reduced such that there is no significant loss in image quality. Two steps are involved in this process | |
| 748 | 791 | ||
| 749 | - | ||
| 750 | - | ||
| 792 | + | * color quantization: This creates a palette with a maximum allowed size and adds those colors that are a good approximation of all colors in the original image. | |
| 793 | + | * dithering: The palette colors are distributed over the image so that the original image is best approximated. | |
| 751 | 794 | ||
| 752 | - | ||
| 795 | + | For this purpose, fcl-image contains the units fpquantizer and fpditherer. The following code is adapted from the forum post <https://forum.lazarus.freepascal.org/index.php/topic,13468.msg70495>. It reduces the input image to an image with the specified bits per pixels (4 bits per pixel --> 16 colors, 8 bits per pixel --> 256 colors): | |
| 753 | 796 | ||
| 754 | - | ||
| 797 | + | [](</File:fcl-image_lenna_24bpp.png>) | |
| 755 | 798 | ||
| 756 | - | ||
| 799 | + | [](</File:fcl-image_lenna_8bpp.png>) | |
| 800 | + | ``` | |
| 757 | 801 | uses | |
| 758 | - | ||
| 759 | - | ||
| 760 | - | ||
| 761 | - | ||
| 762 | - | ||
| 763 | - | ||
| 764 | - | ||
| 765 | - | ||
| 766 | - | ||
| 767 | - | ||
| 768 | - | ||
| 769 | - | ||
| 770 | - | ||
| 771 | - | ||
| 772 | - | ||
| 773 | - | ||
| 774 | - | ||
| 775 | - | ||
| 776 | - | ||
| 777 | - | ||
| 778 | - | ||
| 779 | - | ||
| 780 | - | ||
| 781 | - | ||
| 782 | - | ||
| 783 | - | ||
| 784 | - | ||
| 785 | - | ||
| 786 | - | ||
| 787 | - | ||
| 788 | - | ||
| 789 | - | ||
| 790 | - | ||
| 791 | - | ||
| 802 | + | fpimage, fpimgcanv, fpreadbmp, fpwritebmp, fpquantizer, fpditherer; | |
| 803 | + | ||
| 804 | + | procedure ReduceColors(SourceFile, TargetFile:String; BitsPerPixel: Integer); | |
| 805 | + | var | |
| 806 | + | sourceImg, targetImg: TFPMemoryImage; | |
| 807 | + | reader: TFPReaderBMP; | |
| 808 | + | writer: TFPWriterBMP; | |
| 809 | + | quantizer: TFPMedianCutQuantizer = nil; | |
| 810 | + | palette: TFPPalette = nil; | |
| 811 | + | ditherer: TFPFloydSteinbergDitherer = nil; | |
| 812 | + | begin | |
| 813 | + | if FileExists(SourceFile) then begin | |
| 814 | + | sourceImg := TFPMemoryImage.Create(0,0); | |
| 815 | + | targetImg := TFPMemoryImage.Create(0,0); | |
| 816 | + | reader := TFPReaderBMP.Create; | |
| 817 | + | writer := TFPWriterBMP.Create; | |
| 818 | + | try | |
| 819 | + | sourceImg.LoadFromFile(SourceFile); | |
| 820 | + | quantizer := TFPMedianCutQuantizer.Create; | |
| 821 | + | quantizer.ColorNumber := 1 shl BitsPerPixel; | |
| 822 | + | quantizer.Add(sourceImg); | |
| 823 | + | palette := quantizer.Quantize; | |
| 824 | + | ditherer := TFPFloydSteinbergDitherer.Create(palette); | |
| 825 | + | ditherer.Dither(sourceImg, targetImg); | |
| 826 | + | writer.BitsPerPixel := BitsPerPixel; | |
| 827 | + | targetImg.SaveToFile(TargetFile, writer); | |
| 828 | + | finally | |
| 829 | + | ditherer.Free; | |
| 830 | + | palette.Free; | |
| 831 | + | quantizer.Free; | |
| 832 | + | writer.Free; | |
| 833 | + | reader.Free; | |
| 834 | + | targetImg.Free; | |
| 835 | + | sourceImg.Free; | |
| 836 | + | end; | |
| 837 | + | end; | |
| 792 | 838 | end; | |
| 793 | - | ||
| 794 | - | ||
| 795 | 839 | ``` | |
| 796 | 840 | ||
| 797 | - | ||
| 841 | + | The same code can be used to reduce the colored input image to a dithered black-and-white image. Since here the palette consists of black and white only, the quantizer can be omitted: | |
| 798 | 842 | ||
| 799 | - | ||
| 800 | - | ||
| 801 | - | ||
| 802 | - | ||
| 843 | + | [](</File:fcl-image_lenna_24bpp.png>) | |
| 803 | 844 | ||
| 804 | - | ||
| 805 | - | ||
| 806 | - | ||
| 807 | - | ||
| 808 | - | ||
| 809 | - | ||
| 810 | - | ||
| 811 | - | ||
| 812 | - | ||
| 813 | - | ||
| 814 | - | ||
| 815 | - | ||
| 816 | - | ||
| 817 | - | ||
| 818 | - | ||
| 819 | - | ||
| 820 | - | ||
| 821 | - | ||
| 822 | - | ||
| 823 | - | ||
| 824 | - | ||
| 825 | - | ||
| 826 | - | ||
| 827 | - | ||
| 828 | - | ||
| 829 | - | ||
| 830 | - | ||
| 831 | - | ||
| 832 | - | ||
| 833 | - | ||
| 845 | + | [](</File:fcl-image_lenna_1bpp.png>) | |
| 834 | 846 | ``` | |
| 847 | + | uses | |
| 848 | + | SysUtils, | |
| 849 | + | fpimage, fpimgcanv, fpreadbmp, fpwritebmp, fpditherer; | |
| 850 | + | ||
| 851 | + | procedure BlackAndWhite(SourceFile, TargetFile:String); | |
| 852 | + | var | |
| 853 | + | sourceImg, targetImg: TFPMemoryImage; | |
| 854 | + | reader: TFPReaderBMP; | |
| 855 | + | writer: TFPWriterBMP; | |
| 856 | + | palette: TFPPalette = nil; | |
| 857 | + | ditherer: TFPFloydSteinbergDitherer = nil; | |
| 858 | + | begin | |
| 859 | + | sourceImg := TFPMemoryImage.Create(0,0); | |
| 860 | + | targetImg := TFPMemoryImage.Create(0,0); | |
| 861 | + | reader := TFPReaderBMP.Create; | |
| 862 | + | writer := TFPWriterBMP.Create; | |
| 863 | + | try | |
| 864 | + | sourceImg.LoadFromFile(SourceFile); | |
| 865 | + | palette := TFPPalette.Create(2); | |
| 866 | + | palette.Add(colBlack); | |
| 867 | + | palette.Add(colWhite); | |
| 868 | + | ditherer := TFPFloydSteinbergDitherer.Create(palette); | |
| 869 | + | ditherer.Dither(sourceImg, targetImg); | |
| 870 | + | writer.BitsPerPixel := 1; | |
| 871 | + | targetImg.SaveToFile(TargetFile, writer); | |
| 872 | + | finally | |
| 873 | + | ditherer.Free; | |
| 874 | + | palette.Free; | |
| 875 | + | writer.Free; | |
| 876 | + | reader.Free; | |
| 877 | + | targetImg.Free; | |
| 878 | + | sourceImg.Free; | |
| 879 | + | end; | |
| 880 | + | end; | |
| 881 | + | ``` | |
| 835 | 882 | ||
| 836 | 883 | ### Gamma correction | |
| 837 | 884 | ||
| 838 | - | ||
| 839 | - | ||
| 840 | - | ||
| 885 | + | FCL-Image can be used for image transformations, such as **gamma correction** (adjustment of pixel values to modify brightness, see <https://en.wikipedia.org/wiki/Gamma_correction>): | |
| 886 | + | ``` | |
| 841 | 887 | uses | |
| 842 | - | ||
| 843 | - | ||
| 844 | - | ||
| 845 | - | ||
| 846 | - | ||
| 847 | - | ||
| 848 | - | ||
| 849 | - | ||
| 850 | - | ||
| 851 | - | ||
| 852 | - | ||
| 853 | - | ||
| 854 | - | ||
| 855 | - | ||
| 856 | - | ||
| 857 | - | ||
| 858 | - | ||
| 859 | - | ||
| 860 | - | ||
| 861 | - | ||
| 862 | - | ||
| 863 | - | ||
| 864 | - | ||
| 865 | - | ||
| 866 | - | ||
| 867 | - | ||
| 868 | - | ||
| 869 | - | ||
| 870 | - | ||
| 888 | + | fpimage, fpreadjpeg, fpwritejpeg, math; | |
| 889 | + | ||
| 890 | + | procedure GammaCorrection(const ASrcFile, ADestFile: String; AGamma: Double); | |
| 891 | + | var | |
| 892 | + | img: TFPMemoryImage; | |
| 893 | + | reader: TFPCustomImageReader; | |
| 894 | + | writer: TFPCustomImageWriter; | |
| 895 | + | i, j: Integer; | |
| 896 | + | clr: TFPColor; | |
| 897 | + | begin | |
| 898 | + | Assert(AGamma <> 0.0); | |
| 899 | + | ||
| 900 | + | img := TFPMemoryImage.Create(0, 0); | |
| 901 | + | try | |
| 902 | + | reader := TFPReaderJpeg.Create; | |
| 903 | + | try | |
| 904 | + | img.LoadFromFile(ASrcFile, reader); | |
| 905 | + | finally | |
| 906 | + | reader.Free; | |
| 907 | + | end; | |
| 908 | + | ||
| 909 | + | for j := 0 to img.Height - 1 do | |
| 910 | + | for i := 0 to img.Width - 1 do | |
| 911 | + | begin | |
| 912 | + | clr := img.Colors[i, j]; | |
| 913 | + | clr.Red := round(((clr.Red / 65535)**AGamma)*65535); | |
| 914 | + | clr.Green := round(((clr.Green / 65535)**AGamma)*65535); | |
| 915 | + | clr.Blue := round(((clr.Blue / 65535)**AGamma)*65535); | |
| 916 | + | img.Colors[i, j] := clr; | |
| 917 | + | end; | |
| 918 | + | ||
| 919 | + | writer := TFPWriterJpeg.Create; | |
| 920 | + | try | |
| 921 | + | img.SaveToFile(ADestFile, writer); | |
| 922 | + | finally | |
| 923 | + | writer.Free; | |
| 924 | + | end; | |
| 925 | + | finally | |
| 926 | + | img.Free; | |
| 871 | 927 | end; | |
| 872 | - | ||
| 873 | - | ||
| 874 | - | ||
| 875 | - | ||
| 876 | - | ||
| 877 | - | ||
| 878 | 928 | end; | |
| 879 | - | ||
| 880 | - | ||
| 881 | - | ||
| 882 | - | ||
| 883 | 929 | ``` | |
| 884 | 930 | ||
| 885 | 931 | ### Bar codes | |
| 886 | 932 | ||
| 887 | - | ||
| 933 | + | FCL-Image contains some units to create bar codes. Here is an example how to create a QRCode and save it as a graphics file: | |
| 888 | 934 | ||
| 889 | - | ||
| 935 | + | [](</File:fpc_output_qrcode_as_imagefile.png>) | |
| 936 | + | ``` | |
| 890 | 937 | program qrcode_as_imagefile; | |
| 891 | - | ||
| 892 | - | ||
| 893 | - | ||
| 894 | - | ||
| 895 | - | ||
| 896 | - | ||
| 897 | - | ||
| 898 | - | ||
| 899 | - | ||
| 900 | - | ||
| 901 | - | ||
| 902 | - | ||
| 903 | - | ||
| 904 | - | ||
| 905 | - | ||
| 906 | - | ||
| 907 | - | ||
| 908 | - | ||
| 909 | - | ||
| 938 | + | uses | |
| 939 | + | fpqrcodegen, fpimgqrcode, fpWritePng; | |
| 940 | + | const | |
| 941 | + | TEXT_STRING = 'This is a qrcode generated by fpc.'; | |
| 942 | + | FILE_NAME = 'qrcode.png'; | |
| 943 | + | var | |
| 944 | + | qrcode: TImageQRCodeGenerator; | |
| 945 | + | begin | |
| 946 | + | qrcode := TImageQRCodeGenerator.Create; | |
| 947 | + | try | |
| 948 | + | qrcode.PixelSize := 4; | |
| 949 | + | qrcode.Border := 8; | |
| 950 | + | qrcode.ErrorCorrectionLevel := EccHigh; | |
| 951 | + | qrcode.Generate(TEXT_STRING); | |
| 952 | + | qrcode.SaveToFile(FILE_NAME); | |
| 953 | + | finally | |
| 954 | + | qrcode.Free; | |
| 955 | + | end; | |
| 956 | + | end. | |
| 910 | 957 | ``` | |
| 911 | 958 | ||
| 912 | - | ||
| 959 | + | And the next example creates a "2-of-5-industrial" barcode: | |
| 913 | 960 | ||
| 914 | - | ||
| 915 | - | ||
| 916 | - | ||
| 917 | - | ||
| 918 | - | ||
| 919 | - | ||
| 920 | - | ||
| 921 | - | ||
| 922 | - | ||
| 923 | - | ||
| 924 | - | ||
| 925 | - | ||
| 926 | - | ||
| 927 | - | ||
| 928 | - | ||
| 929 | - | ||
| 930 | - | ||
| 931 | - | ||
| 932 | - | ||
| 933 | - | ||
| 934 | - | ||
| 935 | - | ||
| 936 | - | ||
| 937 | - | ||
| 938 | - | ||
| 939 | - | ||
| 940 | - | ||
| 941 | - | ||
| 942 | - | ||
| 943 | - | ||
| 944 | - | ||
| 945 | - | ||
| 946 | - | ||
| 947 | - | ||
| 948 | - | ||
| 949 | - | ||
| 950 | - | ||
| 951 | - | ||
| 961 | + | [](</File:fpc_output_code25_as_imagefile.png>) | |
| 952 | 962 | ``` | |
| 963 | + | program code25_as_image_file; | |
| 964 | + | uses | |
| 965 | + | Types, fpImage, fpBarCode, fpImgBarCode, fpWritePng; | |
| 966 | + | const | |
| 967 | + | HEIGHT = 60; | |
| 968 | + | NUMERIC_TEXT = '0123456789'; | |
| 969 | + | ENCODING = be2of5Industrial; | |
| 970 | + | UNIT_WIDTH = 2; | |
| 971 | + | WEIGHT = 4; | |
| 972 | + | FILE_NAME = 'barcode.png'; | |
| 973 | + | var | |
| 974 | + | barcode: TFPDrawBarcode; | |
| 975 | + | width: Integer; | |
| 976 | + | begin | |
| 977 | + | barcode := TFPDrawBarcode.Create; | |
| 978 | + | try | |
| 979 | + | barcode.Encoding := ENCODING; | |
| 980 | + | barcode.UnitWidth := UNIT_WIDTH; | |
| 981 | + | barcode.Weight := WEIGHT; | |
| 982 | + | barcode.Text := NUMERIC_TEXT; | |
| 983 | + | width := barcode.CalcWidth; | |
| 984 | + | barcode.Rect := Rect(0, 0, width, HEIGHT); | |
| 985 | + | barcode.Image := TFPCompactImgGray8Bit.Create(barcode.Rect.Width, barcode.Rect.Height); | |
| 986 | + | try | |
| 987 | + | if barcode.Draw then | |
| 988 | + | begin | |
| 989 | + | barcode.Image.SaveToFile(FILE_NAME); | |
| 990 | + | WriteLn('Saved to file ', FILE_NAME); | |
| 991 | + | end else | |
| 992 | + | WriteLn('Error generating barcode.'); | |
| 993 | + | finally | |
| 994 | + | barcode.Image.Free; | |
| 995 | + | end; | |
| 996 | + | finally | |
| 997 | + | barcode.Free; | |
| 998 | + | end; | |
| 999 | + | end. | |
| 1000 | + | ``` | |
| 953 | 1001 | ||
| 954 | 1002 | ### Getting image size without loading the image itself | |
| 955 | 1003 | ||
| 956 | - | ||
| 957 | - | ||
| 958 | - | ||
| 1004 | + | Answer by forum member **wp**. | |
| 959 | 1005 | ||
| 960 | - | ||
| 961 | - | ||
| 1006 | + | The basic image reader type, `TFPCustomImageReader`, is equipped with a class function `ImageSize: TPoint` which determines the image size in pixels without decoding the image data. In case of a jpeg image, the reader class is `TFPReaderJPEG` in unit `fpreadjpeg`; therefore, assuming that the image file is accessed via a stream, the image size can be obtained as a `TPoint` by a one-liner: | |
| 962 | 1007 | ``` | |
| 963 | - | ||
| 964 | - | ||
| 1008 | + | Size := TFPReaderJPEG.ImageSize(stream); | |
| 1009 | + | ``` | |
| 965 | 1010 | ||
| 966 | - | ||
| 1011 | + | If you have other image file formats you must pick the correct reader class from the fcl-image units. To help you, the basic fcl-image class, `TFPCustomImage`, has class methods `FindReaderFromStream(AStream)` which determines the required reader class from the file header or `FindReaderFromFileName(AFileName)` which determines the reader class from the extension of the filename. | |
| 967 | 1012 | ||
| 968 | - | ||
| 1013 | + | Putting these class methods together you can determine the image size for any image format supported by fcl-image by the following short function (do not forget to list the reader units in the uses clause for all file formats requested): | |
| 1014 | + | ``` | |
| 969 | 1015 | uses | |
| 970 | - | ||
| 971 | - | ||
| 972 | - | ||
| 973 | - | ||
| 974 | - | ||
| 975 | - | ||
| 976 | - | ||
| 977 | - | ||
| 978 | - | ||
| 979 | - | ||
| 980 | - | ||
| 981 | - | ||
| 982 | - | ||
| 1016 | + | [...] fpImage, fpReadJpeg, fpReadBMP, fpReadPNG, fpReadGIF, fpReadPCX, fpReadTIFF, fpReadTGA; | |
| 1017 | + | ||
| 1018 | + | function GetImageSize(const AFileName: String; out ASize: TPoint): Boolean; | |
| 1019 | + | var | |
| 1020 | + | stream: TStream; | |
| 1021 | + | readerClass: TFPCustomImageReaderClass; | |
| 983 | 1022 | begin | |
| 984 | - | ||
| 985 | - | ||
| 1023 | + | Result := false; | |
| 1024 | + | ASize := Point(-1, -1); | |
| 1025 | + | stream := TFileStream.Create(AFileName, fmOpenRead); | |
| 1026 | + | try | |
| 1027 | + | readerClass := TFPCustomImage.FindReaderFromStream(stream); | |
| 1028 | + | if readerClass <> nil then | |
| 1029 | + | begin | |
| 1030 | + | ASize := readerClass.ImageSize(stream); | |
| 1031 | + | Result := true; | |
| 1032 | + | end; | |
| 1033 | + | finally | |
| 1034 | + | stream.Free; | |
| 1035 | + | end; | |
| 986 | 1036 | end; | |
| 987 | - | ||
| 988 | - | ||
| 989 | - | ||
| 990 | - | ||
| 991 | 1037 | ``` | |
| 992 | 1038 | ||
| 1039 | + |  **Note:** It is not guaranteed that file readers from third-party graphics libraries (e.g. BGRABitmap) support this mechanism. If their reader class does not implement the ImageSize class method the result of the function above will be (-1,-1). | |
| 1040 | + | ||
| 993 | 1041 | ## See also | |
| 994 | 1042 | ||
| 995 | - | ||
| 996 | - | ||
| 997 | - | ||
| 1043 | + | * [[Package List|Packages List]] | |
| 1044 | + | * [[Developing with Graphics]] | |
| 1045 | + | * [Stretching a Canvas: Image support for Free Pascal](<https://www.freepascal.org/~michael/articles/#canvas>): In this extensive article by Michael Van Canneyt, the Free Pascal support for images and simple drawing support is presented. | |
| 998 | 1046 | ||
| 999 | 1047 | --- | |
| 1000 | - | ||
| 1001 | - | ||
| 1002 | - | ||
| 1003 | - | ||
| 1048 | + | *From the Free Pascal wiki, page "fcl-image", as the Internet Archive kept it on 2025-08-19, last changed on 2025-07-01. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
