| 1 | {
|
|---|
| 2 | /**************************************************************************\
|
|---|
| 3 | bgrabitmaptypes.pas
|
|---|
| 4 | -------------------
|
|---|
| 5 | This unit defines basic types and it must be
|
|---|
| 6 | included in the 'uses' clause.
|
|---|
| 7 |
|
|---|
| 8 | --> Include BGRABitmap and BGRABitmapTypes in the 'uses' clause.
|
|---|
| 9 | If you are using LCL types, add also BGRAGraphics unit.
|
|---|
| 10 |
|
|---|
| 11 | ****************************************************************************
|
|---|
| 12 | * *
|
|---|
| 13 | * This file is part of BGRABitmap library which is distributed under the *
|
|---|
| 14 | * modified LGPL. *
|
|---|
| 15 | * *
|
|---|
| 16 | * See the file COPYING.modifiedLGPL.txt, included in this distribution, *
|
|---|
| 17 | * for details about the copyright. *
|
|---|
| 18 | * *
|
|---|
| 19 | * This program is distributed in the hope that it will be useful, *
|
|---|
| 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|---|
| 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
|
|---|
| 22 | * *
|
|---|
| 23 | ****************************************************************************
|
|---|
| 24 | }
|
|---|
| 25 |
|
|---|
| 26 | unit BGRABitmapTypes;
|
|---|
| 27 |
|
|---|
| 28 | {$mode objfpc}{$H+}
|
|---|
| 29 | {$i bgrabitmap.inc}
|
|---|
| 30 |
|
|---|
| 31 | interface
|
|---|
| 32 |
|
|---|
| 33 | uses
|
|---|
| 34 | Classes, Types, BGRAGraphics,
|
|---|
| 35 | FPImage, FPImgCanv{$IFDEF BGRABITMAP_USE_LCL}, LCLType, GraphType, LResources{$ENDIF},
|
|---|
| 36 | BGRAMultiFileType;
|
|---|
| 37 |
|
|---|
| 38 | type
|
|---|
| 39 | TMultiFileContainer = BGRAMultiFileType.TMultiFileContainer;
|
|---|
| 40 | Int32or64 = {$IFDEF CPU64}Int64{$ELSE}LongInt{$ENDIF};
|
|---|
| 41 | UInt32or64 = {$IFDEF CPU64}UInt64{$ELSE}LongWord{$ENDIF};
|
|---|
| 42 | HDC = {$IFDEF BGRABITMAP_USE_LCL}LCLType.HDC{$ELSE}PtrUInt{$ENDIF};
|
|---|
| 43 |
|
|---|
| 44 | {=== Miscellaneous types ===}
|
|---|
| 45 |
|
|---|
| 46 | type
|
|---|
| 47 | {* Options when doing a floodfill (also called bucket fill) }
|
|---|
| 48 | TFloodfillMode = (
|
|---|
| 49 | {** Pixels that are filled are replaced }
|
|---|
| 50 | fmSet,
|
|---|
| 51 | {** Pixels that are filled are drawn upon with the fill color }
|
|---|
| 52 | fmDrawWithTransparency,
|
|---|
| 53 | {** Pixels that are filled are drawn upon to the extent that the color underneath is similar to
|
|---|
| 54 | the start color. The more different the different is, the less it is drawn upon }
|
|---|
| 55 | fmProgressive);
|
|---|
| 56 |
|
|---|
| 57 | {* Specifies how much smoothing is applied to the computation of the median }
|
|---|
| 58 | TMedianOption = (moNone, moLowSmooth, moMediumSmooth, moHighSmooth);
|
|---|
| 59 | {* Specifies the shape of a predefined blur }
|
|---|
| 60 | TRadialBlurType = (
|
|---|
| 61 | {** Gaussian-like, pixel importance decreases progressively }
|
|---|
| 62 | rbNormal,
|
|---|
| 63 | {** Disk blur, pixel importance does not decrease progressively }
|
|---|
| 64 | rbDisk,
|
|---|
| 65 | {** Pixel are considered when they are at a certain distance }
|
|---|
| 66 | rbCorona,
|
|---|
| 67 | {** Gaussian-like, but 10 times smaller than ''rbNormal'' }
|
|---|
| 68 | rbPrecise,
|
|---|
| 69 | {** Gaussian-like but simplified to be computed faster }
|
|---|
| 70 | rbFast,
|
|---|
| 71 | {** Box blur, pixel importance does not decrease progressively
|
|---|
| 72 | and the pixels are included when they are in a square.
|
|---|
| 73 | This is much faster than ''rbFast'' however you may get
|
|---|
| 74 | square shapes in the resulting image }
|
|---|
| 75 | rbBox);
|
|---|
| 76 |
|
|---|
| 77 | TEmbossOption = (eoTransparent, eoPreserveHue);
|
|---|
| 78 | TEmbossOptions = set of TEmbossOption;
|
|---|
| 79 |
|
|---|
| 80 | TTextLayout = BGRAGraphics.TTextLayout;
|
|---|
| 81 | TFontBidiMode = (fbmAuto, fbmLeftToRight, fbmRightToLeft);
|
|---|
| 82 | TBidiTextAlignment = (btaNatural, btaOpposite, btaLeftJustify, btaRightJustify, btaCenter);
|
|---|
| 83 |
|
|---|
| 84 | const
|
|---|
| 85 | RadialBlurTypeToStr: array[TRadialBlurType] of string =
|
|---|
| 86 | ('Normal','Disk','Corona','Precise','Fast','Box');
|
|---|
| 87 |
|
|---|
| 88 |
|
|---|
| 89 | tlTop = BGRAGraphics.tlTop;
|
|---|
| 90 | tlCenter = BGRAGraphics.tlCenter;
|
|---|
| 91 | tlBottom = BGRAGraphics.tlBottom;
|
|---|
| 92 |
|
|---|
| 93 | // checks the bounds of an image in the given clipping rectangle
|
|---|
| 94 | function CheckPutImageBounds(x, y, tx, ty: integer; out minxb, minyb, maxxb, maxyb, ignoreleft: integer; const cliprect: TRect): boolean;
|
|---|
| 95 |
|
|---|
| 96 | {==== Imported from GraphType ====}
|
|---|
| 97 | //if this unit is defined, otherwise
|
|---|
| 98 | //define here the types used by the library.
|
|---|
| 99 | {$IFDEF BGRABITMAP_USE_LCL}
|
|---|
| 100 | type
|
|---|
| 101 | { Order of the lines in an image }
|
|---|
| 102 | TRawImageLineOrder = GraphType.TRawImageLineOrder;
|
|---|
| 103 | { Order of the bits in a byte containing pixel values }
|
|---|
| 104 | TRawImageBitOrder = GraphType.TRawImageBitOrder;
|
|---|
| 105 | { Order of the bytes in a group of byte containing pixel values }
|
|---|
| 106 | TRawImageByteOrder = GraphType.TRawImageByteOrder;
|
|---|
| 107 | { Definition of a single line 3D bevel }
|
|---|
| 108 | TGraphicsBevelCut = GraphType.TGraphicsBevelCut;
|
|---|
| 109 |
|
|---|
| 110 | const
|
|---|
| 111 | riloTopToBottom = GraphType.riloTopToBottom; // The first line (line 0) is the top line
|
|---|
| 112 | riloBottomToTop = GraphType.riloBottomToTop; // The first line (line 0) is the bottom line
|
|---|
| 113 |
|
|---|
| 114 | riboBitsInOrder = GraphType.riboBitsInOrder; // Bit 0 is pixel 0
|
|---|
| 115 | riboReversedBits = GraphType.riboReversedBits; // Bit 0 is pixel 7 (Bit 1 is pixel 6, ...)
|
|---|
| 116 |
|
|---|
| 117 | riboLSBFirst = GraphType.riboLSBFirst; // least significant byte first (little endian)
|
|---|
| 118 | riboMSBFirst = GraphType.riboMSBFirst; // most significant byte first (big endian)
|
|---|
| 119 |
|
|---|
| 120 | fsSurface = GraphType.fsSurface; //type is defined as Graphics.TFillStyle
|
|---|
| 121 | fsBorder = GraphType.fsBorder;
|
|---|
| 122 |
|
|---|
| 123 | bvNone = GraphType.bvNone;
|
|---|
| 124 | bvLowered = GraphType.bvLowered;
|
|---|
| 125 | bvRaised = GraphType.bvRaised;
|
|---|
| 126 | bvSpace = GraphType.bvSpace;
|
|---|
| 127 | {$ELSE}
|
|---|
| 128 | type
|
|---|
| 129 | {* Order of the lines in an image }
|
|---|
| 130 | TRawImageLineOrder = (
|
|---|
| 131 | {** The first line in memory (line 0) is the top line }
|
|---|
| 132 | riloTopToBottom,
|
|---|
| 133 | {** The first line in memory (line 0) is the bottom line }
|
|---|
| 134 | riloBottomToTop);
|
|---|
| 135 |
|
|---|
| 136 | {* Order of the bits in a byte containing pixel values }
|
|---|
| 137 | TRawImageBitOrder = (
|
|---|
| 138 | {** The lowest bit is on the left. So with a monochrome picture, bit 0 would be pixel 0 }
|
|---|
| 139 | riboBitsInOrder,
|
|---|
| 140 | {** The lowest bit is on the right. So with a momochrome picture, bit 0 would be pixel 7 (bit 1 would be pixel 6, ...) }
|
|---|
| 141 | riboReversedBits);
|
|---|
| 142 |
|
|---|
| 143 | {* Order of the bytes in a group of byte containing pixel values }
|
|---|
| 144 | TRawImageByteOrder = (
|
|---|
| 145 | {** Least significant byte first (little endian) }
|
|---|
| 146 | riboLSBFirst,
|
|---|
| 147 | {** most significant byte first (big endian) }
|
|---|
| 148 | riboMSBFirst);
|
|---|
| 149 |
|
|---|
| 150 | {* Definition of a single line 3D bevel }
|
|---|
| 151 | TGraphicsBevelCut =
|
|---|
| 152 | (
|
|---|
| 153 | {** No bevel }
|
|---|
| 154 | bvNone,
|
|---|
| 155 | {** Shape is lowered, light is on the bottom-right corner }
|
|---|
| 156 | bvLowered,
|
|---|
| 157 | {** Shape is raised, light is on the top-left corner }
|
|---|
| 158 | bvRaised,
|
|---|
| 159 | {** Shape is at the same level, there is no particular lighting }
|
|---|
| 160 | bvSpace);
|
|---|
| 161 | {$ENDIF}
|
|---|
| 162 |
|
|---|
| 163 | {$DEFINE INCLUDE_INTERFACE}
|
|---|
| 164 | {$I bgrapixel.inc}
|
|---|
| 165 |
|
|---|
| 166 | {$DEFINE INCLUDE_INTERFACE}
|
|---|
| 167 | {$I geometrytypes.inc}
|
|---|
| 168 |
|
|---|
| 169 | {$DEFINE INCLUDE_INTERFACE}
|
|---|
| 170 | {$i csscolorconst.inc}
|
|---|
| 171 |
|
|---|
| 172 | {$DEFINE INCLUDE_SCANNER_INTERFACE }
|
|---|
| 173 | {$I bgracustombitmap.inc}
|
|---|
| 174 |
|
|---|
| 175 | {==== Integer math ====}
|
|---|
| 176 |
|
|---|
| 177 | {* Computes the value modulo cycle, and if the ''value'' is negative, the result
|
|---|
| 178 | is still positive }
|
|---|
| 179 | function PositiveMod(value, cycle: Int32or64): Int32or64; inline; overload;
|
|---|
| 180 |
|
|---|
| 181 | { Sin65536 and Cos65536 are fast routines to compute sine and cosine as integer values.
|
|---|
| 182 | They use a table to store already computed values. The return value is an integer
|
|---|
| 183 | ranging from 0 to 65536, so the mean value is 32768 and the half amplitude is
|
|---|
| 184 | 32768 instead of 1. The input has a period of 65536, so you can supply any integer
|
|---|
| 185 | without applying a modulo. }
|
|---|
| 186 |
|
|---|
| 187 | { Compute all values now }
|
|---|
| 188 | procedure PrecalcSin65536;
|
|---|
| 189 |
|
|---|
| 190 | {* Returns an integer approximation of the sine. Value ranges from 0 to 65535,
|
|---|
| 191 | where 65536 corresponds to the next cycle }
|
|---|
| 192 | function Sin65536(value: word): Int32or64; inline;
|
|---|
| 193 | {* Returns an integer approximation of the cosine. Value ranges from 0 to 65535,
|
|---|
| 194 | where 65536 corresponds to the next cycle }
|
|---|
| 195 | function Cos65536(value: word): Int32or64; inline;
|
|---|
| 196 |
|
|---|
| 197 | {* Returns the square root of the given byte, considering that
|
|---|
| 198 | 255 is equal to unity }
|
|---|
| 199 | function ByteSqrt(value: byte): byte; inline;
|
|---|
| 200 |
|
|---|
| 201 | {==== Types provided for fonts ====}
|
|---|
| 202 | type
|
|---|
| 203 | {* Quality to be used to render text }
|
|---|
| 204 | TBGRAFontQuality = (
|
|---|
| 205 | {** Use the system capabilities. It is rather fast however it may be
|
|---|
| 206 | not be smoothed. }
|
|---|
| 207 | fqSystem,
|
|---|
| 208 | {** Use the system capabilities to render with ClearType. This quality is
|
|---|
| 209 | of course better than fqSystem however it may not be perfect.}
|
|---|
| 210 | fqSystemClearType,
|
|---|
| 211 | {** Garanties a high quality antialiasing. }
|
|---|
| 212 | fqFineAntialiasing,
|
|---|
| 213 | {** Fine antialiasing with ClearType in assuming an LCD display in red/green/blue order }
|
|---|
| 214 | fqFineClearTypeRGB,
|
|---|
| 215 | {** Fine antialiasing with ClearType in assuming an LCD display in blue/green/red order }
|
|---|
| 216 | fqFineClearTypeBGR);
|
|---|
| 217 |
|
|---|
| 218 | {* Measurements of a font }
|
|---|
| 219 | TFontPixelMetric = record
|
|---|
| 220 | {** The values have been computed }
|
|---|
| 221 | Defined: boolean;
|
|---|
| 222 | {** Position of the baseline, where most letters lie }
|
|---|
| 223 | Baseline,
|
|---|
| 224 | {** Position of the top of the small letters (x being one of them) }
|
|---|
| 225 | xLine,
|
|---|
| 226 | {** Position of the top of the UPPERCASE letters }
|
|---|
| 227 | CapLine,
|
|---|
| 228 | {** Position of the bottom of letters like g and p }
|
|---|
| 229 | DescentLine,
|
|---|
| 230 | {** Total line height including line spacing defined by the font }
|
|---|
| 231 | Lineheight: integer;
|
|---|
| 232 | end;
|
|---|
| 233 |
|
|---|
| 234 | {* Vertical anchoring of the font. When text is drawn, a start coordinate
|
|---|
| 235 | is necessary. Text can be positioned in different ways. This enum
|
|---|
| 236 | defines what position it is regarding the font }
|
|---|
| 237 | TFontVerticalAnchor = (
|
|---|
| 238 | {** The top of the font. Everything will be drawn below the start coordinate. }
|
|---|
| 239 | fvaTop,
|
|---|
| 240 | {** The center of the font }
|
|---|
| 241 | fvaCenter,
|
|---|
| 242 | {** The top of capital letters }
|
|---|
| 243 | fvaCapLine,
|
|---|
| 244 | {** The center of capital letters }
|
|---|
| 245 | fvaCapCenter,
|
|---|
| 246 | {** The top of small letters }
|
|---|
| 247 | fvaXLine,
|
|---|
| 248 | {** The center of small letters }
|
|---|
| 249 | fvaXCenter,
|
|---|
| 250 | {** The baseline, the bottom of most letters }
|
|---|
| 251 | fvaBaseline,
|
|---|
| 252 | {** The bottom of letters that go below the baseline }
|
|---|
| 253 | fvaDescentLine,
|
|---|
| 254 | {** The bottom of the font. Everything will be drawn above the start coordinate }
|
|---|
| 255 | fvaBottom);
|
|---|
| 256 |
|
|---|
| 257 | {* Definition of a function that handles work-break }
|
|---|
| 258 | TWordBreakHandler = procedure(var ABeforeUTF8, AAfterUTF8: string) of object;
|
|---|
| 259 |
|
|---|
| 260 | {* Alignment for a typewriter, that does not have any more information
|
|---|
| 261 | than a square shape containing glyphs }
|
|---|
| 262 | TBGRATypeWriterAlignment = (twaTopLeft, twaTop, twaTopRight, twaLeft, twaMiddle, twaRight, twaBottomLeft, twaBottom, twaBottomRight);
|
|---|
| 263 | {* How a typewriter must render its content on a Canvas2d }
|
|---|
| 264 | TBGRATypeWriterOutlineMode = (twoPath, twoFill, twoStroke, twoFillOverStroke, twoStrokeOverFill, twoFillThenStroke, twoStrokeThenFill);
|
|---|
| 265 |
|
|---|
| 266 | { TBGRACustomFontRenderer }
|
|---|
| 267 | {* Abstract class for all font renderers }
|
|---|
| 268 | TBGRACustomFontRenderer = class
|
|---|
| 269 | {** Specifies the font to use. Unless the font renderer accept otherwise,
|
|---|
| 270 | the name is in human readable form, like 'Arial', 'Times New Roman', ... }
|
|---|
| 271 | FontName: string;
|
|---|
| 272 |
|
|---|
| 273 | {** Specifies the set of styles to be applied to the font.
|
|---|
| 274 | These can be fsBold, fsItalic, fsStrikeOut, fsUnderline.
|
|---|
| 275 | So the value [fsBold,fsItalic] means that the font must be bold and italic }
|
|---|
| 276 | FontStyle: TFontStyles;
|
|---|
| 277 |
|
|---|
| 278 | {** Specifies the quality of rendering. Default value is fqSystem }
|
|---|
| 279 | FontQuality : TBGRAFontQuality;
|
|---|
| 280 |
|
|---|
| 281 | {** Specifies the rotation of the text, for functions that support text rotation.
|
|---|
| 282 | It is expressed in tenth of degrees, positive values going counter-clockwise }
|
|---|
| 283 | FontOrientation: integer;
|
|---|
| 284 |
|
|---|
| 285 | {** Specifies the height of the font without taking into account additional line spacing.
|
|---|
| 286 | A negative value means that it is the full height instead }
|
|---|
| 287 | FontEmHeight: integer;
|
|---|
| 288 |
|
|---|
| 289 | {** Returns measurement for the current font in pixels }
|
|---|
| 290 | function GetFontPixelMetric: TFontPixelMetric; virtual; abstract;
|
|---|
| 291 |
|
|---|
| 292 | {** Returns the total size of the string provided using the current font.
|
|---|
| 293 | Orientation is not taken into account, so that the width is along the text }
|
|---|
| 294 | function TextSize(sUTF8: string): TSize; overload; virtual; abstract;
|
|---|
| 295 | function TextSize(sUTF8: string; AMaxWidth: integer; ARightToLeft: boolean): TSize; overload; virtual; abstract;
|
|---|
| 296 |
|
|---|
| 297 | function TextFitInfo(sUTF8: string; AMaxWidth: integer): integer; virtual; abstract;
|
|---|
| 298 | function TextSizeAngle(sUTF8: string; {%H-}orientationTenthDegCCW: integer): TSize; virtual;
|
|---|
| 299 |
|
|---|
| 300 | {** Draws the UTF8 encoded string, with color ''c''.
|
|---|
| 301 | If align is taLeftJustify, (''x'',''y'') is the top-left corner.
|
|---|
| 302 | If align is taCenter, (''x'',''y'') is at the top and middle of the text.
|
|---|
| 303 | If align is taRightJustify, (''x'',''y'') is the top-right corner.
|
|---|
| 304 | The value of ''FontOrientation'' is taken into account, so that the text may be rotated }
|
|---|
| 305 | procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment); overload; virtual; abstract;
|
|---|
| 306 | procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment; {%H-}ARightToLeft: boolean); overload; virtual;
|
|---|
| 307 |
|
|---|
| 308 | {** Same as above functions, except that the text is filled using texture.
|
|---|
| 309 | The value of ''FontOrientation'' is taken into account, so that the text may be rotated }
|
|---|
| 310 | procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment); overload; virtual; abstract;
|
|---|
| 311 | procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment; {%H-}ARightToLeft: boolean); overload; virtual;
|
|---|
| 312 |
|
|---|
| 313 | {** Same as above, except that the orientation is specified, overriding the value of the property ''FontOrientation'' }
|
|---|
| 314 | procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; c: TBGRAPixel; align: TAlignment); overload; virtual; abstract;
|
|---|
| 315 | {** Same as above, except that the orientation is specified, overriding the value of the property ''FontOrientation'' }
|
|---|
| 316 | procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientationTenthDegCCW: integer; sUTF8: string; texture: IBGRAScanner; align: TAlignment); overload; virtual; abstract;
|
|---|
| 317 |
|
|---|
| 318 | {** Draw the UTF8 encoded string at the coordinate (''x'',''y''), clipped inside the rectangle ''ARect''.
|
|---|
| 319 | Additional style information is provided by the style parameter.
|
|---|
| 320 | The color ''c'' is used to fill the text. No rotation is applied. }
|
|---|
| 321 | procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel); overload; virtual; abstract;
|
|---|
| 322 |
|
|---|
| 323 | {** Same as above except a ''texture'' is used to fill the text }
|
|---|
| 324 | procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; texture: IBGRAScanner); overload; virtual; abstract;
|
|---|
| 325 |
|
|---|
| 326 | {** Copy the path for the UTF8 encoded string into ''ADest''.
|
|---|
| 327 | If ''align'' is ''taLeftJustify'', (''x'',''y'') is the top-left corner.
|
|---|
| 328 | If ''align'' is ''taCenter'', (''x'',''y'') is at the top and middle of the text.
|
|---|
| 329 | If ''align'' is ''taRightJustify'', (''x'',''y'') is the top-right corner. }
|
|---|
| 330 | procedure CopyTextPathTo({%H-}ADest: IBGRAPath; {%H-}x, {%H-}y: single; {%H-}s: string; {%H-}align: TAlignment); virtual; //optional
|
|---|
| 331 | function HandlesTextPath: boolean; virtual;
|
|---|
| 332 | end;
|
|---|
| 333 |
|
|---|
| 334 | {* Output mode for the improved renderer for readability. This is used by the font renderer based on LCL in ''BGRAText'' }
|
|---|
| 335 | TBGRATextOutImproveReadabilityMode = (irMask, irNormal, irClearTypeRGB, irClearTypeBGR);
|
|---|
| 336 |
|
|---|
| 337 | {** Removes line ending and tab characters from a string (for a function
|
|---|
| 338 | like ''TextOut'' that does not handle this). this works with UTF8 strings
|
|---|
| 339 | as well }
|
|---|
| 340 | function CleanTextOutString(s: string): string;
|
|---|
| 341 | {** Remove the line ending at the specified position or return False.
|
|---|
| 342 | This works with UTF8 strings however the index is the byte index }
|
|---|
| 343 | function RemoveLineEnding(var s: string; indexByte: integer): boolean;
|
|---|
| 344 | {** Remove the line ending at the specified position or return False.
|
|---|
| 345 | The index is the character index, that may be different from the
|
|---|
| 346 | byte index }
|
|---|
| 347 | function RemoveLineEndingUTF8(var sUTF8: string; indexUTF8: integer): boolean;
|
|---|
| 348 | {** Default word break handler }
|
|---|
| 349 | procedure BGRADefaultWordBreakHandler(var ABefore, AAfter: string);
|
|---|
| 350 |
|
|---|
| 351 | {==== Images and resampling ====}
|
|---|
| 352 |
|
|---|
| 353 | type
|
|---|
| 354 | {* How the resample is to be computed }
|
|---|
| 355 | TResampleMode = (
|
|---|
| 356 | {** Low quality resample by repeating pixels, stretching them }
|
|---|
| 357 | rmSimpleStretch,
|
|---|
| 358 | {** Use resample filters. This gives high
|
|---|
| 359 | quality resampling however this the proportion changes slightly because
|
|---|
| 360 | the first and last pixel are considered to occupy only half a unit as
|
|---|
| 361 | they are considered as the border of the picture
|
|---|
| 362 | (pixel-centered coordinates) }
|
|---|
| 363 | rmFineResample);
|
|---|
| 364 |
|
|---|
| 365 | {* List of resample filter to be used with ''rmFineResample'' }
|
|---|
| 366 | TResampleFilter = (
|
|---|
| 367 | {** Equivalent of simple stretch with high quality and pixel-centered coordinates }
|
|---|
| 368 | rfBox,
|
|---|
| 369 | {** Linear interpolation giving slow transition between pixels }
|
|---|
| 370 | rfLinear,
|
|---|
| 371 | {** Mix of ''rfLinear'' and ''rfCosine'' giving medium speed stransition between pixels }
|
|---|
| 372 | rfHalfCosine,
|
|---|
| 373 | {** Cosine-like interpolation giving fast transition between pixels }
|
|---|
| 374 | rfCosine,
|
|---|
| 375 | {** Simple bi-cubic filter (blurry) }
|
|---|
| 376 | rfBicubic,
|
|---|
| 377 | {** Mitchell filter, good for downsizing interpolation }
|
|---|
| 378 | rfMitchell,
|
|---|
| 379 | {** Spline filter, good for upsizing interpolation, however slightly blurry }
|
|---|
| 380 | rfSpline,
|
|---|
| 381 | {** Lanczos with radius 2, blur is corrected }
|
|---|
| 382 | rfLanczos2,
|
|---|
| 383 | {** Lanczos with radius 3, high contrast }
|
|---|
| 384 | rfLanczos3,
|
|---|
| 385 | {** Lanczos with radius 4, high contrast }
|
|---|
| 386 | rfLanczos4,
|
|---|
| 387 | {** Best quality using rfMitchell or rfSpline }
|
|---|
| 388 | rfBestQuality);
|
|---|
| 389 |
|
|---|
| 390 | const
|
|---|
| 391 | {** List of strings to represent resample filters }
|
|---|
| 392 | ResampleFilterStr : array[TResampleFilter] of string =
|
|---|
| 393 | ('Box','Linear','HalfCosine','Cosine','Bicubic','Mitchell','Spline',
|
|---|
| 394 | 'Lanczos2','Lanczos3','Lanczos4','BestQuality');
|
|---|
| 395 |
|
|---|
| 396 | {** Gives the sample filter represented by a string }
|
|---|
| 397 | function StrToResampleFilter(str: string): TResampleFilter;
|
|---|
| 398 |
|
|---|
| 399 | type
|
|---|
| 400 | {* List of image formats }
|
|---|
| 401 | TBGRAImageFormat = (
|
|---|
| 402 | {** Unknown format }
|
|---|
| 403 | ifUnknown,
|
|---|
| 404 | {** JPEG format, opaque, lossy compression }
|
|---|
| 405 | ifJpeg,
|
|---|
| 406 | {** PNG format, transparency, lossless compression }
|
|---|
| 407 | ifPng,
|
|---|
| 408 | {** GIF format, single transparent color, lossless in theory but only low number of colors allowed }
|
|---|
| 409 | ifGif,
|
|---|
| 410 | {** BMP format, transparency, no compression. Note that transparency is
|
|---|
| 411 | not supported by all BMP readers so it is recommended to avoid
|
|---|
| 412 | storing images with transparency in this format }
|
|---|
| 413 | ifBmp,
|
|---|
| 414 | {** iGO BMP (16-bit, rudimentary lossless compression) }
|
|---|
| 415 | ifBmpMioMap,
|
|---|
| 416 | {** ICO format, contains different sizes of the same image }
|
|---|
| 417 | ifIco,
|
|---|
| 418 | {** CUR format, has hotspot, contains different sizes of the same image }
|
|---|
| 419 | ifCur,
|
|---|
| 420 | {** PCX format, opaque, rudimentary lossless compression }
|
|---|
| 421 | ifPcx,
|
|---|
| 422 | {** Paint.NET format, layers, lossless compression }
|
|---|
| 423 | ifPaintDotNet,
|
|---|
| 424 | {** LazPaint format, layers, lossless compression }
|
|---|
| 425 | ifLazPaint,
|
|---|
| 426 | {** OpenRaster format, layers, lossless compression }
|
|---|
| 427 | ifOpenRaster,
|
|---|
| 428 | {** Phoxo format, layers }
|
|---|
| 429 | ifPhoxo,
|
|---|
| 430 | {** Photoshop format, layers, rudimentary lossless compression }
|
|---|
| 431 | ifPsd,
|
|---|
| 432 | {** Targa format (TGA), transparency, rudimentary lossless compression }
|
|---|
| 433 | ifTarga,
|
|---|
| 434 | {** TIFF format, limited support }
|
|---|
| 435 | ifTiff,
|
|---|
| 436 | {** X-Window capture, limited support }
|
|---|
| 437 | ifXwd,
|
|---|
| 438 | {** X-Pixmap, text encoded image, limited support }
|
|---|
| 439 | ifXPixMap,
|
|---|
| 440 | {** Scalable Vector Graphic, vectorial, read-only as raster }
|
|---|
| 441 | ifSvg);
|
|---|
| 442 |
|
|---|
| 443 | {* Image information from superficial analysis }
|
|---|
| 444 | TQuickImageInfo = record
|
|---|
| 445 | {** Width in pixels }
|
|---|
| 446 | Width,
|
|---|
| 447 | {** Height in pixels }
|
|---|
| 448 | Height,
|
|---|
| 449 | {** Bitdepth for colors (1, 2, 4, 8 for images with palette/grayscale, 16, 24 or 48 if each channel is present) }
|
|---|
| 450 | ColorDepth,
|
|---|
| 451 | {** Bitdepth for alpha (0 if no alpha channel, 1 if bit mask, 8 or 16 if alpha channel) }
|
|---|
| 452 | AlphaDepth: integer;
|
|---|
| 453 | end;
|
|---|
| 454 |
|
|---|
| 455 | {* Bitmap reader with additional features }
|
|---|
| 456 | TBGRAImageReader = class(TFPCustomImageReader)
|
|---|
| 457 | {** Return bitmap information (size, bit depth) }
|
|---|
| 458 | function GetQuickInfo(AStream: TStream): TQuickImageInfo; virtual; abstract;
|
|---|
| 459 | {** Return a draft of the bitmap, the ratio may change compared to the original width and height (useful to make thumbnails) }
|
|---|
| 460 | function GetBitmapDraft(AStream: TStream; AMaxWidth, AMaxHeight: integer; out AOriginalWidth,AOriginalHeight: integer): TBGRACustomBitmap; virtual; abstract;
|
|---|
| 461 | end;
|
|---|
| 462 |
|
|---|
| 463 | {* Options when loading an image }
|
|---|
| 464 | TBGRALoadingOption = (
|
|---|
| 465 | {** Do not clear RGB channels when alpha is zero (not recommended) }
|
|---|
| 466 | loKeepTransparentRGB,
|
|---|
| 467 | {** Consider BMP to be opaque if no alpha value is provided (for compatibility) }
|
|---|
| 468 | loBmpAutoOpaque,
|
|---|
| 469 | {** Load JPEG quickly however with a lower quality }
|
|---|
| 470 | loJpegQuick);
|
|---|
| 471 | TBGRALoadingOptions = set of TBGRALoadingOption;
|
|---|
| 472 |
|
|---|
| 473 | var
|
|---|
| 474 | {** List of stream readers for images }
|
|---|
| 475 | DefaultBGRAImageReader: array[TBGRAImageFormat] of TFPCustomImageReaderClass;
|
|---|
| 476 | {** List of stream writers for images }
|
|---|
| 477 | DefaultBGRAImageWriter: array[TBGRAImageFormat] of TFPCustomImageWriterClass;
|
|---|
| 478 |
|
|---|
| 479 | {** Detect the file format of a given file }
|
|---|
| 480 | function DetectFileFormat(AFilenameUTF8: string): TBGRAImageFormat;
|
|---|
| 481 | {** Detect the file format of a given stream. ''ASuggestedExtensionUTF8'' can
|
|---|
| 482 | be provided to guess the format }
|
|---|
| 483 | function DetectFileFormat(AStream: TStream; ASuggestedExtensionUTF8: string = ''): TBGRAImageFormat;
|
|---|
| 484 | {** Returns the file format that is most likely to be stored in the
|
|---|
| 485 | given filename (according to its extension) }
|
|---|
| 486 | function SuggestImageFormat(AFilenameOrExtensionUTF8: string): TBGRAImageFormat;
|
|---|
| 487 | {** Returns a likely image extension for the format }
|
|---|
| 488 | function SuggestImageExtension(AFormat: TBGRAImageFormat): string;
|
|---|
| 489 | {** Create an image reader for the given format }
|
|---|
| 490 | function CreateBGRAImageReader(AFormat: TBGRAImageFormat): TFPCustomImageReader;
|
|---|
| 491 | {** Create an image writer for the given format. ''AHasTransparentPixels''
|
|---|
| 492 | specifies if alpha channel must be supported }
|
|---|
| 493 | function CreateBGRAImageWriter(AFormat: TBGRAImageFormat; AHasTransparentPixels: boolean): TFPCustomImageWriter;
|
|---|
| 494 |
|
|---|
| 495 | {$DEFINE INCLUDE_INTERFACE}
|
|---|
| 496 | {$I bgracustombitmap.inc}
|
|---|
| 497 |
|
|---|
| 498 | operator =(const AGuid1, AGuid2: TGuid): boolean;
|
|---|
| 499 |
|
|---|
| 500 | type
|
|---|
| 501 | { TBGRAResourceManager }
|
|---|
| 502 |
|
|---|
| 503 | TBGRAResourceManager = class
|
|---|
| 504 | protected
|
|---|
| 505 | function GetWinResourceType(AExtension: string): pchar;
|
|---|
| 506 | public
|
|---|
| 507 | function GetResourceStream(AFilename: string): TStream; virtual;
|
|---|
| 508 | function IsWinResource(AFilename: string): boolean; virtual;
|
|---|
| 509 | end;
|
|---|
| 510 |
|
|---|
| 511 | var
|
|---|
| 512 | BGRAResource : TBGRAResourceManager;
|
|---|
| 513 |
|
|---|
| 514 | implementation
|
|---|
| 515 |
|
|---|
| 516 | uses Math, SysUtils, BGRAUTF8, BGRAUnicode,
|
|---|
| 517 | FPReadXwd, FPReadXPM,
|
|---|
| 518 | FPWriteTiff, FPWriteJPEG, BGRAWritePNG, FPWriteBMP, FPWritePCX,
|
|---|
| 519 | FPWriteTGA, FPWriteXPM;
|
|---|
| 520 |
|
|---|
| 521 | {$DEFINE INCLUDE_IMPLEMENTATION}
|
|---|
| 522 | {$I geometrytypes.inc}
|
|---|
| 523 |
|
|---|
| 524 | {$DEFINE INCLUDE_IMPLEMENTATION}
|
|---|
| 525 | {$I csscolorconst.inc}
|
|---|
| 526 |
|
|---|
| 527 | {$DEFINE INCLUDE_IMPLEMENTATION}
|
|---|
| 528 | {$I bgracustombitmap.inc}
|
|---|
| 529 |
|
|---|
| 530 | {$DEFINE INCLUDE_IMPLEMENTATION}
|
|---|
| 531 | {$I bgrapixel.inc}
|
|---|
| 532 |
|
|---|
| 533 | function CleanTextOutString(s: string): string;
|
|---|
| 534 | var idxIn, idxOut: integer;
|
|---|
| 535 | begin
|
|---|
| 536 | setlength(result, length(s));
|
|---|
| 537 | idxIn := 1;
|
|---|
| 538 | idxOut := 1;
|
|---|
| 539 | while IdxIn <= length(s) do
|
|---|
| 540 | begin
|
|---|
| 541 | if not (s[idxIn] in[#13,#10,#9]) then //those characters are always 1 byte long so it is the same with UTF8
|
|---|
| 542 | begin
|
|---|
| 543 | result[idxOut] := s[idxIn];
|
|---|
| 544 | inc(idxOut);
|
|---|
| 545 | end;
|
|---|
| 546 | inc(idxIn);
|
|---|
| 547 | end;
|
|---|
| 548 | setlength(result, idxOut-1);
|
|---|
| 549 | end;
|
|---|
| 550 |
|
|---|
| 551 | function RemoveLineEnding(var s: string; indexByte: integer): boolean;
|
|---|
| 552 | begin //we can ignore UTF8 character length because #13 and #10 are always 1 byte long
|
|---|
| 553 | //so this function can be applied to UTF8 strings as well
|
|---|
| 554 | result := false;
|
|---|
| 555 | if length(s) >= indexByte then
|
|---|
| 556 | begin
|
|---|
| 557 | if s[indexByte] in[#13,#10] then
|
|---|
| 558 | begin
|
|---|
| 559 | result := true;
|
|---|
| 560 | if length(s) >= indexByte+1 then
|
|---|
| 561 | begin
|
|---|
| 562 | if (s[indexByte+1] <> s[indexByte]) and (s[indexByte+1] in[#13,#10]) then
|
|---|
| 563 | delete(s,indexByte,2)
|
|---|
| 564 | else
|
|---|
| 565 | delete(s,indexByte,1);
|
|---|
| 566 | end
|
|---|
| 567 | else
|
|---|
| 568 | delete(s,indexByte,1);
|
|---|
| 569 | end;
|
|---|
| 570 | end;
|
|---|
| 571 | end;
|
|---|
| 572 |
|
|---|
| 573 | function RemoveLineEndingUTF8(var sUTF8: string; indexUTF8: integer): boolean;
|
|---|
| 574 | var indexByte: integer;
|
|---|
| 575 | pIndex: PChar;
|
|---|
| 576 | begin
|
|---|
| 577 | pIndex := UTF8CharStart(@sUTF8[1],length(sUTF8),indexUTF8);
|
|---|
| 578 | if pIndex = nil then
|
|---|
| 579 | begin
|
|---|
| 580 | result := false;
|
|---|
| 581 | exit;
|
|---|
| 582 | end;
|
|---|
| 583 | indexByte := pIndex - @sUTF8[1];
|
|---|
| 584 | result := RemoveLineEnding(sUTF8, indexByte);
|
|---|
| 585 | end;
|
|---|
| 586 |
|
|---|
| 587 | procedure BGRADefaultWordBreakHandler(var ABefore, AAfter: string);
|
|---|
| 588 | const spacingChars = [' '];
|
|---|
| 589 | wordBreakChars = [' ',#9,'-','?','!'];
|
|---|
| 590 | var p, charLen: integer;
|
|---|
| 591 | u: Cardinal;
|
|---|
| 592 | begin
|
|---|
| 593 | if (AAfter <> '') and (ABefore <> '') and not (AAfter[1] in spacingChars) and not (ABefore[length(ABefore)] in wordBreakChars) then
|
|---|
| 594 | begin
|
|---|
| 595 | p := length(ABefore);
|
|---|
| 596 | while (p > 1) and not (ABefore[p-1] in wordBreakChars) do dec(p);
|
|---|
| 597 | while (p < length(ABefore)+1) and (ABefore[p] in [#$80..#$BF]) do inc(p); //do not split UTF8 char
|
|---|
| 598 | //keep non-spacing mark together
|
|---|
| 599 | while p <= length(ABefore) do
|
|---|
| 600 | begin
|
|---|
| 601 | charLen := UTF8CharacterLength(@ABefore[p]);
|
|---|
| 602 | if p+charLen > length(ABefore)+1 then charLen := length(ABefore)+1-p;
|
|---|
| 603 | u := UTF8CodepointToUnicode(@ABefore[p],charLen);
|
|---|
| 604 | if GetUnicodeBidiClass(u) = ubcNonSpacingMark then
|
|---|
| 605 | inc(p,charLen)
|
|---|
| 606 | else
|
|---|
| 607 | break;
|
|---|
| 608 | end;
|
|---|
| 609 |
|
|---|
| 610 | if p = 1 then
|
|---|
| 611 | begin
|
|---|
| 612 | //keep ideographic punctuation together
|
|---|
| 613 | charLen := UTF8CharacterLength(@AAfter[p]);
|
|---|
| 614 | if charLen > length(AAfter) then charLen := length(AAfter);
|
|---|
| 615 | u := UTF8CodepointToUnicode(@AAfter[p],charLen);
|
|---|
| 616 | case u of
|
|---|
| 617 | UNICODE_IDEOGRAPHIC_COMMA,
|
|---|
| 618 | UNICODE_IDEOGRAPHIC_FULL_STOP,
|
|---|
| 619 | UNICODE_FULLWIDTH_COMMA,
|
|---|
| 620 | UNICODE_HORIZONTAL_ELLIPSIS:
|
|---|
| 621 | begin
|
|---|
| 622 | p := length(ABefore)+1;
|
|---|
| 623 | while p > 1 do
|
|---|
| 624 | begin
|
|---|
| 625 | charLen := 1;
|
|---|
| 626 | dec(p);
|
|---|
| 627 | while (p > 0) and (ABefore[p] in [#$80..#$BF]) do
|
|---|
| 628 | begin
|
|---|
| 629 | dec(p); //do not split UTF8 char
|
|---|
| 630 | inc(charLen);
|
|---|
| 631 | end;
|
|---|
| 632 | if charLen <= 4 then
|
|---|
| 633 | u := UTF8CodepointToUnicode(@ABefore[p],charLen)
|
|---|
| 634 | else
|
|---|
| 635 | u := ord('A');
|
|---|
| 636 | case GetUnicodeBidiClass(u) of
|
|---|
| 637 | ubcNonSpacingMark: ; // include NSM
|
|---|
| 638 | ubcOtherNeutrals, ubcWhiteSpace, ubcCommonSeparator, ubcEuropeanNumberSeparator:
|
|---|
| 639 | begin
|
|---|
| 640 | p := 1;
|
|---|
| 641 | break;
|
|---|
| 642 | end
|
|---|
| 643 | else
|
|---|
| 644 | break;
|
|---|
| 645 | end;
|
|---|
| 646 | end;
|
|---|
| 647 | end;
|
|---|
| 648 | end;
|
|---|
| 649 | end;
|
|---|
| 650 |
|
|---|
| 651 | if p > 1 then //can put the word after
|
|---|
| 652 | begin
|
|---|
| 653 | AAfter := copy(ABefore,p,length(ABefore)-p+1)+AAfter;
|
|---|
| 654 | ABefore := copy(ABefore,1,p-1);
|
|---|
| 655 | end else
|
|---|
| 656 | begin //cannot put the word after, so before
|
|---|
| 657 |
|
|---|
| 658 | end;
|
|---|
| 659 | end;
|
|---|
| 660 | while (ABefore <> '') and (ABefore[length(ABefore)] in spacingChars) do delete(ABefore,length(ABefore),1);
|
|---|
| 661 | while (AAfter <> '') and (AAfter[1] in spacingChars) do delete(AAfter,1,1);
|
|---|
| 662 | end;
|
|---|
| 663 |
|
|---|
| 664 |
|
|---|
| 665 | function StrToResampleFilter(str: string): TResampleFilter;
|
|---|
| 666 | var f: TResampleFilter;
|
|---|
| 667 | begin
|
|---|
| 668 | result := rfLinear;
|
|---|
| 669 | str := LowerCase(str);
|
|---|
| 670 | for f := low(TResampleFilter) to high(TResampleFilter) do
|
|---|
| 671 | if CompareText(str,ResampleFilterStr[f])=0 then
|
|---|
| 672 | begin
|
|---|
| 673 | result := f;
|
|---|
| 674 | exit;
|
|---|
| 675 | end;
|
|---|
| 676 | end;
|
|---|
| 677 |
|
|---|
| 678 | { TBGRACustomFontRenderer }
|
|---|
| 679 |
|
|---|
| 680 | function TBGRACustomFontRenderer.TextSizeAngle(sUTF8: string;
|
|---|
| 681 | orientationTenthDegCCW: integer): TSize;
|
|---|
| 682 | begin
|
|---|
| 683 | result := TextSize(sUTF8); //ignore orientation by default
|
|---|
| 684 | end;
|
|---|
| 685 |
|
|---|
| 686 | procedure TBGRACustomFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
|
|---|
| 687 | y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment;
|
|---|
| 688 | ARightToLeft: boolean);
|
|---|
| 689 | begin
|
|---|
| 690 | //if RightToLeft is not handled
|
|---|
| 691 | TextOut(ADest,x,y,sUTF8,c,align);
|
|---|
| 692 | end;
|
|---|
| 693 |
|
|---|
| 694 | procedure TBGRACustomFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
|
|---|
| 695 | y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment;
|
|---|
| 696 | ARightToLeft: boolean);
|
|---|
| 697 | begin
|
|---|
| 698 | //if RightToLeft is not handled
|
|---|
| 699 | TextOut(ADest,x,y,sUTF8,texture,align);
|
|---|
| 700 | end;
|
|---|
| 701 |
|
|---|
| 702 | procedure TBGRACustomFontRenderer.CopyTextPathTo(ADest: IBGRAPath; x, y: single; s: string; align: TAlignment);
|
|---|
| 703 | begin {optional implementation} end;
|
|---|
| 704 |
|
|---|
| 705 | function TBGRACustomFontRenderer.HandlesTextPath: boolean;
|
|---|
| 706 | begin
|
|---|
| 707 | result := false;
|
|---|
| 708 | end;
|
|---|
| 709 |
|
|---|
| 710 |
|
|---|
| 711 | function CheckPutImageBounds(x, y, tx, ty: integer; out minxb, minyb, maxxb,
|
|---|
| 712 | maxyb, ignoreleft: integer; const cliprect: TRect): boolean;
|
|---|
| 713 | var x2,y2: integer;
|
|---|
| 714 | begin
|
|---|
| 715 | if (x >= cliprect.Right) or (y >= cliprect.Bottom) or (x <= cliprect.Left-tx) or
|
|---|
| 716 | (y <= cliprect.Top-ty) or (ty <= 0) or (tx <= 0) then
|
|---|
| 717 | begin
|
|---|
| 718 | result := false;
|
|---|
| 719 | exit;
|
|---|
| 720 | end;
|
|---|
| 721 |
|
|---|
| 722 | x2 := x + tx - 1;
|
|---|
| 723 | y2 := y + ty - 1;
|
|---|
| 724 |
|
|---|
| 725 | if y < cliprect.Top then
|
|---|
| 726 | minyb := cliprect.Top
|
|---|
| 727 | else
|
|---|
| 728 | minyb := y;
|
|---|
| 729 | if y2 >= cliprect.Bottom then
|
|---|
| 730 | maxyb := cliprect.Bottom - 1
|
|---|
| 731 | else
|
|---|
| 732 | maxyb := y2;
|
|---|
| 733 |
|
|---|
| 734 | if x < cliprect.Left then
|
|---|
| 735 | begin
|
|---|
| 736 | ignoreleft := cliprect.Left-x;
|
|---|
| 737 | minxb := cliprect.Left;
|
|---|
| 738 | end
|
|---|
| 739 | else
|
|---|
| 740 | begin
|
|---|
| 741 | ignoreleft := 0;
|
|---|
| 742 | minxb := x;
|
|---|
| 743 | end;
|
|---|
| 744 | if x2 >= cliprect.Right then
|
|---|
| 745 | maxxb := cliprect.Right - 1
|
|---|
| 746 | else
|
|---|
| 747 | maxxb := x2;
|
|---|
| 748 |
|
|---|
| 749 | result := true;
|
|---|
| 750 | end;
|
|---|
| 751 |
|
|---|
| 752 | {************************** Cyclic functions *******************}
|
|---|
| 753 |
|
|---|
| 754 | // Get the cyclic value in the range [0..cycle-1]
|
|---|
| 755 | function PositiveMod(value, cycle: Int32or64): Int32or64; inline;
|
|---|
| 756 | begin
|
|---|
| 757 | result := value mod cycle;
|
|---|
| 758 | if result < 0 then //modulo can be negative
|
|---|
| 759 | Inc(result, cycle);
|
|---|
| 760 | end;
|
|---|
| 761 |
|
|---|
| 762 | { Table of precalc values. Note : the value is stored for
|
|---|
| 763 | the first half of the cycle, and values are stored 'minus 1'
|
|---|
| 764 | in order to stay in the range 0..65535 }
|
|---|
| 765 | var
|
|---|
| 766 | sinTab65536: packed array of word;
|
|---|
| 767 | byteSqrtTab: packed array of word;
|
|---|
| 768 |
|
|---|
| 769 | function Sin65536(value: word): Int32or64;
|
|---|
| 770 | var b: integer;
|
|---|
| 771 | begin
|
|---|
| 772 | //allocate array
|
|---|
| 773 | if sinTab65536 = nil then
|
|---|
| 774 | setlength(sinTab65536,32768);
|
|---|
| 775 |
|
|---|
| 776 | if value >= 32768 then //function is upside down after half-period
|
|---|
| 777 | begin
|
|---|
| 778 | b := value xor 32768;
|
|---|
| 779 | if sinTab65536[b] = 0 then //precalc
|
|---|
| 780 | sinTab65536[b] := round((sin(b*2*Pi/65536)+1)*65536/2)-1;
|
|---|
| 781 | result := not sinTab65536[b];
|
|---|
| 782 | end else
|
|---|
| 783 | begin
|
|---|
| 784 | b := value;
|
|---|
| 785 | if sinTab65536[b] = 0 then //precalc
|
|---|
| 786 | sinTab65536[b] := round((sin(b*2*Pi/65536)+1)*65536/2)-1;
|
|---|
| 787 | {$hints off}
|
|---|
| 788 | result := sinTab65536[b]+1;
|
|---|
| 789 | {$hints on}
|
|---|
| 790 | end;
|
|---|
| 791 | end;
|
|---|
| 792 |
|
|---|
| 793 | function Cos65536(value: word): Int32or64;
|
|---|
| 794 | begin
|
|---|
| 795 | {$PUSH}{$R-}
|
|---|
| 796 | result := Sin65536(value+16384); //cosine is translated
|
|---|
| 797 | {$POP}
|
|---|
| 798 | end;
|
|---|
| 799 |
|
|---|
| 800 | procedure PrecalcSin65536;
|
|---|
| 801 | var
|
|---|
| 802 | i: Integer;
|
|---|
| 803 | begin
|
|---|
| 804 | for i := 0 to 32767 do Sin65536(i);
|
|---|
| 805 | end;
|
|---|
| 806 |
|
|---|
| 807 | procedure PrecalcByteSqrt;
|
|---|
| 808 | var i: integer;
|
|---|
| 809 | begin
|
|---|
| 810 | if byteSqrtTab = nil then
|
|---|
| 811 | begin
|
|---|
| 812 | setlength(byteSqrtTab,256);
|
|---|
| 813 | for i := 0 to 255 do
|
|---|
| 814 | byteSqrtTab[i] := round(sqrt(i/255)*255);
|
|---|
| 815 | end;
|
|---|
| 816 | end;
|
|---|
| 817 |
|
|---|
| 818 | function ByteSqrt(value: byte): byte; inline;
|
|---|
| 819 | begin
|
|---|
| 820 | if byteSqrtTab = nil then PrecalcByteSqrt;
|
|---|
| 821 | result := ByteSqrtTab[value];
|
|---|
| 822 | end;
|
|---|
| 823 |
|
|---|
| 824 | function DetectFileFormat(AFilenameUTF8: string): TBGRAImageFormat;
|
|---|
| 825 | var stream: TFileStreamUTF8;
|
|---|
| 826 | begin
|
|---|
| 827 | try
|
|---|
| 828 | stream := TFileStreamUTF8.Create(AFilenameUTF8,fmOpenRead or fmShareDenyWrite);
|
|---|
| 829 | except
|
|---|
| 830 | result := ifUnknown;
|
|---|
| 831 | exit;
|
|---|
| 832 | end;
|
|---|
| 833 | try
|
|---|
| 834 | result := DetectFileFormat(stream, ExtractFileExt(AFilenameUTF8));
|
|---|
| 835 | finally
|
|---|
| 836 | stream.Free;
|
|---|
| 837 | end;
|
|---|
| 838 | end;
|
|---|
| 839 |
|
|---|
| 840 | function DetectFileFormat(AStream: TStream; ASuggestedExtensionUTF8: string
|
|---|
| 841 | ): TBGRAImageFormat;
|
|---|
| 842 | var
|
|---|
| 843 | scores: array[TBGRAImageFormat] of integer;
|
|---|
| 844 | imageFormat,bestImageFormat: TBGRAImageFormat;
|
|---|
| 845 | bestScore: integer;
|
|---|
| 846 |
|
|---|
| 847 | procedure DetectFromStream;
|
|---|
| 848 | var
|
|---|
| 849 | {%H-}magic: packed array[0..7] of byte;
|
|---|
| 850 | {%H-}dwords: packed array[0..9] of DWORD;
|
|---|
| 851 | magicAsText: string;
|
|---|
| 852 |
|
|---|
| 853 | streamStartPos, maxFileSize: Int64;
|
|---|
| 854 | expectedFileSize: DWord;
|
|---|
| 855 |
|
|---|
| 856 | procedure DetectTarga;
|
|---|
| 857 | var
|
|---|
| 858 | paletteCount: integer;
|
|---|
| 859 | {%H-}targaPixelFormat: packed record pixelDepth: byte; imgDescriptor: byte; end;
|
|---|
| 860 | begin
|
|---|
| 861 | if (magic[1] in[$00,$01]) and (magic[2] in[0,1,2,3,9,10,11]) and (maxFileSize >= 18) then
|
|---|
| 862 | begin
|
|---|
| 863 | paletteCount:= magic[5] + magic[6] shl 8;
|
|---|
| 864 | if ((paletteCount = 0) and (magic[7] = 0)) or
|
|---|
| 865 | (magic[7] in [16,24,32]) then //check palette bit count
|
|---|
| 866 | begin
|
|---|
| 867 | AStream.Position:= streamStartPos+16;
|
|---|
| 868 | if AStream.Read({%H-}targaPixelFormat,2) = 2 then
|
|---|
| 869 | begin
|
|---|
| 870 | if (targaPixelFormat.pixelDepth in [8,16,24,32]) and
|
|---|
| 871 | (targaPixelFormat.imgDescriptor and 15 < targaPixelFormat.pixelDepth) then
|
|---|
| 872 | inc(scores[ifTarga],2);
|
|---|
| 873 | end;
|
|---|
| 874 | end;
|
|---|
| 875 | end;
|
|---|
| 876 | end;
|
|---|
| 877 |
|
|---|
| 878 | procedure DetectLazPaint;
|
|---|
| 879 | var
|
|---|
| 880 | w,h: dword;
|
|---|
| 881 | i: integer;
|
|---|
| 882 | begin
|
|---|
| 883 | if (copy(magicAsText,1,8) = 'LazPaint') then //with header
|
|---|
| 884 | begin
|
|---|
| 885 | AStream.Position:= streamStartPos+8;
|
|---|
| 886 | if AStream.Read(dwords,10*4) = 10*4 then
|
|---|
| 887 | begin
|
|---|
| 888 | for i := 0 to 6 do dwords[i] := LEtoN(dwords[i]);
|
|---|
| 889 | if (dwords[0] = 0) and (dwords[1] <= maxFileSize) and (dwords[5] <= maxFileSize) and
|
|---|
| 890 | (dwords[9] <= maxFileSize) and
|
|---|
| 891 | (dwords[6] = 0) then inc(scores[ifLazPaint],2);
|
|---|
| 892 | end;
|
|---|
| 893 | end else //without header
|
|---|
| 894 | if ((magic[0] <> 0) or (magic[1] <> 0)) and (magic[2] = 0) and (magic[3] = 0) and
|
|---|
| 895 | ((magic[4] <> 0) or (magic[5] <> 0)) and (magic[6] = 0) and (magic[7] = 0) then
|
|---|
| 896 | begin
|
|---|
| 897 | w := magic[0] + (magic[1] shl 8);
|
|---|
| 898 | h := magic[4] + (magic[5] shl 8);
|
|---|
| 899 | AStream.Position:= streamStartPos+8;
|
|---|
| 900 | if AStream.Read(dwords,4) = 4 then
|
|---|
| 901 | begin
|
|---|
| 902 | dwords[0] := LEtoN(dwords[0]);
|
|---|
| 903 | if (dwords[0] > 0) and (dwords[0] < 65536) then
|
|---|
| 904 | begin
|
|---|
| 905 | if 12+dwords[0] < expectedFileSize then
|
|---|
| 906 | begin
|
|---|
| 907 | AStream.Position:= streamStartPos+12+dwords[0];
|
|---|
| 908 | if AStream.Read(dwords,6*4) = 6*4 then
|
|---|
| 909 | begin
|
|---|
| 910 | for i := 0 to 5 do dwords[i] := LEtoN(dwords[i]);
|
|---|
| 911 | if (dwords[0] <= w) and (dwords[1] <= h) and
|
|---|
| 912 | (dwords[2] <= w) and (dwords[3] <= h) and
|
|---|
| 913 | (dwords[2] >= dwords[0]) and (dwords[3] >= dwords[1]) and
|
|---|
| 914 | ((dwords[4] = 0) or (dwords[4] = 1)) and
|
|---|
| 915 | (dwords[5] > 0) then inc(scores[ifLazPaint],1);
|
|---|
| 916 | end;
|
|---|
| 917 | end;
|
|---|
| 918 | end;
|
|---|
| 919 | end;
|
|---|
| 920 | end;
|
|---|
| 921 | end;
|
|---|
| 922 |
|
|---|
| 923 | begin
|
|---|
| 924 | fillchar({%H-}magic, sizeof(magic), 0);
|
|---|
| 925 | fillchar({%H-}dwords, sizeof(dwords), 0);
|
|---|
| 926 |
|
|---|
| 927 | streamStartPos:= AStream.Position;
|
|---|
| 928 | maxFileSize:= AStream.Size - streamStartPos;
|
|---|
| 929 | if maxFileSize < 8 then exit;
|
|---|
| 930 | if AStream.Read(magic,sizeof(magic)) <> sizeof(magic) then
|
|---|
| 931 | begin
|
|---|
| 932 | fillchar(scores,sizeof(scores),0);
|
|---|
| 933 | exit;
|
|---|
| 934 | end;
|
|---|
| 935 | setlength(magicAsText,sizeof(magic));
|
|---|
| 936 | move(magic[0],magicAsText[1],sizeof(magic));
|
|---|
| 937 |
|
|---|
| 938 | if (magic[0] = $ff) and (magic[1] = $d8) then
|
|---|
| 939 | begin
|
|---|
| 940 | inc(scores[ifJpeg]);
|
|---|
| 941 | if (magic[2] = $ff) and (magic[3] >= $c0) then inc(scores[ifJpeg]);
|
|---|
| 942 | end;
|
|---|
| 943 |
|
|---|
| 944 | if (magic[0] = $89) and (magic[1] = $50) and (magic[2] = $4e) and
|
|---|
| 945 | (magic[3] = $47) and (magic[4] = $0d) and (magic[5] = $0a) and
|
|---|
| 946 | (magic[6] = $1a) and (magic[7] = $0a) then inc(scores[ifPng],2);
|
|---|
| 947 |
|
|---|
| 948 | if (copy(magicAsText,1,6)='GIF87a') or (copy(magicAsText,1,6)='GIF89a') then inc(scores[ifGif],2);
|
|---|
| 949 |
|
|---|
| 950 | if (magic[0] = $0a) and (magic[1] in [0,2,3,4,5]) and (magic[2] in[0,1]) and (magic[3] in[1,2,4,8]) then
|
|---|
| 951 | inc(scores[ifPcx],2);
|
|---|
| 952 |
|
|---|
| 953 | if (copy(magicAsText,1,2)='BM') then
|
|---|
| 954 | begin
|
|---|
| 955 | inc(scores[ifBmp]);
|
|---|
| 956 | expectedFileSize:= magic[2] + (magic[3] shl 8) + (magic[4] shl 16) + (magic[5] shl 24);
|
|---|
| 957 | if expectedFileSize = maxFileSize then inc(scores[ifBmp]);
|
|---|
| 958 | end else
|
|---|
| 959 | if (copy(magicAsText,1,2)='RL') then
|
|---|
| 960 | begin
|
|---|
| 961 | inc(scores[ifBmpMioMap]);
|
|---|
| 962 | if (magic[2] in[0,1]) and (magic[3] = 0) then inc(scores[ifBmpMioMap]);
|
|---|
| 963 | end;
|
|---|
| 964 |
|
|---|
| 965 | if (magic[0] = $00) and (magic[1] = $00) and (magic[3] = $00) and
|
|---|
| 966 | (magic[4] + (magic[5] shl 8) > 0) then
|
|---|
| 967 | begin
|
|---|
| 968 | if magic[2] = $01 then
|
|---|
| 969 | inc(scores[ifIco])
|
|---|
| 970 | else if magic[2] = $02 then
|
|---|
| 971 | inc(scores[ifCur]);
|
|---|
| 972 | end;
|
|---|
| 973 |
|
|---|
| 974 | if (copy(magicAsText,1,4) = 'PDN3') then
|
|---|
| 975 | begin
|
|---|
| 976 | expectedFileSize:= 6 + (magic[4] + (magic[5] shl 8) + (magic[6] shl 16)) + 2;
|
|---|
| 977 | if expectedFileSize <= maxFileSize then
|
|---|
| 978 | begin
|
|---|
| 979 | inc(scores[ifPaintDotNet]);
|
|---|
| 980 | if magic[7] = $3c then inc(scores[ifPaintDotNet]);
|
|---|
| 981 | end;
|
|---|
| 982 | end;
|
|---|
| 983 |
|
|---|
| 984 | if (copy(magicAsText,1,4) = 'oXo ') then
|
|---|
| 985 | begin
|
|---|
| 986 | inc(scores[ifPhoxo],1);
|
|---|
| 987 | if (magic[4] = 1) and (magic[5] = 0) and (magic[6] = 0) and (magic[7] = 0) then
|
|---|
| 988 | inc(scores[ifPhoxo],1);
|
|---|
| 989 | end;
|
|---|
| 990 |
|
|---|
| 991 | DetectLazPaint;
|
|---|
| 992 |
|
|---|
| 993 | if (magic[0] = $50) and (magic[1] = $4b) and (magic[2] = $03) and (magic[3] = $04) then
|
|---|
| 994 | begin
|
|---|
| 995 | if DefaultBGRAImageReader[ifOpenRaster] = nil then inc(scores[ifOpenRaster]) else
|
|---|
| 996 | with CreateBGRAImageReader(ifOpenRaster) do
|
|---|
| 997 | try
|
|---|
| 998 | AStream.Position := streamStartPos;
|
|---|
| 999 | if CheckContents(AStream) then inc(scores[ifOpenRaster],2);
|
|---|
| 1000 | finally
|
|---|
| 1001 | Free;
|
|---|
| 1002 | end;
|
|---|
| 1003 | end;
|
|---|
| 1004 |
|
|---|
| 1005 | if (copy(magicAsText,1,4) = '8BPS') and (magic[4] = $00) and (magic[5] = $01) then inc(scores[ifPsd],2);
|
|---|
| 1006 |
|
|---|
| 1007 | DetectTarga;
|
|---|
| 1008 |
|
|---|
| 1009 | if (copy(magicAsText,1,2)='II') and (magic[2] = 42) and (magic[3]=0) then inc(scores[ifTiff]) else
|
|---|
| 1010 | if (copy(magicAsText,1,2)='MM') and (magic[2] = 0) and (magic[3]=42) then inc(scores[ifTiff]);
|
|---|
| 1011 |
|
|---|
| 1012 | if (copy(magicAsText,1,8) = '/* XPM *') or (copy(magicAsText,1,6) = '! XPM2') then inc(scores[ifXPixMap]);
|
|---|
| 1013 |
|
|---|
| 1014 | if (copy(magicAsText,1,6) = '<?xml ') then inc(scores[ifSvg]);
|
|---|
| 1015 |
|
|---|
| 1016 | AStream.Position := streamStartPos;
|
|---|
| 1017 | end;
|
|---|
| 1018 |
|
|---|
| 1019 | var
|
|---|
| 1020 | extFormat: TBGRAImageFormat;
|
|---|
| 1021 |
|
|---|
| 1022 | begin
|
|---|
| 1023 | result := ifUnknown;
|
|---|
| 1024 | for imageFormat:= low(TBGRAImageFormat) to high(TBGRAImageFormat) do
|
|---|
| 1025 | scores[imageFormat] := 0;
|
|---|
| 1026 |
|
|---|
| 1027 | ASuggestedExtensionUTF8:= UTF8LowerCase(ASuggestedExtensionUTF8);
|
|---|
| 1028 | if (ASuggestedExtensionUTF8 <> '') and (ASuggestedExtensionUTF8[1] <> '.') then //first UTF8 char is in first pos
|
|---|
| 1029 | ASuggestedExtensionUTF8 := '.'+ASuggestedExtensionUTF8;
|
|---|
| 1030 |
|
|---|
| 1031 | extFormat:= SuggestImageFormat(ASuggestedExtensionUTF8);
|
|---|
| 1032 | if extFormat <> ifUnknown then inc(scores[extFormat]);
|
|---|
| 1033 |
|
|---|
| 1034 | If AStream <> nil then DetectFromStream;
|
|---|
| 1035 |
|
|---|
| 1036 | bestScore := 0;
|
|---|
| 1037 | bestImageFormat:= ifUnknown;
|
|---|
| 1038 | for imageFormat:=low(TBGRAImageFormat) to high(TBGRAImageFormat) do
|
|---|
| 1039 | if scores[imageFormat] > bestScore then
|
|---|
| 1040 | begin
|
|---|
| 1041 | bestScore:= scores[imageFormat];
|
|---|
| 1042 | bestImageFormat:= imageFormat;
|
|---|
| 1043 | end;
|
|---|
| 1044 | result := bestImageFormat;
|
|---|
| 1045 | end;
|
|---|
| 1046 |
|
|---|
| 1047 | function SuggestImageFormat(AFilenameOrExtensionUTF8: string): TBGRAImageFormat;
|
|---|
| 1048 | var ext: string;
|
|---|
| 1049 | posDot: integer;
|
|---|
| 1050 | begin
|
|---|
| 1051 | result := ifUnknown;
|
|---|
| 1052 |
|
|---|
| 1053 | ext := ExtractFileName(AFilenameOrExtensionUTF8);
|
|---|
| 1054 | posDot := LastDelimiter('.', ext);
|
|---|
| 1055 | if posDot <> 0 then ext := copy(ext,posDot,length(ext)-posDot+1)
|
|---|
| 1056 | else ext := '.'+ext;
|
|---|
| 1057 | ext := UTF8LowerCase(ext);
|
|---|
| 1058 |
|
|---|
| 1059 | if (ext = '.jpg') or (ext = '.jpeg') then result := ifJpeg else
|
|---|
| 1060 | if (ext = '.png') then result := ifPng else
|
|---|
| 1061 | if (ext = '.gif') then result := ifGif else
|
|---|
| 1062 | if (ext = '.pcx') then result := ifPcx else
|
|---|
| 1063 | if (ext = '.bmp') then result := ifBmp else
|
|---|
| 1064 | if (ext = '.ico') then result := ifIco else
|
|---|
| 1065 | if (ext = '.cur') then result := ifCur else
|
|---|
| 1066 | if (ext = '.pdn') then result := ifPaintDotNet else
|
|---|
| 1067 | if (ext = '.lzp') then result := ifLazPaint else
|
|---|
| 1068 | if (ext = '.ora') then result := ifOpenRaster else
|
|---|
| 1069 | if (ext = '.psd') then result := ifPsd else
|
|---|
| 1070 | if (ext = '.tga') then result := ifTarga else
|
|---|
| 1071 | if (ext = '.tif') or (ext = '.tiff') then result := ifTiff else
|
|---|
| 1072 | if (ext = '.xwd') then result := ifXwd else
|
|---|
| 1073 | if (ext = '.xpm') then result := ifXPixMap else
|
|---|
| 1074 | if (ext = '.oxo') then result := ifPhoxo else
|
|---|
| 1075 | if (ext = '.svg') then result := ifSvg;
|
|---|
| 1076 | end;
|
|---|
| 1077 |
|
|---|
| 1078 | function SuggestImageExtension(AFormat: TBGRAImageFormat): string;
|
|---|
| 1079 | begin
|
|---|
| 1080 | case AFormat of
|
|---|
| 1081 | ifJpeg: result := 'jpg';
|
|---|
| 1082 | ifPng: result := 'png';
|
|---|
| 1083 | ifGif: result := 'gif';
|
|---|
| 1084 | ifBmp: result := 'bmp';
|
|---|
| 1085 | ifBmpMioMap: result := 'bmp';
|
|---|
| 1086 | ifIco: result := 'ico';
|
|---|
| 1087 | ifCur: result := 'ico';
|
|---|
| 1088 | ifPcx: result := 'pcx';
|
|---|
| 1089 | ifPaintDotNet: result := 'pdn';
|
|---|
| 1090 | ifLazPaint: result := 'lzp';
|
|---|
| 1091 | ifOpenRaster: result := 'ora';
|
|---|
| 1092 | ifPhoxo: result := 'oXo';
|
|---|
| 1093 | ifPsd: result := 'psd';
|
|---|
| 1094 | ifTarga: result := 'tga';
|
|---|
| 1095 | ifTiff: result := 'tif';
|
|---|
| 1096 | ifXwd: result := 'xwd';
|
|---|
| 1097 | ifXPixMap: result := 'xpm';
|
|---|
| 1098 | ifSvg: result := 'svg';
|
|---|
| 1099 | else result := '?';
|
|---|
| 1100 | end;
|
|---|
| 1101 | end;
|
|---|
| 1102 |
|
|---|
| 1103 | function CreateBGRAImageReader(AFormat: TBGRAImageFormat): TFPCustomImageReader;
|
|---|
| 1104 | begin
|
|---|
| 1105 | if DefaultBGRAImageReader[AFormat] = nil then
|
|---|
| 1106 | begin
|
|---|
| 1107 | case AFormat of
|
|---|
| 1108 | ifUnknown: raise exception.Create('The image format is unknown.');
|
|---|
| 1109 | ifOpenRaster: raise exception.Create('You need to call BGRAOpenRaster.RegisterOpenRasterFormat to read this image.');
|
|---|
| 1110 | ifPaintDotNet: raise exception.Create('You need to call BGRAPaintNet.RegisterPaintNetFormat to read this image.');
|
|---|
| 1111 | ifSvg: raise exception.Create('You need to call BGRA.RegisterSvgFormat to read this image.');
|
|---|
| 1112 | else
|
|---|
| 1113 | raise exception.Create('The image reader is not registered for this image format.');
|
|---|
| 1114 | end;
|
|---|
| 1115 | end;
|
|---|
| 1116 | result := DefaultBGRAImageReader[AFormat].Create;
|
|---|
| 1117 | end;
|
|---|
| 1118 |
|
|---|
| 1119 | function CreateBGRAImageWriter(AFormat: TBGRAImageFormat; AHasTransparentPixels: boolean): TFPCustomImageWriter;
|
|---|
| 1120 | begin
|
|---|
| 1121 | if DefaultBGRAImageWriter[AFormat] = nil then
|
|---|
| 1122 | begin
|
|---|
| 1123 | case AFormat of
|
|---|
| 1124 | ifUnknown: raise exception.Create('The image format is unknown');
|
|---|
| 1125 | ifOpenRaster: raise exception.Create('You need to call BGRAOpenRaster.RegisterOpenRasterFormat to write with this image format.');
|
|---|
| 1126 | ifPhoxo: raise exception.Create('You need to call BGRAPhoxo.RegisterPhoxoFormat to write with this image format.');
|
|---|
| 1127 | else
|
|---|
| 1128 | raise exception.Create('The image writer is not registered for this image format.');
|
|---|
| 1129 | end;
|
|---|
| 1130 | end;
|
|---|
| 1131 |
|
|---|
| 1132 | if AFormat = ifPng then
|
|---|
| 1133 | begin
|
|---|
| 1134 | result := TBGRAWriterPNG.Create;
|
|---|
| 1135 | TBGRAWriterPNG(result).UseAlpha := AHasTransparentPixels;
|
|---|
| 1136 | end else
|
|---|
| 1137 | if AFormat = ifBmp then
|
|---|
| 1138 | begin
|
|---|
| 1139 | result := TFPWriterBMP.Create;
|
|---|
| 1140 | if AHasTransparentPixels then
|
|---|
| 1141 | TFPWriterBMP(result).BitsPerPixel := 32 else
|
|---|
| 1142 | TFPWriterBMP(result).BitsPerPixel := 24;
|
|---|
| 1143 | end else
|
|---|
| 1144 | if AFormat = ifXPixMap then
|
|---|
| 1145 | begin
|
|---|
| 1146 | result := TFPWriterXPM.Create;
|
|---|
| 1147 | TFPWriterXPM(result).ColorCharSize := 2;
|
|---|
| 1148 | end else
|
|---|
| 1149 | result := DefaultBGRAImageWriter[AFormat].Create;
|
|---|
| 1150 | end;
|
|---|
| 1151 |
|
|---|
| 1152 | operator =(const AGuid1, AGuid2: TGuid): boolean;
|
|---|
| 1153 | begin
|
|---|
| 1154 | result := CompareMem(@AGuid1, @AGuid2, sizeof(TGuid));
|
|---|
| 1155 | end;
|
|---|
| 1156 |
|
|---|
| 1157 | type
|
|---|
| 1158 | TResourceType = record
|
|---|
| 1159 | ext: string;
|
|---|
| 1160 | code: pchar;
|
|---|
| 1161 | end;
|
|---|
| 1162 |
|
|---|
| 1163 | const
|
|---|
| 1164 | ResourceTypes: array[1..7] of TResourceType =
|
|---|
| 1165 | ((ext: 'CUR'; code: RT_GROUP_CURSOR),
|
|---|
| 1166 | (ext: 'BMP'; code: RT_BITMAP),
|
|---|
| 1167 | (ext: 'ICO'; code: RT_GROUP_ICON),
|
|---|
| 1168 | (ext: 'DAT'; code: RT_RCDATA),
|
|---|
| 1169 | (ext: 'DATA'; code: RT_RCDATA),
|
|---|
| 1170 | (ext: 'HTM'; code: RT_HTML),
|
|---|
| 1171 | (ext: 'HTML'; code: RT_HTML));
|
|---|
| 1172 |
|
|---|
| 1173 | { TBGRAResourceManager }
|
|---|
| 1174 |
|
|---|
| 1175 | function TBGRAResourceManager.GetWinResourceType(AExtension: string): pchar;
|
|---|
| 1176 | var
|
|---|
| 1177 | i: Integer;
|
|---|
| 1178 | begin
|
|---|
| 1179 | if (AExtension <> '') and (AExtension[1]='.') then delete(AExtension,1,1);
|
|---|
| 1180 | for i := low(ResourceTypes) to high(ResourceTypes) do
|
|---|
| 1181 | if AExtension = ResourceTypes[i].ext then
|
|---|
| 1182 | exit(ResourceTypes[i].code);
|
|---|
| 1183 |
|
|---|
| 1184 | exit(RT_RCDATA);
|
|---|
| 1185 | end;
|
|---|
| 1186 |
|
|---|
| 1187 | function TBGRAResourceManager.GetResourceStream(AFilename: string): TStream;
|
|---|
| 1188 | var
|
|---|
| 1189 | name,ext: RawByteString;
|
|---|
| 1190 | rt: PChar;
|
|---|
| 1191 | begin
|
|---|
| 1192 | ext := UpperCase(ExtractFileExt(AFilename));
|
|---|
| 1193 | name := ChangeFileExt(AFilename,'');
|
|---|
| 1194 | rt := GetWinResourceType(ext);
|
|---|
| 1195 |
|
|---|
| 1196 | if (rt = RT_GROUP_CURSOR) or (rt = RT_GROUP_ICON) then
|
|---|
| 1197 | raise exception.Create('Not implemented');
|
|---|
| 1198 |
|
|---|
| 1199 | result := TResourceStream.Create(HINSTANCE, name, rt);
|
|---|
| 1200 | end;
|
|---|
| 1201 |
|
|---|
| 1202 | function TBGRAResourceManager.IsWinResource(AFilename: string): boolean;
|
|---|
| 1203 | var
|
|---|
| 1204 | name,ext: RawByteString;
|
|---|
| 1205 | rt: PChar;
|
|---|
| 1206 | begin
|
|---|
| 1207 | ext := UpperCase(ExtractFileExt(AFilename));
|
|---|
| 1208 | name := ChangeFileExt(AFilename,'');
|
|---|
| 1209 | rt := GetWinResourceType(ext);
|
|---|
| 1210 | result := FindResource(HINSTANCE, pchar(name), rt)<>0;
|
|---|
| 1211 | end;
|
|---|
| 1212 |
|
|---|
| 1213 | {$IFDEF BGRABITMAP_USE_LCL}
|
|---|
| 1214 | type
|
|---|
| 1215 |
|
|---|
| 1216 | { TLCLResourceManager }
|
|---|
| 1217 |
|
|---|
| 1218 | TLCLResourceManager = class(TBGRAResourceManager)
|
|---|
| 1219 | protected
|
|---|
| 1220 | function FindLazarusResource(AFilename: string): TLResource;
|
|---|
| 1221 | public
|
|---|
| 1222 | function GetResourceStream(AFilename: string): TStream; override;
|
|---|
| 1223 | function IsWinResource(AFilename: string): boolean; override;
|
|---|
| 1224 | end;
|
|---|
| 1225 |
|
|---|
| 1226 | function TLCLResourceManager.FindLazarusResource(AFilename: string): TLResource;
|
|---|
| 1227 | var
|
|---|
| 1228 | name,ext: RawByteString;
|
|---|
| 1229 | begin
|
|---|
| 1230 | ext := UpperCase(ExtractFileExt(AFilename));
|
|---|
| 1231 | if (ext<>'') and (ext[1]='.') then Delete(ext,1,1);
|
|---|
| 1232 | name := ChangeFileExt(AFilename,'');
|
|---|
| 1233 | if ext<>'' then
|
|---|
| 1234 | result := LazarusResources.Find(name,ext)
|
|---|
| 1235 | else
|
|---|
| 1236 | result := LazarusResources.Find(name);
|
|---|
| 1237 | end;
|
|---|
| 1238 |
|
|---|
| 1239 | function TLCLResourceManager.GetResourceStream(AFilename: string): TStream;
|
|---|
| 1240 | var
|
|---|
| 1241 | res: TLResource;
|
|---|
| 1242 | begin
|
|---|
| 1243 | res := FindLazarusResource(AFilename);
|
|---|
| 1244 | if Assigned(res) then
|
|---|
| 1245 | result := TLazarusResourceStream.CreateFromHandle(res)
|
|---|
| 1246 | else
|
|---|
| 1247 | result := inherited GetResourceStream(AFilename);
|
|---|
| 1248 | end;
|
|---|
| 1249 |
|
|---|
| 1250 | function TLCLResourceManager.IsWinResource(AFilename: string): boolean;
|
|---|
| 1251 | begin
|
|---|
| 1252 | if FindLazarusResource(AFilename)<>nil then
|
|---|
| 1253 | result := false
|
|---|
| 1254 | else
|
|---|
| 1255 | Result:=inherited IsWinResource(AFilename);
|
|---|
| 1256 | end;
|
|---|
| 1257 |
|
|---|
| 1258 | {$ENDIF}
|
|---|
| 1259 |
|
|---|
| 1260 | initialization
|
|---|
| 1261 |
|
|---|
| 1262 | {$DEFINE INCLUDE_INIT}
|
|---|
| 1263 | {$I bgrapixel.inc}
|
|---|
| 1264 |
|
|---|
| 1265 | {$DEFINE INCLUDE_INIT}
|
|---|
| 1266 | {$I csscolorconst.inc}
|
|---|
| 1267 |
|
|---|
| 1268 | DefaultBGRAImageWriter[ifJpeg] := TFPWriterJPEG;
|
|---|
| 1269 | DefaultBGRAImageWriter[ifPng] := TBGRAWriterPNG;
|
|---|
| 1270 | DefaultBGRAImageWriter[ifBmp] := TFPWriterBMP;
|
|---|
| 1271 | DefaultBGRAImageWriter[ifPcx] := TFPWriterPCX;
|
|---|
| 1272 | DefaultBGRAImageWriter[ifTarga] := TFPWriterTarga;
|
|---|
| 1273 | DefaultBGRAImageWriter[ifXPixMap] := TFPWriterXPM;
|
|---|
| 1274 | DefaultBGRAImageWriter[ifTiff] := TFPWriterTiff;
|
|---|
| 1275 | //writing XWD not implemented
|
|---|
| 1276 |
|
|---|
| 1277 | DefaultBGRAImageReader[ifXwd] := TFPReaderXWD;
|
|---|
| 1278 | //the other readers are registered by their unit
|
|---|
| 1279 |
|
|---|
| 1280 | {$IFDEF BGRABITMAP_USE_LCL}
|
|---|
| 1281 | BGRAResource := TLCLResourceManager.Create;
|
|---|
| 1282 | {$ELSE}
|
|---|
| 1283 | BGRAResource := TBGRAResourceManager.Create;
|
|---|
| 1284 | {$ENDIF}
|
|---|
| 1285 |
|
|---|
| 1286 | finalization
|
|---|
| 1287 |
|
|---|
| 1288 | {$DEFINE INCLUDE_FINAL}
|
|---|
| 1289 | {$I csscolorconst.inc}
|
|---|
| 1290 |
|
|---|
| 1291 | {$DEFINE INCLUDE_FINAL}
|
|---|
| 1292 | {$I bgrapixel.inc}
|
|---|
| 1293 |
|
|---|
| 1294 | BGRAResource.Free;
|
|---|
| 1295 | end.
|
|---|